From f3c8ef86b869ef7a2a14bd7cc149c23a1c62cd6e Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 20:17:55 -0800 Subject: [PATCH 001/178] perf(sidebar): skip Hidden projects host labels while collapsed (#41788) --- .../sidebar/hidden-projects/HiddenProjectsSection.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/renderer/src/components/sidebar/hidden-projects/HiddenProjectsSection.tsx b/src/renderer/src/components/sidebar/hidden-projects/HiddenProjectsSection.tsx index 9c8d48edc52..c349572193f 100644 --- a/src/renderer/src/components/sidebar/hidden-projects/HiddenProjectsSection.tsx +++ b/src/renderer/src/components/sidebar/hidden-projects/HiddenProjectsSection.tsx @@ -36,8 +36,10 @@ const HiddenProjectsSection = React.memo(function HiddenProjectsSection() { const [expanded, setExpanded] = useState(false) const [pendingKeys, setPendingKeys] = useState>(new Set()) // Name hosts only when one is not this computer; a local-only list needs no host column. + // Why: this selector reruns on every store write; skip it while the list is collapsed. const hostLabels = useAppStore( useShallow((state) => + expanded && hiddenRepos.some((repo) => getRepoExecutionHostId(repo) !== LOCAL_EXECUTION_HOST_ID) ? hiddenRepos.map((repo) => selectExecutionHostDisplayLabel(state, getRepoExecutionHostId(repo)) From 89bac6f40084ad2953d292d95d679da5df3f3065 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 20:18:10 -0800 Subject: [PATCH 002/178] fix(plugins): host API 1.7-1.9 release floors are 1.4.259 (#41789) --- docs/schemas/plugins/orca-plugin.v2.schema.json | 8 ++++---- skill-guides/orca-plugin-authoring/references/host-api.md | 8 ++++---- src/cli/skills/bundled-skill-guides.ts | 8 ++++---- src/shared/plugins/workers/plugin-host-api-1-8.ts | 2 +- src/shared/plugins/workers/plugin-host-api-1-9.ts | 2 +- .../plugins/workers/plugin-host-api-workspace-methods.ts | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/schemas/plugins/orca-plugin.v2.schema.json b/docs/schemas/plugins/orca-plugin.v2.schema.json index 0d1cefe4b4f..2e89039c322 100644 --- a/docs/schemas/plugins/orca-plugin.v2.schema.json +++ b/docs/schemas/plugins/orca-plugin.v2.schema.json @@ -1601,10 +1601,10 @@ "secrets:own": "1.4.245", "settings:own": "1.4.160", "appearance:write": "1.4.249", - "workspace:create": "1.4.258", - "automation:read": "1.4.258", - "automation:write": "1.4.258", - "history:read": "1.4.258" + "workspace:create": "1.4.259", + "automation:read": "1.4.259", + "automation:write": "1.4.259", + "history:read": "1.4.259" } } }, diff --git a/skill-guides/orca-plugin-authoring/references/host-api.md b/skill-guides/orca-plugin-authoring/references/host-api.md index 6497b06ee2d..cbd67c18db7 100644 --- a/skill-guides/orca-plugin-authoring/references/host-api.md +++ b/skill-guides/orca-plugin-authoring/references/host-api.md @@ -188,10 +188,10 @@ List each in `contributes.hooks` (`{ "event", "onError"? }`; `main` and `trusted - `secrets:own` (since 1.4.245): Store and read secrets in the plugin's own encrypted file - `settings:own` (since 1.4.160): Read and change the plugin's own settings - `appearance:write` (since 1.4.249): Publish and select desktop application, editor and terminal colors -- `workspace:create` (since 1.4.258): Create workspaces (new Git branches and worktrees) in your projects, local and SSH; a project's setup script runs as its settings say -- `automation:read` (since 1.4.258): Read your automations: their names, prompts, agents, projects, workspaces and schedules -- `automation:write` (since 1.4.258): Create automations that start agents in your projects on a schedule, even when the plugin is not running, and change or delete them (Orca asks you first for ones the plugin did not create, and before it changes what one with extra agent settings runs) -- `history:read` (since 1.4.258): Read the list of your past agent sessions on this computer, from the history files of the coding agents Orca supports, including running WSL distributions on Windows (not SSH or other machines, Antigravity IDE conversations or subagent transcripts): each session's title, which is often the start of its first message, agent, workspace, branch, times, and message and token counts; never the conversation itself +- `workspace:create` (since 1.4.259): Create workspaces (new Git branches and worktrees) in your projects, local and SSH; a project's setup script runs as its settings say +- `automation:read` (since 1.4.259): Read your automations: their names, prompts, agents, projects, workspaces and schedules +- `automation:write` (since 1.4.259): Create automations that start agents in your projects on a schedule, even when the plugin is not running, and change or delete them (Orca asks you first for ones the plugin did not create, and before it changes what one with extra agent settings runs) +- `history:read` (since 1.4.259): Read the list of your past agent sessions on this computer, from the history files of the coding agents Orca supports, including running WSL distributions on Windows (not SSH or other machines, Antigravity IDE conversations or subagent transcripts): each session's title, which is often the start of its first message, agent, workspace, branch, times, and message and token counts; never the conversation itself ## Errors diff --git a/src/cli/skills/bundled-skill-guides.ts b/src/cli/skills/bundled-skill-guides.ts index f4c32cb8e3a..9a7cebdb579 100644 --- a/src/cli/skills/bundled-skill-guides.ts +++ b/src/cli/skills/bundled-skill-guides.ts @@ -3225,10 +3225,10 @@ const ORCA_PLUGIN_AUTHORING_HOST_API_REFERENCE_MARKDOWN = [ "- `secrets:own` (since 1.4.245): Store and read secrets in the plugin's own encrypted file", "- `settings:own` (since 1.4.160): Read and change the plugin's own settings", "- `appearance:write` (since 1.4.249): Publish and select desktop application, editor and terminal colors", - "- `workspace:create` (since 1.4.258): Create workspaces (new Git branches and worktrees) in your projects, local and SSH; a project's setup script runs as its settings say", - "- `automation:read` (since 1.4.258): Read your automations: their names, prompts, agents, projects, workspaces and schedules", - "- `automation:write` (since 1.4.258): Create automations that start agents in your projects on a schedule, even when the plugin is not running, and change or delete them (Orca asks you first for ones the plugin did not create, and before it changes what one with extra agent settings runs)", - "- `history:read` (since 1.4.258): Read the list of your past agent sessions on this computer, from the history files of the coding agents Orca supports, including running WSL distributions on Windows (not SSH or other machines, Antigravity IDE conversations or subagent transcripts): each session's title, which is often the start of its first message, agent, workspace, branch, times, and message and token counts; never the conversation itself", + "- `workspace:create` (since 1.4.259): Create workspaces (new Git branches and worktrees) in your projects, local and SSH; a project's setup script runs as its settings say", + "- `automation:read` (since 1.4.259): Read your automations: their names, prompts, agents, projects, workspaces and schedules", + "- `automation:write` (since 1.4.259): Create automations that start agents in your projects on a schedule, even when the plugin is not running, and change or delete them (Orca asks you first for ones the plugin did not create, and before it changes what one with extra agent settings runs)", + "- `history:read` (since 1.4.259): Read the list of your past agent sessions on this computer, from the history files of the coding agents Orca supports, including running WSL distributions on Windows (not SSH or other machines, Antigravity IDE conversations or subagent transcripts): each session's title, which is often the start of its first message, agent, workspace, branch, times, and message and token counts; never the conversation itself", "", "## Errors", "", diff --git a/src/shared/plugins/workers/plugin-host-api-1-8.ts b/src/shared/plugins/workers/plugin-host-api-1-8.ts index a66967d0a64..65802df7379 100644 --- a/src/shared/plugins/workers/plugin-host-api-1-8.ts +++ b/src/shared/plugins/workers/plugin-host-api-1-8.ts @@ -1,3 +1,3 @@ /** Provisional release floor for host API 1.8 (workspace creation, agent resume, automations), for * plugins that require it in `engines.orca` instead of checking `host.has`; not a stock-release claim. */ -export const PLUGIN_HOST_API_1_8_SINCE = '1.4.258' +export const PLUGIN_HOST_API_1_8_SINCE = '1.4.259' diff --git a/src/shared/plugins/workers/plugin-host-api-1-9.ts b/src/shared/plugins/workers/plugin-host-api-1-9.ts index 53faca83a4c..1d4a10bdcb1 100644 --- a/src/shared/plugins/workers/plugin-host-api-1-9.ts +++ b/src/shared/plugins/workers/plugin-host-api-1-9.ts @@ -1,4 +1,4 @@ /** Provisional release floor for host API 1.9 (agent session history, resuming past sessions, * task titles, hiding projects), for plugins that require it in `engines.orca` instead of checking `host.has`; * not a stock-release claim. */ -export const PLUGIN_HOST_API_1_9_SINCE = '1.4.258' +export const PLUGIN_HOST_API_1_9_SINCE = '1.4.259' diff --git a/src/shared/plugins/workers/plugin-host-api-workspace-methods.ts b/src/shared/plugins/workers/plugin-host-api-workspace-methods.ts index 5230c9e4be5..b695ece131f 100644 --- a/src/shared/plugins/workers/plugin-host-api-workspace-methods.ts +++ b/src/shared/plugins/workers/plugin-host-api-workspace-methods.ts @@ -12,7 +12,7 @@ import { spec, WORKER_ONLY } from './plugin-host-method-spec' /** Provisional release floor for host API 1.7, for plugins that require it in `engines.orca` * instead of checking `host.has`; not a stock-release claim. */ -export const PLUGIN_HOST_API_1_7_SINCE = '1.4.258' +export const PLUGIN_HOST_API_1_7_SINCE = '1.4.259' export const PLUGIN_WORKSPACE_LIST_LIMIT = 500 export const PLUGIN_PROJECT_LIST_LIMIT = 500 From c1364b6d77c8f0a564c22aeabe53e83938358bb1 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 20:18:22 -0800 Subject: [PATCH 003/178] test(github): stub the moved gh downloader in the job-log retention benchmark (#41790) --- .../check-job-log-cache-retention-child.mjs | 1 + .../check/check-job-log-host-measurement.mjs | 22 ++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/github/client/check/check-job-log-cache-retention-child.mjs b/src/main/github/client/check/check-job-log-cache-retention-child.mjs index ca3851150ea..62d2bc788be 100644 --- a/src/main/github/client/check/check-job-log-cache-retention-child.mjs +++ b/src/main/github/client/check/check-job-log-cache-retention-child.mjs @@ -96,6 +96,7 @@ async function retained(count, bytes, offset) { for (let id = 0; id < 3; id++) { await cold(id + 1, 128 * 1024) } +assert.equal(host.downloadCount(), 3, 'Cold downloads bypassed the generated gh fixture') host.prCheckLogTailCache.clear() const pinnedBefore = memory() let pinned = Array.from({ length: 8 }, (_, index) => parent(8 * mib, index).slice(-limit)) diff --git a/src/main/github/client/check/check-job-log-host-measurement.mjs b/src/main/github/client/check/check-job-log-host-measurement.mjs index 60f735817d3..9d567c0d163 100644 --- a/src/main/github/client/check/check-job-log-host-measurement.mjs +++ b/src/main/github/client/check/check-job-log-host-measurement.mjs @@ -22,6 +22,9 @@ export async function ghExecFileAsync() { return { stdout: output, stderr: '' }; } ` +const ghExecFileSpecifier = '../../../git/command-runner/hosted-cli/gh-exec-file' +const ghExecFileSource = 'src/main/git/command-runner/hosted-cli/gh-exec-file.ts' +const ghExecFileFilter = /^\.\.\/\.\.\/\.\.\/git\/command-runner\/hosted-cli\/gh-exec-file$/ const scriptUrl = (name) => new URL(name, import.meta.url) const scriptRoot = fileURLToPath(scriptUrl('../../../../../')) const sourceName = (root, filename) => path.relative(root, filename).split(path.sep).join('/') @@ -34,12 +37,12 @@ const cpuDescriptors = [8, 63].map((size) => ({ export async function prepareHostMeasurement(root = process.cwd()) { const directory = await mkdtemp(path.join(tmpdir(), 'orca-check-host-retention-')) + let ghExecFileStubbed = false try { const bundle = await build({ absWorkingDir: root, stdin: { - contents: - "export { attachFailedJobLogTails, prCheckLogTailCache } from './check-job-log-tails'; export { setFixtureOutput, downloadCount } from '../../gh-utils';", + contents: `export { attachFailedJobLogTails, prCheckLogTailCache } from './check-job-log-tails'; export { setFixtureOutput, downloadCount } from '${ghExecFileSpecifier}';`, resolveDir: path.join(root, 'src', 'main', 'github', 'client', 'check'), loader: 'ts' }, @@ -52,10 +55,12 @@ export async function prepareHostMeasurement(root = process.cwd()) { { name: 'generated-gh-capture-result', setup(builder) { - builder.onResolve({ filter: /^\.\.\/\.\.\/gh-utils$/ }, () => ({ - path: 'generated-gh-result', - namespace: 'fixture' - })) + // Why: check-job-log-download.ts imports ghExecFileAsync by this exact + // specifier; stubbing any other path lets the bundle spawn the real gh. + builder.onResolve({ filter: ghExecFileFilter }, () => { + ghExecFileStubbed = true + return { path: 'generated-gh-result', namespace: 'fixture' } + }) builder.onLoad({ filter: /.*/, namespace: 'fixture' }, () => ({ contents: fixtureSource, loader: 'js' @@ -67,6 +72,11 @@ export async function prepareHostMeasurement(root = process.cwd()) { const bundleSources = Object.keys(bundle.metafile.inputs) .filter((name) => name !== '' && !name.startsWith('fixture:')) .sort() + assert(ghExecFileStubbed, 'Host measurement did not stub the gh downloader') + assert( + !bundleSources.includes(ghExecFileSource), + 'Host measurement bundle includes the real gh downloader' + ) const executedSources = [ import.meta.filename, fileURLToPath(scriptUrl('./check-job-log-cache-retention-child.mjs')), From 57dbdf871376291cf5363d1c28a544e69882b7d4 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 20:21:00 -0800 Subject: [PATCH 004/178] test: remove narrow wakeup and worker output checks (#41807) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- ...erver-claude-task-wakeup-lifecycle.test.ts | 178 +----------------- .../manual-dispatch-observation.test.ts | 139 -------------- .../worker-interactive-wait.test.ts | 17 -- .../worker/output/worker-archive-read.test.ts | 126 ------------- .../worker/output/worker-output.test.ts | 98 ---------- 5 files changed, 1 insertion(+), 557 deletions(-) delete mode 100644 src/main/runtime/rpc/methods/orchestration/worker/observation/manual-dispatch-observation.test.ts delete mode 100644 src/main/runtime/rpc/methods/orchestration/worker/output/worker-archive-read.test.ts diff --git a/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts b/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts index 319dec28c0b..2491c657d63 100644 --- a/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts +++ b/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts @@ -21,17 +21,6 @@ vi.mock('electron', () => ({ vi.mock('../../../../telemetry/client', () => ({ track: vi.fn() })) vi.mock('../../../../telemetry/cohort-classifier', () => ({ getCohortAtEmit: vi.fn(() => ({})) })) -type CapturedHook = { scenario: string; kind: string; payload?: Record } -const hooks: CapturedHook[] = readFileSync( - join( - import.meta.dirname, - '../../../../../shared/__fixtures__/claude-task-notification-hooks.jsonl' - ), - 'utf8' -) - .trim() - .split('\n') - .map((line) => JSON.parse(line)) const SESSION = '00000000-0000-4000-8000-000000000000' beforeEach(() => { @@ -79,115 +68,8 @@ async function host(remote: boolean, launchToken?: string) { return { desktop, post, row, stop } } -describe.each([false, true])('Claude wake-up production ingress remote=%s', (remote) => { - it.each([ - 'one-subagent', - 'two-subagents-together', - 'subagent-resumed', - 'subagent-with-own-shell', - 'two-shells-together' - ])('%s never exposes completion before the final captured wake-up', async (scenario) => { - const server = await host(remote) - const records = hooks.filter((record) => record.scenario === scenario && record.payload) - const finalWakeup = records.findLastIndex( - (record) => - record.payload?.hook_event_name === 'UserPromptSubmit' && - String(record.payload.prompt).startsWith('') - ) - expect(finalWakeup).toBeGreaterThan(0) - const prematureCompletion: number[] = [] - try { - for (const [index, record] of records.entries()) { - await server.post(record.payload!) - const row = server.row() - if (index < finalWakeup && row?.state === 'done' && !row.sessionBoundary) { - prematureCompletion.push(index) - } - } - expect(prematureCompletion).toEqual([]) - expect(server.row()).toMatchObject({ - state: 'done', - connectionId: remote ? 'ssh-owner' : null - }) - } finally { - server.stop() - } - }) -}) - -describe.each([false, true])('Claude cancellation with an owed wake-up remote=%s', (remote) => { - it.each([false, true])( - 'keeps cancellation bounded unless a new prompt opens a turn: %s', - async (newTurn) => { - vi.useFakeTimers({ - shouldAdvanceTime: true, - toFake: ['Date', 'performance', 'setTimeout', 'clearTimeout'] - }) - const server = await host(remote) - try { - await server.post({ hook_event_name: 'UserPromptSubmit', prompt: 'delegate' }) - await server.post({ hook_event_name: 'SubagentStart', agent_id: 'a1' }) - await server.post({ - hook_event_name: 'PostToolUse', - tool_name: 'Agent', - tool_response: { isAsync: true, agentId: 'a1' } - }) - await server.post({ hook_event_name: 'SubagentStop', agent_id: 'a1' }) - const baseline = server.row()! - expect( - server.desktop.inferInterrupt({ - paneKey: PANE, - baselineUpdatedAt: baseline.receivedAt, - baselineStateStartedAt: baseline.stateStartedAt, - baselinePrompt: baseline.prompt, - baselineAgentType: 'claude', - intent: 'ctrl-c' - }) - ).toBe(true) - expect(server.row()).toMatchObject({ - mainAgent: { state: 'done', outcome: 'cancellation' } - }) - await server.post({ - hook_event_name: 'PostToolUse', - tool_name: 'Read', - tool_response: { content: 'late result' } - }) - if (newTurn) { - await server.post({ hook_event_name: 'UserPromptSubmit', prompt: 'start a fresh turn' }) - } - vi.advanceTimersByTime(120_000) - if (newTurn) { - expect(server.row()).toMatchObject({ state: 'working', mainAgent: { state: 'working' } }) - expect(server.row()?.mainAgent).not.toHaveProperty('outcome') - await server.post({ hook_event_name: 'Stop', background_tasks: [] }) - vi.advanceTimersByTime(60_000) - expect(server.row()?.state).toBe('done') - } else { - expect(server.row()).toMatchObject({ - state: 'done', - interrupted: true, - mainAgent: { state: 'done', outcome: 'cancellation' } - }) - } - } finally { - server.stop() - } - } - ) -}) - describe.each([false, true])('real automation Claude wake-up oracle remote=%s', (remote) => { - it.each([ - 'stop', - 'missing-notification', - 'native-idle', - 'cancel', - 'duplicate', - 'shell-duplicate', - 'end-before-launch', - 'notification-before-launch', - 'notification-before-end' - ])( + it.each(['missing-notification', 'native-idle', 'notification-before-launch'])( 'waits on captured ready bytes through task finishing or missing wake-up expiry: %s', async (ending) => { const server = await host(remote, 'transcript-launch') @@ -346,61 +228,3 @@ describe.each([false, true])('real automation Claude wake-up oracle remote=%s', } ) }) - -describe('Claude finishing turn permission restoration', () => { - it.each(['lead', 'child'])( - 'retains the cycle through a %s question and clears it on Stop or a fresh prompt', - async (owner) => { - const server = await host(false) - try { - await server.post({ hook_event_name: 'UserPromptSubmit', prompt: 'delegate' }) - await server.post({ hook_event_name: 'SubagentStart', agent_id: 'a1' }) - await server.post({ - hook_event_name: 'PostToolUse', - tool_name: 'Agent', - tool_response: { isAsync: true, agentId: 'a1' } - }) - await server.post({ hook_event_name: 'SubagentStop', agent_id: 'a1' }) - await server.post({ - hook_event_name: 'UserPromptSubmit', - prompt: 'a1completed' - }) - expect(server.row()?.claudeTaskWakeupPending).toBe('finishing-turn') - const child = owner === 'child' ? { agent_id: 'child-question' } : {} - await server.post({ - hook_event_name: 'PreToolUse', - tool_name: 'AskUserQuestion', - tool_use_id: 'question', - ...child - }) - expect(server.row()).toMatchObject({ - state: 'waiting', - claudeTaskWakeupPending: 'finishing-turn' - }) - const baseline = server.row()! - expect( - server.desktop.inferQuestionAnswered({ - paneKey: PANE, - baselineUpdatedAt: baseline.receivedAt, - baselineStateStartedAt: baseline.stateStartedAt, - baselinePrompt: baseline.prompt, - baselineAgentType: 'claude' - }) - ).toBe(true) - await server.post({ hook_event_name: 'PostToolUse', tool_name: 'Read', tool_response: {} }) - expect(server.row()).toMatchObject({ - mainAgent: { state: 'working' }, - claudeTaskWakeupPending: 'finishing-turn' - }) - if (owner === 'lead') { - await server.post({ hook_event_name: 'UserPromptSubmit', prompt: 'a fresh typed turn' }) - expect(server.row()?.claudeTaskWakeupPending).toBeUndefined() - } - await server.post({ hook_event_name: 'Stop', background_tasks: [] }) - expect(server.row()?.claudeTaskWakeupPending).toBeUndefined() - } finally { - server.stop() - } - } - ) -}) diff --git a/src/main/runtime/rpc/methods/orchestration/worker/observation/manual-dispatch-observation.test.ts b/src/main/runtime/rpc/methods/orchestration/worker/observation/manual-dispatch-observation.test.ts deleted file mode 100644 index bb03b10a3b1..00000000000 --- a/src/main/runtime/rpc/methods/orchestration/worker/observation/manual-dispatch-observation.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { OrcaRuntimeService } from '../../../../../orca-runtime' -import { OrchestrationDb } from '../../../../../orchestration/db/orchestration-db' -import '../../../../../orchestration/db/contract-constants' -import '../../../../../orchestration/db/messages/mailbox-routing-page' -import '../../../../../orchestration/db/dispatch-row-writer' -import '../../../../../orchestration/db/attempt-outcome-projection' -import { ORCHESTRATION_METHODS } from '../../orchestration' -import { createRootDispatch } from '../../../../../orchestration/db/root-dispatch-test-fixture' -import { isStreamingMethod, eraseRpcMethods } from '../../../../core' - -describe('manual Dispatch observation', () => { - let db: OrchestrationDb | undefined - - afterEach(() => db?.close()) - - it('covers the real dispatch --inject entry path before observing the lane', async () => { - db = new OrchestrationDb(':memory:') - const runtime = new OrcaRuntimeService() - runtime.setOrchestrationDb(db) - const coordinatorPaneKey = 'tab_coord:leaf_coord' - const workerPaneKey = 'tab_worker:leaf_worker' - vi.spyOn(runtime, 'getTerminalPaneKey').mockImplementation((handle) => - handle === 'term_coord' ? coordinatorPaneKey : workerPaneKey - ) - // Authority is per handle in the real runtime; a flat mock would give the coordinator the worker's pane. - vi.spyOn(runtime, 'getOrchestrationDispatchAuthority').mockImplementation( - (handle) => - ({ - terminalHandle: handle, - paneKey: handle === 'term_coord' ? coordinatorPaneKey : workerPaneKey, - processIncarnation: - handle === 'term_coord' ? 'runtime_test:term_coord:1' : 'runtime_test:term_worker:1' - }) as never - ) - vi.spyOn(runtime, 'isTerminalRunningAgent').mockResolvedValue(true) - vi.spyOn(runtime, 'sendTerminalAgentPrompt').mockResolvedValue({ - handle: 'term_worker', - accepted: true, - bytesWritten: 1 - }) - vi.spyOn(runtime, 'showTerminal').mockResolvedValue({ - handle: 'term_worker', - connected: true, - status: 'running' - } as never) - vi.spyOn(runtime, 'getTerminalProcessIncarnation').mockReturnValue('runtime_test:term_worker:1') - vi.spyOn(runtime, 'getTerminalLivenessVerdict').mockReturnValue({ - status: 'live', - ptyIds: ['runtime_test:term_worker:1'] - }) - vi.spyOn(runtime, 'getTerminalOrchestrationCliCommand').mockReturnValue('orca') - const run = db.createRun({ - objective: 'STA-3848 repro', - coordinatorHandle: 'term_coord', - coordinatorPaneKey - }) - const task = db.createTask({ spec: 'injected lane', runId: run.id }) - const dispatchMethod = eraseRpcMethods(ORCHESTRATION_METHODS).find( - (candidate) => candidate.name === 'orchestration.dispatch' - ) - if (!dispatchMethod || isStreamingMethod(dispatchMethod)) { - throw new Error('Missing method orchestration.dispatch') - } - - const result = (await dispatchMethod.handler( - dispatchMethod.params?.parse({ - task: task.id, - to: 'term_worker', - from: 'term_coord', - run: run.id, - inject: true - }), - { runtime, legacyCoordinatorRunId: run.id } - )) as { dispatch: { id: string } } - - expect(db.getWorkerDispatch(result.dispatch.id)).toBeUndefined() - expect(db.getDispatchContextById(result.dispatch.id)).toMatchObject({ - assignee_handle: 'term_worker', - assignee_pane_key: workerPaneKey, - process_incarnation: 'runtime_test:term_worker:1' - }) - - const workerShowMethod = eraseRpcMethods(ORCHESTRATION_METHODS).find( - (candidate) => candidate.name === 'orchestration.workerShow' - ) - if (!workerShowMethod || isStreamingMethod(workerShowMethod)) { - throw new Error('Missing method orchestration.workerShow') - } - await expect( - workerShowMethod.handler(workerShowMethod.params?.parse({ dispatch: result.dispatch.id }), { - runtime - }) - ).resolves.toMatchObject({ - worker: { state: 'unsupervised', stage: 'context_only' }, - observation: { status: 'live', exactWorker: true } - }) - }) - - it.each([ - ['orchestration.workerStop', 'stopped'], - ['orchestration.workerAbandon', 'abandoned'] - ] as const)('%s fences the assignment without closing the operator pane', async (name, state) => { - db = new OrchestrationDb(':memory:') - const runtime = new OrcaRuntimeService() - runtime.setOrchestrationDb(db) - const closeTerminal = vi.spyOn(runtime, 'closeTerminal') - const task = db.createTask({ - runId: 'run_legacy_local', - spec: 'operator-owned lane' - }) - const dispatch = createRootDispatch( - db, - task.id, - 'term_worker', - 'tab_worker:leaf_worker', - 'launch-hash', - 'runtime_test:term_worker:1' - ) - const method = eraseRpcMethods(ORCHESTRATION_METHODS).find( - (candidate) => candidate.name === name - ) - - if (!method || isStreamingMethod(method)) { - throw new Error(`Missing method ${name}`) - } - const result = await method.handler(method.params?.parse({ dispatch: dispatch.id }), { - runtime - }) - - expect(result).toMatchObject({ state, processAction: 'none' }) - expect(closeTerminal).not.toHaveBeenCalled() - expect(db.getWorkerDispatch(dispatch.id)).toBeUndefined() - expect(db.getDispatchContextById(dispatch.id)).toMatchObject({ - status: 'failed', - last_failure: state - }) - }) -}) diff --git a/src/main/runtime/rpc/methods/orchestration/worker/observation/worker-interactive-wait.test.ts b/src/main/runtime/rpc/methods/orchestration/worker/observation/worker-interactive-wait.test.ts index 5d97602b481..19f23d68467 100644 --- a/src/main/runtime/rpc/methods/orchestration/worker/observation/worker-interactive-wait.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/worker/observation/worker-interactive-wait.test.ts @@ -131,21 +131,4 @@ describe('worker-show interactive wait (STA-3714, STA-4513)', () => { } }) }) - - it('reports an explicit null for the same lane inside a long tool call', async () => { - // Explicit null means checked-and-not-waiting; absence preserves compatibility with hosts too old to know. - const result = await showWorkerPaneServing(fixture('cursor-agent-long-tool-call')) - - expect(result).toMatchObject({ observation: { exactWorker: true, agentWait: null } }) - }) - - it('omits the field entirely for a worker it could not verify', async () => { - // Replaced processes were never checked; null would falsely claim no wait. - const result = (await showWorkerPaneServing(fixture('cursor-agent-approval-prompt'), { - breakIdentity: true - })) as { observation: Record } - - expect(result.observation.exactWorker).toBe(false) - expect('agentWait' in result.observation).toBe(false) - }) }) diff --git a/src/main/runtime/rpc/methods/orchestration/worker/output/worker-archive-read.test.ts b/src/main/runtime/rpc/methods/orchestration/worker/output/worker-archive-read.test.ts deleted file mode 100644 index d3bffa0ff5e..00000000000 --- a/src/main/runtime/rpc/methods/orchestration/worker/output/worker-archive-read.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { existsSync } from 'node:fs' -import { mkdtemp, rm, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { ExactWorkerProviderSession } from '../../../../../../../shared/orchestration/workers/orchestration-worker-output' -import { OrchestrationDb } from '../../../../../orchestration/db/orchestration-db' -import { - captureWorkerOutputReference, - releaseCommitFieldsForCapturedArchive -} from '../../../../../orchestration/workers/archives/worker-output-archive' - -// Resolve only the exact file, so a missing transcript never falls back to this machine's agent homes. -vi.mock('../../../../../../native-chat/session-file-resolver', () => ({ - resolveSessionFilePath: async ( - _agent: string, - _sessionId: string, - options: { transcriptPath?: string } - ) => - options.transcriptPath && existsSync(options.transcriptPath) ? options.transcriptPath : null -})) - -const { readArchivedWorkerOutput } = await import('./worker-archive-read') - -function codexMessage(id: string, text: string): string { - return JSON.stringify({ - timestamp: '2026-11-11T12:00:00.000Z', - type: 'event_msg', - payload: { id, type: 'agent_message', message: text } - }) -} - -const RELEASED = { id: 'res_1', terminal_handle: 'term_1', release_state: 'released' } as const - -describe('reading a released terminal worker', () => { - let directory: string - let transcriptPath: string - let db: OrchestrationDb - - beforeEach(async () => { - directory = await mkdtemp(join(tmpdir(), 'orca-released-worker-')) - transcriptPath = join(directory, 'rollout-worker.jsonl') - await writeFile(transcriptPath, `${codexMessage('m1', 'the final answer')}\n`) - db = new OrchestrationDb(':memory:') - }) - - afterEach(async () => { - db.close() - await rm(directory, { recursive: true, force: true }) - }) - - function release(session: Partial | null): void { - const captured = captureWorkerOutputReference({ - runtime: { - getExactWorkerProviderSession: () => - session && { - paneKey: 'tab_1:leaf', - processIncarnation: 'pty:1', - agent: 'codex', - providerSession: { key: 'session_id', id: 'session-1', transcriptPath }, - observedAt: 1, - ...session - } - }, - terminalHandle: 'term_1', - attachedAtMs: 0 - }) - const { kind, content } = releaseCommitFieldsForCapturedArchive(captured) - db.storeWorkerTerminalArchive({ dispatchId: 'ctx_1', resourceId: 'res_1', kind, content }) - } - - const read = () => - readArchivedWorkerOutput({ - db, - dispatchId: 'ctx_1', - workerState: 'succeeded', - resource: RELEASED - }) - - it('reads its transcript from the original file, keeping no copy', async () => { - release({}) - - await expect(read()).resolves.toMatchObject({ - source: 'transcript', - archived: true, - transcript: { - messages: [{ id: 'm1', blocks: [{ type: 'text', text: 'the final answer' }] }] - }, - status: { liveness: 'exited' } - }) - expect(db.getWorkerTerminalArchive('ctx_1')?.content).not.toContain('the final answer') - }) - - it('says the output is no longer available once the original is gone', async () => { - release({}) - await rm(transcriptPath) - - await expect(read()).rejects.toMatchObject({ - code: 'archive_unavailable', - message: expect.stringContaining('See its report (worker_done) or task result.') - }) - }) - - it('keeps serving the saved copy of a row released before references', async () => { - db.storeWorkerTerminalArchive({ - dispatchId: 'ctx_1', - resourceId: 'res_1', - kind: 'transcript_pin', - content: JSON.stringify({ - version: 2, - agent: 'codex', - processIncarnation: 'pty:1', - messages: [ - { id: 'old', role: 'assistant', blocks: [{ type: 'text', text: 'saved copy' }] } - ], - limited: false, - warnings: [] - }) - }) - - const output = JSON.stringify(await read()) - - expect(output).toContain('saved copy') - expect(output).not.toContain('the final answer') - }) -}) diff --git a/src/main/runtime/rpc/methods/orchestration/worker/output/worker-output.test.ts b/src/main/runtime/rpc/methods/orchestration/worker/output/worker-output.test.ts index 91bd685e25c..de1c77db93f 100644 --- a/src/main/runtime/rpc/methods/orchestration/worker/output/worker-output.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/worker/output/worker-output.test.ts @@ -15,7 +15,6 @@ function codexMessage(id: string, text: string): string { describe('exact orchestration worker output', () => { let directory: string let transcriptA: string - let transcriptB: string let providerSession: ReturnType let runtime: OrcaRuntimeService const readTerminal = vi.fn() @@ -23,9 +22,7 @@ describe('exact orchestration worker output', () => { beforeEach(async () => { directory = await mkdtemp(join(tmpdir(), 'orca-worker-output-')) transcriptA = join(directory, 'session-a.jsonl') - transcriptB = join(directory, 'session-b.jsonl') await writeFile(transcriptA, `${codexMessage('a', 'worker A only')}\n`) - await writeFile(transcriptB, `${codexMessage('b', 'worker B only')}\n`) providerSession = { paneKey: 'tab:worker', processIncarnation: 'pty:incarnation-1', @@ -68,101 +65,6 @@ describe('exact orchestration worker output', () => { ...overrides }) - it('reads only the exact pane session and keeps its local path private', async () => { - const result = await read() - - expect(result).toMatchObject({ - source: 'transcript', - provider: 'codex', - transcript: { - messages: [{ id: 'a', blocks: [{ type: 'text', text: 'worker A only' }] }] - } - }) - expect(JSON.stringify(result)).not.toContain(transcriptA) - expect(JSON.stringify(result)).not.toContain('worker B only') - expect(readTerminal).not.toHaveBeenCalled() - }) - - it('labels OpenCode as a terminal fallback when no transcript decoder exists', async () => { - const capability = `dcap_${'A'.repeat(43)}` - readTerminal.mockResolvedValue({ - handle: 'term_worker', - status: 'running', - tail: [`opencode --dispatch-capability ${capability}`], - truncated: false, - nextCursor: '9' - }) - providerSession = { - ...providerSession!, - agent: 'opencode', - providerSession: { - key: 'session_id', - id: 'session-opencode', - transcriptPath: transcriptA - } - } - - const result = await read() - - expect(result).toMatchObject({ - source: 'terminal', - fallbackReason: 'provider_unsupported', - terminal: { tail: ['opencode --dispatch-capability [dispatch capability redacted]'] }, - warnings: [ - 'Dispatch capability tokens were redacted from terminal output.', - 'Terminal wrap information was unavailable; capability masking used complete lines.' - ] - }) - expect(JSON.stringify(result)).not.toContain(capability) - }) - - it('redacts dispatch capabilities from terminal composer drafts', async () => { - const capability = `dcap_${'A'.repeat(43)}` - readTerminal.mockResolvedValue({ - handle: 'term_worker', - status: 'running', - tail: ['safe output'], - draft: `send --dispatch-capability ${capability}`, - truncated: false, - nextCursor: '9' - }) - providerSession = null - - const result = await read() - - expect(result).toMatchObject({ - source: 'terminal', - terminal: { - tail: ['safe output'], - draft: 'send --dispatch-capability [dispatch capability redacted]' - }, - warnings: [ - 'Dispatch capability tokens were redacted from terminal output.', - 'Terminal wrap information was unavailable; capability masking used complete lines.' - ] - }) - expect(JSON.stringify(result)).not.toContain(capability) - }) - - it('rejects an old cursor after the exact provider session changes', async () => { - const initial = await read() - if (initial.source !== 'transcript') { - throw new Error('Expected transcript output') - } - providerSession = { - ...providerSession!, - providerSession: { - key: 'session_id', - id: 'session-b', - transcriptPath: transcriptB - } - } - - await expect(read({ cursor: initial.cursor })).rejects.toMatchObject({ - code: 'source_changed' - }) - }) - it('rejects an old cursor after a same-inode truncate/regrow', async () => { const initial = await read() if (initial.source !== 'transcript') { From faf7c69ffaa99406af8e9cd3776f3ed12d88174e Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 12 Nov 2026 20:26:18 -0800 Subject: [PATCH 005/178] Revert "Chat New line: draw only when it helps, then fade once seen" (#41735) (#41804) This reverts commit 6854ab5ae98bce18152defe904603024c885d7d8. Conflict resolution: native-chat-new-rows-marker.test.tsx (pre-existing) and native-chat-new-rows-marker.journal.test.tsx (added by #41735) stay deleted, as #41782 removed/trimmed them on main after #41735 landed. --- .../native-chat/NativeChatMessageList.tsx | 12 +- .../new-rows/NativeChatNewRowsMarker.tsx | 113 +------- .../native-chat-missed-row-positions.ts | 60 ----- ...tive-chat-new-rows-marker.journal.test.tsx | 166 ------------ .../new-rows/native-chat-new-rows-marker.ts | 255 +++++------------- .../settings/chat/chat-behavior-search.ts | 2 +- src/renderer/src/i18n/locales/de.json | 2 +- src/renderer/src/i18n/locales/en.json | 2 +- src/renderer/src/i18n/locales/es.json | 2 +- src/renderer/src/i18n/locales/fr.json | 2 +- src/renderer/src/i18n/locales/ja.json | 2 +- src/renderer/src/i18n/locales/ko.json | 2 +- src/renderer/src/i18n/locales/pt.json | 2 +- src/renderer/src/i18n/locales/ru.json | 2 +- src/renderer/src/i18n/locales/tr.json | 2 +- src/renderer/src/i18n/locales/zh-Hant.json | 2 +- src/renderer/src/i18n/locales/zh.json | 2 +- 17 files changed, 90 insertions(+), 540 deletions(-) delete mode 100644 src/renderer/src/components/native-chat/viewport/new-rows/native-chat-missed-row-positions.ts delete mode 100644 src/renderer/src/components/native-chat/viewport/new-rows/native-chat-new-rows-marker.journal.test.tsx diff --git a/src/renderer/src/components/native-chat/NativeChatMessageList.tsx b/src/renderer/src/components/native-chat/NativeChatMessageList.tsx index 9b2d692f5d9..91f07967fda 100644 --- a/src/renderer/src/components/native-chat/NativeChatMessageList.tsx +++ b/src/renderer/src/components/native-chat/NativeChatMessageList.tsx @@ -244,11 +244,11 @@ export function NativeChatMessageList({ [allSlots, journalItems, stopping, journalSubmissions, latestTurn, hostWork] ) const navigationRequest = useNativeChatNavigationRequest(slots, messages, subagentSections) - const showingLatest = isVisible && session.readCurrent === true - const newRows = useNativeChatNewRowsMarker(slots, waitingSlots, showingLatest, { - items: journalItems ?? [], - latestTurn - }) + const newRows = useNativeChatNewRowsMarker( + slots, + waitingSlots, + isVisible && session.readCurrent === true + ) const transcriptWindow = useNativeChatTranscriptWindow({ scrollRef, slots, @@ -416,7 +416,7 @@ export function NativeChatMessageList({ pendingId={navigation.pendingRailId} /> - + diff --git a/src/renderer/src/components/native-chat/viewport/new-rows/NativeChatNewRowsMarker.tsx b/src/renderer/src/components/native-chat/viewport/new-rows/NativeChatNewRowsMarker.tsx index eb58adb1ed9..c2d20e404a3 100644 --- a/src/renderer/src/components/native-chat/viewport/new-rows/NativeChatNewRowsMarker.tsx +++ b/src/renderer/src/components/native-chat/viewport/new-rows/NativeChatNewRowsMarker.tsx @@ -1,6 +1,5 @@ -import { useEffect, useLayoutEffect, useRef, useState } from 'react' +import { useEffect, useLayoutEffect, useRef } from 'react' import { useTranslate } from '@/i18n/use-translate' -import { cn } from '@/lib/class-names' import { NATIVE_CHAT_BOTTOM_THRESHOLD_PX } from '../scroll/native-chat-autoscroll' import type { NativeChatNewRowsMarkerState } from './native-chat-new-rows-marker' import { nativeChatScrollOffsetWithin } from '../virtualization/use-native-chat-viewport-align' @@ -8,54 +7,8 @@ import { nativeChatScrollOffsetWithin } from '../virtualization/use-native-chat- /** Fields in the pane that take text for something other than the conversation. */ const NOT_THE_CONVERSATION = '[data-native-chat-typing-redirect-ignore="true"]' -/** A line is drawn for missed rows taller than this many views, when nothing else calls for one. */ -export const NEW_ROWS_MARKER_MIN_VIEWPORTS = 1 -/** How long a line stays once it is fully in view of a reader who is looking. */ -export const NEW_ROWS_MARKER_LINGER_MS = 3000 -export const NEW_ROWS_MARKER_FADE_MS = 480 - -/** - * How far the transcript reaches below the marker, against the height of the view; null without - * layout. The window's spacers stand in for unmounted rows at the virtualizer's measured or - * estimated sizes. Rows drawn after the window (held sends, the live activity line) are not counted. - */ -function missedHeight(marker: HTMLElement): { missedPx: number; viewportPx: number } | null { - const rows = marker.closest('[data-native-chat-window]') - const scroller = rows?.closest('[data-native-chat-scroll]') - if (!rows || !scroller || scroller.clientHeight === 0) { - return null - } - const top = nativeChatScrollOffsetWithin(marker, scroller) - const rowsTop = nativeChatScrollOffsetWithin(rows, scroller) - return top === null || rowsTop === null - ? null - : { missedPx: rowsTop + rows.offsetHeight - top, viewportPx: scroller.clientHeight } -} - -/** Fully in view, allowing for the rounding fractional zoom brings to intersection ratios. */ -const WHOLE_RATIO = 0.99 - -/** Whether the marker shows whole in the transcript's view. */ -function useFullyInView(ref: React.RefObject, enabled: boolean): boolean { - const [inView, setInView] = useState(false) - useEffect(() => { - const element = ref.current - if (!enabled || !element || typeof IntersectionObserver === 'undefined') { - setInView(false) - return - } - const observer = new IntersectionObserver( - ([entry]) => setInView(entry !== undefined && entry.intersectionRatio >= WHOLE_RATIO), - { root: element.closest('[data-native-chat-scroll]'), threshold: WHOLE_RATIO } - ) - observer.observe(element) - return () => observer.disconnect() - }, [ref, enabled]) - return inView -} - export function NativeChatNewRowsMarker({ - marker: { look, arriving, watching, onArrived, onDismiss, onMeasured, onFaded }, + marker: { arriving, onArrived, onDismiss }, onReveal }: { marker: NativeChatNewRowsMarkerState @@ -66,20 +19,8 @@ export function NativeChatNewRowsMarker({ const ref = useRef(null) const label = t('components.native-chat.newRowsMarker', 'New') - // Measured once, in layout before paint: the re-render shows the line or draws nothing. useLayoutEffect(() => { - if (look !== 'measure' || !ref.current) { - return - } - const height = missedHeight(ref.current) - // Without layout, draw: a missing line costs more than one that fades on its own. - onMeasured( - height === null || height.missedPx > NEW_ROWS_MARKER_MIN_VIEWPORTS * height.viewportPx - ) - }, [look, onMeasured]) - - useLayoutEffect(() => { - if (!arriving || look !== 'line') { + if (!arriving) { return } onArrived() @@ -100,19 +41,7 @@ export function NativeChatNewRowsMarker({ // Reveal the prompt’s row unless it would leave the marker too low in the viewport. const intoRow = top - (nativeChatScrollOffsetWithin(row, scroller) ?? top) onReveal(intoRow < scroller.clientHeight / 2 ? row : marker) - }, [arriving, look, onArrived, onReveal]) - - // The clock starts over whenever the reader stops seeing the line before it runs out. - const inView = useFullyInView(ref, look === 'line') - const seeing = look === 'line' && inView && watching - useEffect(() => { - if (!seeing) { - return - } - // Faded from here on, so a look away mid-fade already counts it as seen; the fade itself is CSS. - const timer = setTimeout(onFaded, NEW_ROWS_MARKER_LINGER_MS) - return () => clearTimeout(timer) - }, [seeing, onFaded]) + }, [arriving, onArrived, onReveal]) useEffect(() => { const pane = ref.current?.closest('[data-native-chat-root]') @@ -126,23 +55,12 @@ export function NativeChatNewRowsMarker({ return () => pane?.removeEventListener('beforeinput', onInput) }, [onDismiss]) - // Faded, it keeps its place and size so nothing below moves, but is no longer read as a separator. - const shown = look === 'line' return (
{label} @@ -151,23 +69,16 @@ export function NativeChatNewRowsMarker({ } /** Keep the live region mounted before inserting text so screen readers announce it. */ -export function NativeChatNewRowsAnnouncement({ - arrival -}: { - /** Counts the returns that found rows missed; a new count is announced again. */ - arrival: number | undefined -}): React.JSX.Element { +export function NativeChatNewRowsAnnouncement({ marked }: { marked: boolean }): React.JSX.Element { const t = useTranslate() return (
- {arrival === undefined ? null : ( - - {t( + {marked + ? t( 'components.native-chat.newRowsAnnouncement', 'New messages arrived while you were away' - )} - - )} + ) + : null}
) } diff --git a/src/renderer/src/components/native-chat/viewport/new-rows/native-chat-missed-row-positions.ts b/src/renderer/src/components/native-chat/viewport/new-rows/native-chat-missed-row-positions.ts deleted file mode 100644 index a74a1ef84f8..00000000000 --- a/src/renderer/src/components/native-chat/viewport/new-rows/native-chat-missed-row-positions.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Where rows sit in the journal, and which of them a reader can have missed. - -import { compareAgentJournalPositions } from '../../../../../../shared/agent-session/journal/agent-session-journal-position' -import type { AgentJournalPosition } from '../../../../../../shared/agent-session/journal/agent-session-journal-types' -import type { NativeChatTranscriptSlot } from '../../conversation/slots/native-chat-transcript-slots' - -/** Use journal positions for conversation rows, including folded-turn bars; terminal-backed rows have none and remain unmarked. */ -export function rowPosition(slot: NativeChatTranscriptSlot): AgentJournalPosition | undefined { - return slot.kind === 'message' && slot.depth === 0 && (slot.drawsMessage || slot.statusAbove) - ? slot.message.journalPosition - : undefined -} - -/** Before every row the journal places: what the reader has read to when no row precedes. */ -const BEFORE_EVERY_ROW: AgentJournalPosition = { sequence: -1, index: 0 } - -/** A prompt the reader sent from this chat, as opposed to a message another agent sent through Orca. */ -export function isReaderPrompt( - slot: NativeChatTranscriptSlot -): slot is Extract { - return ( - slot.kind === 'message' && - slot.depth === 0 && - slot.message.role === 'user' && - slot.message.from === undefined - ) -} - -export function newestRowPosition( - slots: readonly NativeChatTranscriptSlot[], - limit?: AgentJournalPosition -): AgentJournalPosition { - let newest = BEFORE_EVERY_ROW - for (const slot of slots) { - const at = rowPosition(slot) - if ( - at && - (!limit || compareAgentJournalPositions(at, limit) < 0) && - compareAgentJournalPositions(at, newest) > 0 - ) { - newest = at - } - } - return newest -} - -/** Placed in the stretch the reader was away for, and not their own prompt. */ -export function isMissedRow( - slot: NativeChatTranscriptSlot, - after: AgentJournalPosition, - through: AgentJournalPosition -): boolean { - const at = rowPosition(slot) - return ( - at !== undefined && - compareAgentJournalPositions(at, after) > 0 && - compareAgentJournalPositions(at, through) <= 0 && - (slot.kind !== 'message' || slot.message.role !== 'user' || slot.message.from !== undefined) - ) -} diff --git a/src/renderer/src/components/native-chat/viewport/new-rows/native-chat-new-rows-marker.journal.test.tsx b/src/renderer/src/components/native-chat/viewport/new-rows/native-chat-new-rows-marker.journal.test.tsx deleted file mode 100644 index 095f5bd53a6..00000000000 --- a/src/renderer/src/components/native-chat/viewport/new-rows/native-chat-new-rows-marker.journal.test.tsx +++ /dev/null @@ -1,166 +0,0 @@ -// @vitest-environment happy-dom - -// The queued-run rule, driven from journal items through the desktop projection, turn membership, -// slots and the waiting split, the way NativeChatMessageList feeds the marker. - -import { renderHook } from '@testing-library/react' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { - AgentJournalRenderItem, - AgentJournalSubmission, - AgentJournalTurnItem -} from '../../../../../../shared/agent-session/journal/agent-session-journal-types' -import type { AgentJournalTurnScope } from '../../../../../../shared/agent-session/journal/agent-session-journal-turn-scope' -import { agentJournalSubmissionKey } from '../../../../../../shared/agent-session/journal/agent-session-journal-item-key' -import { owesStructuredAgentSessionWork } from '../../../../../../shared/agent-session/view/structured-agent-session-owed-work' -import { withNativeChatCutTurnNotices } from '../../../../../../shared/native-chat/transcript/native-chat-cut-turn-notice' -import { nativeChatTurnMembership } from '../../../../../../shared/native-chat/transcript/native-chat-turn-membership' -import { nativeChatRowsInDrawOrder } from '../../../../../../shared/native-chat/transcript/native-chat-turn-grouping' -import { projectStructuredAgentSessionMessages } from '../../conversation/projection/structured-agent-session-message-projection' -import { buildNativeChatTranscriptSlots } from '../../conversation/slots/native-chat-transcript-slots' -import { splitNativeChatSlotsWaitingBehindLiveTurn } from '../../conversation/slots/native-chat-transcript-slot-selection' -import { useNativeChatNewRowsMarker } from './native-chat-new-rows-marker' - -const THREAD: AgentJournalTurnScope = { kind: 'thread' } -const inTurn = (turnItemId: string): AgentJournalTurnScope => ({ kind: 'turn', turnItemId }) - -function item( - itemId: string, - sequence: number, - body: AgentJournalRenderItem['body'], - turnScope?: AgentJournalTurnScope -): AgentJournalRenderItem { - const row: AgentJournalRenderItem = { itemId, sequence, revision: 1, observedAt: sequence, body } - return turnScope ? { ...row, turnScope } : row -} - -const message = ( - id: string, - sequence: number, - role: 'user' | 'assistant', - scope: AgentJournalTurnScope -): AgentJournalRenderItem => - item(id, sequence, { kind: 'message', role, blocks: [{ type: 'text', text: id }] }, scope) - -/** A turn record; one that names itself as its opener was opened by the provider, not a send. */ -function turn( - id: string, - sequence: number, - state: 'running' | 'completed', - opener = id -): AgentJournalRenderItem { - const body: AgentJournalTurnItem = { - kind: 'turn', - turnId: id, - state, - userItemId: opener, - startedAt: sequence - } - return item(id, sequence, state === 'completed' ? { ...body, outcome: 'success' } : body, THREAD) -} - -const HELD = agentJournalSubmissionKey('held') -/** Accepted by the host and recorded, but not yet handed to the agent. */ -const held: AgentJournalSubmission = { - clientMessageId: 'held', - fence: 1, - payloadFingerprint: 'held', - dispatchState: 'pending', - providerItemId: null, - reason: null, - submittedAt: 4, - resolvedAt: null, - handoverRecorded: true -} -const handedOver = (at: number): AgentJournalSubmission => ({ - ...held, - dispatchState: 'accepted', - providerItemId: HELD, - resolvedAt: at, - handedOverAt: at -}) - -/** What NativeChatMessageList hands the marker for these journal items. */ -function pane(items: AgentJournalRenderItem[], submissions: AgentJournalSubmission[]) { - const working = owesStructuredAgentSessionWork(items, submissions) - const messages = projectStructuredAgentSessionMessages( - withNativeChatCutTurnNotices(items), - [], - submissions - ) - const membership = nativeChatTurnMembership(messages, { items, submissions }) - const slots = buildNativeChatTranscriptSlots({ - messages: nativeChatRowsInDrawOrder(messages, membership.drawOrder), - turnKeys: nativeChatRowsInDrawOrder(membership.turnKeys, membership.drawOrder), - liveTurnKey: membership.liveTurnKey, - unresolvedRowIds: membership.unresolvedRowIds, - receipts: new Map(), - turnStatuses: { active: null, completedByTurn: {} }, - turnDiffs: new Map(), - expandedTurnKeys: new Set(), - isWorking: working, - lifecycleWorking: working - }) - return { - ...splitNativeChatSlotsWaitingBehindLiveTurn(slots, items, false, submissions, { working }), - host: { items } - } -} - -type Pane = ReturnType - -function leaveAndReturn(left: Pane, back: Pane) { - const view = renderHook( - ({ at, showing }: { at: Pane; showing: boolean }) => - useNativeChatNewRowsMarker(at.slots, at.waitingSlots, showing, at.host), - { initialProps: { at: left, showing: true } } - ) - view.rerender({ at: left, showing: false }) - view.rerender({ at: back, showing: true }) - return view -} - -describe('the New line for a send that ran while the reader was away, from the journal', () => { - let hasFocus: ReturnType - beforeEach(() => { - hasFocus = vi.spyOn(document, 'hasFocus').mockReturnValue(true) - }) - afterEach(() => hasFocus.mockRestore()) - - const EARLIER = [ - message('old-prompt', 1, 'user', inTurn('old-turn')), - turn('old-turn', 2, 'completed', 'old-prompt'), - message('old-answer', 3, 'assistant', inTurn('old-turn')) - ] - - it('draws when a held send ran behind a turn the provider opened and drew nothing in', () => { - const left = pane( - [...EARLIER, turn('resumed', 4, 'running'), message(HELD, 5, 'user', THREAD)], - [held] - ) - const back = pane( - [ - ...EARLIER, - turn('resumed', 4, 'completed'), - message(HELD, 6, 'user', inTurn('next')), - turn('next', 7, 'completed', HELD), - message('short-answer', 8, 'assistant', inTurn('next')) - ], - [handedOver(6)] - ) - expect(leaveAndReturn(left, back).result.current.marker).toMatchObject({ look: 'line' }) - }) - - it("weighs a new chat's first answer by height, its send held before any turn opened", () => { - const left = pane([message(HELD, 1, 'user', THREAD)], [held]) - const back = pane( - [ - message(HELD, 2, 'user', inTurn('first')), - turn('first', 3, 'completed', HELD), - message('first-answer', 4, 'assistant', inTurn('first')) - ], - [handedOver(2)] - ) - expect(leaveAndReturn(left, back).result.current.marker).toMatchObject({ look: 'measure' }) - }) -}) diff --git a/src/renderer/src/components/native-chat/viewport/new-rows/native-chat-new-rows-marker.ts b/src/renderer/src/components/native-chat/viewport/new-rows/native-chat-new-rows-marker.ts index 7e910ee5ab2..e31d5e1d225 100644 --- a/src/renderer/src/components/native-chat/viewport/new-rows/native-chat-new-rows-marker.ts +++ b/src/renderer/src/components/native-chat/viewport/new-rows/native-chat-new-rows-marker.ts @@ -3,12 +3,7 @@ import { useCallback, useMemo, useState } from 'react' import { useOrcaWindowForegroundFocused } from '@/hooks/use-orca-window-foreground-focused' import { compareAgentJournalPositions } from '../../../../../../shared/agent-session/journal/agent-session-journal-position' -import type { - AgentJournalPosition, - AgentJournalRenderItem -} from '../../../../../../shared/agent-session/journal/agent-session-journal-types' -import type { AgentSessionLatestTurn } from '../../../../../../shared/agent-session/agent-session-wire' -import { runningStructuredAgentSessionTurnId } from '../../../../../../shared/agent-session/view/structured-agent-session-live-turn' +import type { AgentJournalPosition } from '../../../../../../shared/agent-session/journal/agent-session-journal-types' import { useAppStore } from '@/store' import { nativeChatMarksNewMessages } from '../../../../../../shared/native-chat/native-chat-appearance-settings' import { @@ -16,12 +11,16 @@ import { nativeChatSlotKey } from '../../conversation/slots/native-chat-transcript-slot-selection' import type { NativeChatTranscriptSlot } from '../../conversation/slots/native-chat-transcript-slots' -import { - isMissedRow, - isReaderPrompt, - newestRowPosition, - rowPosition -} from './native-chat-missed-row-positions' + +/** Use journal positions for conversation rows, including folded-turn bars; terminal-backed rows have none and remain unmarked. */ +function rowPosition(slot: NativeChatTranscriptSlot): AgentJournalPosition | undefined { + return slot.kind === 'message' && slot.depth === 0 && (slot.drawsMessage || slot.statusAbove) + ? slot.message.journalPosition + : undefined +} + +/** Before every row the journal places: what the reader has read to when no row precedes. */ +const BEFORE_EVERY_ROW: AgentJournalPosition = { sequence: -1, index: 0 } /** Where a reader who looks away now has read to, and the reply the agent is still writing. */ type NativeChatLeftAt = { @@ -29,12 +28,6 @@ type NativeChatLeftAt = { position: AgentJournalPosition /** A growing reply counts as missed from the position immediately before it. */ writing: { key: string; size: number; before: AgentJournalPosition } | null - /** The host had a turn running, whether or not it had drawn a row yet. */ - running: boolean - /** The reader's prompts there were, by row key: a key stays the same from send to journal. */ - prompts: ReadonlySet - /** Those the host held in its queue, to hand to the agent later. */ - queuedPrompts: ReadonlySet } function rowSize(slot: NativeChatTranscriptSlot): number { @@ -43,10 +36,27 @@ function rowSize(slot: NativeChatTranscriptSlot): number { : 0 } +function newestRowPosition( + slots: readonly NativeChatTranscriptSlot[], + limit?: AgentJournalPosition +): AgentJournalPosition { + let newest = BEFORE_EVERY_ROW + for (const slot of slots) { + const at = rowPosition(slot) + if ( + at && + (!limit || compareAgentJournalPositions(at, limit) < 0) && + compareAgentJournalPositions(at, newest) > 0 + ) { + newest = at + } + } + return newest +} + function nativeChatLeftAt( slots: readonly NativeChatTranscriptSlot[], - waitingSlots: readonly NativeChatTranscriptSlot[], - running: boolean + waitingSlots: readonly NativeChatTranscriptSlot[] ): NativeChatLeftAt { const all = [...slots, ...waitingSlots] // Later user prompts or held sends do not stop the turn’s latest assistant row from growing. @@ -61,18 +71,7 @@ function nativeChatLeftAt( const writing = at ? { key: nativeChatSlotKey(writer), size: rowSize(writer), before: newestRowPosition(all, at) } : null - const prompts = new Set() - const queuedPrompts = new Set() - for (const slot of all) { - if (isReaderPrompt(slot)) { - prompts.add(slot.message.id) - // Held by the host, not merely drawn after a turn that is still opening. - if (slot.message.queued) { - queuedPrompts.add(slot.message.id) - } - } - } - return { position: newestRowPosition(all), writing, prompts, queuedPrompts, running } + return { position: newestRowPosition(all), writing } } /** The first row that came while the reader was away, and the journal stretch it came in. */ @@ -84,9 +83,23 @@ export type NativeChatMissedRows = { through: AgentJournalPosition } +function isMissedRow( + slot: NativeChatTranscriptSlot, + after: AgentJournalPosition, + through: AgentJournalPosition +): boolean { + const at = rowPosition(slot) + return ( + at !== undefined && + compareAgentJournalPositions(at, after) > 0 && + compareAgentJournalPositions(at, through) <= 0 && + (slot.kind !== 'message' || slot.message.role !== 'user' || slot.message.from !== undefined) + ) +} + function nativeChatMissedRows( slots: readonly NativeChatTranscriptSlot[], - { position, writing }: Pick + { position, writing }: NativeChatLeftAt ): NativeChatMissedRows | null { const row = writing && slots.find((slot) => nativeChatSlotKey(slot) === writing.key) const after = writing && !(row && rowSize(row) === writing.size) ? writing.before : position @@ -95,63 +108,6 @@ function nativeChatMissedRows( return first ? { key: nativeChatSlotKey(first), after, through } : null } -/** How the marker shows: a line only where it helps, measured by height when nothing else calls for it. */ -export type NativeChatNewRowsMarkerLook = - /** Laid out but not seen, so the marker can measure what was missed below it. */ - | 'measure' - | 'line' - /** Rows were missed, but too few to need a line: only the announcement tells of them. */ - | 'none' - /** The line was seen and has faded; it keeps its space so nothing below it moves. */ - | 'faded' - -/** Another agent posted, or a prompt the reader did not just send ran while they were away. */ -function nativeChatMissedNeedsLine( - slots: readonly NativeChatTranscriptSlot[], - left: NativeChatLeftAt, - { after, through }: NativeChatMissedRows -): boolean { - const first = slots.find((slot) => isMissedRow(slot, after, through)) - const firstAt = first && rowPosition(first) - return slots.some( - (slot) => - // The only user rows a missed stretch admits are ones another agent sent through Orca. - (slot.kind === 'message' && - slot.message.role === 'user' && - isMissedRow(slot, after, through)) || - (isReaderPrompt(slot) && nativeChatPromptRanWhileAway(slot, left, firstAt)) - ) -} - -/** A queued send handed over, or a prompt this pane never sent (an auto-continue, a connector delivery). */ -function nativeChatPromptRanWhileAway( - slot: Extract, - left: NativeChatLeftAt, - /** Where the first missed row is. */ - firstMissedAt: AgentJournalPosition | undefined -): boolean { - const at = rowPosition(slot) - const key = slot.message.id - if ( - at === undefined || - slot.message.queued === true || - compareAgentJournalPositions(at, left.position) <= 0 - ) { - return false - } - if (!left.prompts.has(key)) { - return true - } - // A queued send ran behind a turn the host had running (drawn or not), or behind work the reader - // missed. The host also holds a chat's first send while the chat opens; no turn runs then, so that - // one runs first, its reply the answer awaited. - return ( - left.queuedPrompts.has(key) && - (left.running || - (firstMissedAt !== undefined && compareAgentJournalPositions(at, firstMissedAt) > 0)) - ) -} - export type NativeChatNewRowsMarkerPlacement = { /** Key of the row the marker draws in. */ key: string @@ -159,17 +115,10 @@ export type NativeChatNewRowsMarkerPlacement = { } export type NativeChatNewRowsMarkerState = NativeChatNewRowsMarkerPlacement & { - look: Exclude /** The reader has just come back: bring the marker into view, once. */ arriving: boolean - /** The window is in front and this pane is showing: a seen line's clock may run. */ - watching: boolean onArrived: () => void onDismiss: () => void - /** With `look: 'measure'`: whether what came below the marker is tall enough to need a line. */ - onMeasured: (tall: boolean) => void - /** With `look: 'line'`: the reader has seen it for long enough, so it fades out. */ - onFaded: () => void } /** Place above the first missed row, or its turn bar when the two lines would otherwise touch. */ @@ -204,15 +153,9 @@ export function useNativeChatNewRowsMarker( /** Rows held behind the running turn: seen and missed like any other, drawn outside `slots`. */ waitingSlots: readonly NativeChatTranscriptSlot[], /** The pane is the one showing, and shows what the host holds now. */ - isShowingLatest: boolean, - /** The host's journal: its turn record, not row liveness, says whether a turn is running. A held - * first send has no turn yet, and a turn the provider opened may have drawn no row. */ - host: { items: readonly AgentJournalRenderItem[]; latestTurn?: AgentSessionLatestTurn | null } + isShowingLatest: boolean ): { - /** The marker to draw, when rows were missed and a line helps. */ marker: NativeChatNewRowsMarkerState | undefined - /** Rows came while the reader was away, line or not; it counts returns, so each is announced. */ - announcement: number | undefined /** The slot drawing the marker, or -1. */ index: number /** The reader answered the conversation, so nothing in it is news to them. */ @@ -229,118 +172,40 @@ export function useNativeChatNewRowsMarker( /** Stands until the reader answers. */ missed: NativeChatMissedRows | null arriving: boolean - look: NativeChatNewRowsMarkerLook - /** While measuring: the faded line from before, kept in place if this return needs no line. */ - faded: NativeChatMissedRows | null - /** Returns that found rows missed. */ - returns: number - }>({ - watching, - left: null, - missed: null, - arriving: false, - look: 'measure', - faded: null, - returns: 0 - }) + }>({ watching, left: null, missed: null, arriving: false }) if (!enabled) { if (!seen.watching || seen.left !== null || seen.missed !== null) { - setSeen({ ...seen, watching, left: null, missed: null, arriving: false, faded: null }) + setSeen({ watching, left: null, missed: null, arriving: false }) } } else if (seen.watching !== watching) { const all = [...slots, ...waitingSlots] if (watching) { - const missed = seen.left && nativeChatMissedRows(all, seen.left) - // Weighed over the transcript only: a row still held behind the turn is marked once it joins it. - const needsLine = - seen.left !== null && missed !== null && nativeChatMissedNeedsLine(slots, seen.left, missed) - // Removing a faded line for no new one would move the rows below it with nothing to show why. - const faded = - seen.look === 'faded' && nativeChatNewRowsMarkerPlacement(all, seen.missed) !== undefined - ? seen.missed - : null - setSeen( - seen.left && missed - ? { - watching, - left: null, - missed, - arriving: true, - look: needsLine ? 'line' : 'measure', - faded: needsLine ? null : faded, - returns: seen.returns + 1 - } - : // Nothing new: a marker from before, faded or not drawn, stays as it was. - { ...seen, watching, left: null, arriving: false } - ) + const missed = seen.left ? nativeChatMissedRows(all, seen.left) : seen.missed + setSeen({ watching, left: null, missed, arriving: seen.left !== null && missed !== null }) } else { - // An unanswered line whose row still stands is kept: the reader may be midway through it. - // Once faded or never drawn, the next return is weighed afresh. - const marked = - (seen.look === 'line' || seen.look === 'measure') && - nativeChatNewRowsMarkerPlacement(all, seen.missed) !== undefined - setSeen({ - ...seen, - watching, - left: marked - ? null - : nativeChatLeftAt( - slots, - waitingSlots, - runningStructuredAgentSessionTurnId(host) !== null - ) - }) + // An unanswered marker whose row still stands is kept: the reader may be midway through it. + const marked = nativeChatNewRowsMarkerPlacement(all, seen.missed) !== undefined + setSeen({ ...seen, watching, left: marked ? null : nativeChatLeftAt(slots, waitingSlots) }) } } - const { missed, look } = seen + const { missed } = seen const placement = useMemo(() => nativeChatNewRowsMarkerPlacement(slots, missed), [slots, missed]) const dismiss = useCallback(() => { // Where they left as well: an answer sent before the chat has caught up settles that too. setSeen((held) => - held.missed === null && held.left === null - ? held - : { ...held, missed: null, left: null, faded: null } + held.missed === null && held.left === null ? held : { ...held, missed: null, left: null } ) }, []) const onArrived = useCallback(() => { setSeen((held) => (held.arriving ? { ...held, arriving: false } : held)) }, []) - const onMeasured = useCallback((tall: boolean) => { - setSeen((held) => - held.look !== 'measure' - ? held - : tall - ? { ...held, look: 'line', faded: null } - : held.faded - ? { ...held, missed: held.faded, look: 'faded', arriving: false, faded: null } - : { ...held, look: 'none', arriving: false } - ) - }, []) - const onFaded = useCallback(() => { - setSeen((held) => (held.look === 'line' ? { ...held, look: 'faded' } : held)) - }, []) const { key, place } = placement ?? {} const marker = useMemo( () => - key && place && look !== 'none' - ? { - key, - place, - look, - arriving: seen.arriving, - watching: focused, - onArrived, - onDismiss: dismiss, - onMeasured, - onFaded - } + key && place + ? { key, place, arriving: seen.arriving, onArrived, onDismiss: dismiss } : undefined, - [key, place, look, seen.arriving, focused, onArrived, dismiss, onMeasured, onFaded] + [key, place, seen.arriving, onArrived, dismiss] ) - return { - marker, - announcement: key ? seen.returns : undefined, - index: marker ? nativeChatSlotIndexOf(slots, key) : -1, - dismiss - } + return { marker, index: nativeChatSlotIndexOf(slots, key), dismiss } } diff --git a/src/renderer/src/components/settings/chat/chat-behavior-search.ts b/src/renderer/src/components/settings/chat/chat-behavior-search.ts index b990c69d389..57178866f34 100644 --- a/src/renderer/src/components/settings/chat/chat-behavior-search.ts +++ b/src/renderer/src/components/settings/chat/chat-behavior-search.ts @@ -86,7 +86,7 @@ export const getChatBehaviorEntriesByKey = createLocalizedCatalog(() => ({ ), description: translate( 'components.settings.nativeChat.markNewMessagesCopy', - "When you come back to a chat, a New line marks what you missed if it's long or someone else wrote, and scrolls there. It fades once you've seen it." + 'Coming back to a chat draws a New line above what arrived while you were away, and scrolls to it if you were reading the latest.' ), keywords: [translate('components.settings.nativeChat.markNewMessagesSearchUnread', 'unread')] }, diff --git a/src/renderer/src/i18n/locales/de.json b/src/renderer/src/i18n/locales/de.json index 0c83b1a6630..062bab6b7c7 100644 --- a/src/renderer/src/i18n/locales/de.json +++ b/src/renderer/src/i18n/locales/de.json @@ -20681,7 +20681,7 @@ "scrollToLatestOnSendCopy": "Senden im hochgescrollten Zustand springt zur neuesten Nachricht und folgt der Antwort. Deaktiviere dies, um deine Position zu behalten.", "scrollToLatestOnSendSearchPosition": "Position", "markNewMessagesTitle": "Neue Nachrichten bei der Rückkehr in einen Chat markieren", - "markNewMessagesCopy": "Wenn du zu einem Chat zurückkehrst, markiert eine Neu-Linie, was du verpasst hast, wenn es lang ist oder jemand anderes geschrieben hat, und scrollt dorthin. Sie verblasst, sobald du sie gesehen hast.", + "markNewMessagesCopy": "Bei der Rückkehr in einen Chat erscheint eine Neu-Linie über dem, was in deiner Abwesenheit eingegangen ist. Warst du bei der neuesten Nachricht, scrollt der Chat dorthin.", "markNewMessagesSearchUnread": "ungelesen", "inlineVisualsTitle": "Eingebettete Grafiken", "inlineVisualsCopy": "Dem Agenten erlauben, Diagramme, Schaubilder und Mockups in seinen Antworten anzuzeigen. Gilt für neu gestartete Chats.", diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index dc1abe90ef6..a5efbd1c672 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -21960,7 +21960,7 @@ "scrollToLatestOnSendCopy": "Sending while scrolled up jumps to the latest message and follows the reply. Turn off to keep your place.", "scrollToLatestOnSendSearchPosition": "position", "markNewMessagesTitle": "Mark new messages when returning to a chat", - "markNewMessagesCopy": "When you come back to a chat, a New line marks what you missed if it's long or someone else wrote, and scrolls there. It fades once you've seen it.", + "markNewMessagesCopy": "Coming back to a chat draws a New line above what arrived while you were away, and scrolls to it if you were reading the latest.", "markNewMessagesSearchUnread": "unread", "inlineVisualsTitle": "Inline visuals", "inlineVisualsCopy": "Let the agent show charts, diagrams and mockups inside its replies. Applies to newly started chats.", diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json index ee0c7657740..c85e0fedcdd 100644 --- a/src/renderer/src/i18n/locales/es.json +++ b/src/renderer/src/i18n/locales/es.json @@ -20448,7 +20448,7 @@ "scrollToLatestOnSendCopy": "Enviar con el chat desplazado hacia arriba salta al último mensaje y sigue la respuesta. Desactívalo para mantener tu posición.", "scrollToLatestOnSendSearchPosition": "posición", "markNewMessagesTitle": "Marcar los mensajes nuevos al volver a un chat", - "markNewMessagesCopy": "Cuando vuelves a un chat, una línea Nuevo marca lo que te perdiste si es largo o si otra persona escribió, y se desplaza hasta allí. Se desvanece en cuanto la ves.", + "markNewMessagesCopy": "Al volver a un chat aparece una línea Nuevo encima de lo que llegó mientras no estabas, y el chat se desplaza hasta ella si estabas leyendo lo más reciente.", "markNewMessagesSearchUnread": "no leído", "followUpBehaviorCopy": "Los mensajes enviados mientras el agente trabaja esperan como tarjetas hasta que termina el turno, en lugar de entrar en el turno en curso. Cmd/Ctrl+Intro hace lo contrario para un mensaje y, si no hay texto escrito, dirige la primera tarjeta en espera al turno en curso. Los mensajes con imágenes también esperan como tarjetas.", "inlineVisualsTitle": "Elementos visuales inline", diff --git a/src/renderer/src/i18n/locales/fr.json b/src/renderer/src/i18n/locales/fr.json index f399f11898a..91e2377e60a 100644 --- a/src/renderer/src/i18n/locales/fr.json +++ b/src/renderer/src/i18n/locales/fr.json @@ -20827,7 +20827,7 @@ "scrollToLatestOnSendCopy": "Envoyer après avoir remonté le fil fait défiler jusqu’au dernier message et suit la réponse. Désactivez cette option pour garder votre position.", "scrollToLatestOnSendSearchPosition": "position", "markNewMessagesTitle": "Signaler les nouveaux messages au retour dans un chat", - "markNewMessagesCopy": "Quand vous revenez dans un chat, une ligne Nouveau marque ce que vous avez manqué si c'est long ou si quelqu'un d'autre a écrit, et y fait défiler la vue. Elle s'estompe une fois vue.", + "markNewMessagesCopy": "En revenant dans un chat, une ligne Nouveau s'affiche au-dessus de ce qui est arrivé pendant votre absence, et le chat y défile si vous lisiez les derniers messages.", "markNewMessagesSearchUnread": "non lu", "followUpBehaviorCopy": "Les messages envoyés pendant que l’agent travaille attendent sous forme de cartes jusqu’à la fin du tour, au lieu d’entrer dans le tour en cours. Cmd/Ctrl+Entrée fait l’inverse pour un message et, si rien n’est saisi, dirige la première carte en attente vers le tour en cours. Les messages contenant des images attendent aussi sous forme de cartes.", "inlineVisualsTitle": "Visuels intégrés", diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json index 7f5f01a0252..511abc5dd2d 100644 --- a/src/renderer/src/i18n/locales/ja.json +++ b/src/renderer/src/i18n/locales/ja.json @@ -19971,7 +19971,7 @@ "scrollToLatestOnSendCopy": "上にスクロールした状態で送信すると、最新のメッセージに移動して返信を追従します。オフにすると現在の位置を維持します。", "scrollToLatestOnSendSearchPosition": "位置", "markNewMessagesTitle": "チャットに戻ったときに新着メッセージを示す", - "markNewMessagesCopy": "チャットに戻ったとき、見逃した内容が長い場合や他の人が書き込んだ場合は、「新着」の線でその位置を示し、そこまでスクロールします。線は一度見るとフェードアウトします。", + "markNewMessagesCopy": "チャットに戻ると、離れている間に届いたメッセージの上に「新着」の線を表示します。最新のメッセージを読んでいた場合は、そこまでスクロールします。", "markNewMessagesSearchUnread": "未読", "followUpBehaviorCopy": "Agent の作業中に送信したメッセージは、実行中のターンに送られず、ターンが終わるまでカードとして待機します。Cmd/Ctrl+Enter では 1 件だけその逆になり、何も入力されていない場合は最初の待機カードを実行中のターンに送ります。画像付きメッセージもカードとして待機します。", "inlineVisualsTitle": "インライン表示", diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json index d6b619537b2..e15a1ef2e38 100644 --- a/src/renderer/src/i18n/locales/ko.json +++ b/src/renderer/src/i18n/locales/ko.json @@ -19968,7 +19968,7 @@ "scrollToLatestOnSendCopy": "위로 스크롤한 상태에서 보내면 최신 메시지로 이동하고 답장을 따라갑니다. 끄면 현재 위치가 유지됩니다.", "scrollToLatestOnSendSearchPosition": "위치", "markNewMessagesTitle": "채팅으로 돌아올 때 새 메시지 표시", - "markNewMessagesCopy": "채팅으로 돌아왔을 때 놓친 내용이 길거나 다른 사람이 글을 썼다면 '새 메시지' 선으로 그 위치를 표시하고 그곳으로 스크롤합니다. 한 번 보고 나면 서서히 흐려집니다.", + "markNewMessagesCopy": "채팅으로 돌아오면 자리를 비운 동안 도착한 내용 위에 '새 메시지' 선을 표시합니다. 최신 메시지를 보고 있었다면 그 위치로 스크롤합니다.", "markNewMessagesSearchUnread": "읽지 않음", "followUpBehaviorCopy": "Agent가 작업하는 동안 보낸 메시지는 진행 중인 턴에 들어가지 않고 턴이 끝날 때까지 카드로 대기합니다. Cmd/Ctrl+Enter를 누르면 메시지 하나에 대해 반대로 동작하며, 입력한 내용이 없으면 첫 대기 카드를 진행 중인 턴으로 보냅니다. 이미지가 포함된 메시지도 카드로 대기합니다.", "inlineVisualsTitle": "인라인 시각 자료", diff --git a/src/renderer/src/i18n/locales/pt.json b/src/renderer/src/i18n/locales/pt.json index e59c8c9b986..e6a95271fa6 100644 --- a/src/renderer/src/i18n/locales/pt.json +++ b/src/renderer/src/i18n/locales/pt.json @@ -22217,7 +22217,7 @@ "scrollToLatestOnSendCopy": "Enviar com o chat rolado para cima leva à mensagem mais recente e acompanha a resposta. Desative para manter sua posição.", "scrollToLatestOnSendSearchPosition": "posição", "markNewMessagesTitle": "Marcar mensagens novas ao voltar para um chat", - "markNewMessagesCopy": "Quando você volta para um chat, uma linha Novas marca o que você perdeu se for longo ou se outra pessoa escreveu, e rola até lá. Ela esmaece depois que você a vê.", + "markNewMessagesCopy": "Ao voltar para um chat, uma linha Novas aparece acima do que chegou enquanto você estava fora, e o chat rola até ela se você estava lendo as mais recentes.", "markNewMessagesSearchUnread": "não lida", "inlineVisualsTitle": "Visualizações nas respostas", "inlineVisualsCopy": "Permitir que o agente mostre gráficos, diagramas e mockups nas respostas. Aplica-se a novos chats.", diff --git a/src/renderer/src/i18n/locales/ru.json b/src/renderer/src/i18n/locales/ru.json index 3645581871e..2a19b39d005 100644 --- a/src/renderer/src/i18n/locales/ru.json +++ b/src/renderer/src/i18n/locales/ru.json @@ -21361,7 +21361,7 @@ "scrollToLatestOnSendCopy": "Отправка при прокрутке вверх переносит к последнему сообщению и следит за ответом. Отключите, чтобы остаться на месте.", "scrollToLatestOnSendSearchPosition": "позиция", "markNewMessagesTitle": "Отмечать новые сообщения при возвращении в чат", - "markNewMessagesCopy": "Когда вы возвращаетесь в чат, линия «Новое» отмечает то, что вы пропустили, если пропущено много или написал кто-то другой, и прокручивает туда. Она плавно гаснет, как только вы её увидите.", + "markNewMessagesCopy": "При возвращении в чат над тем, что пришло в ваше отсутствие, появляется линия «Новое». Если вы читали последние сообщения, чат прокручивается к ней.", "markNewMessagesSearchUnread": "непрочитанные", "inlineVisualsTitle": "Визуализации в чате", "inlineVisualsCopy": "Агент сможет показывать графики, схемы и макеты прямо в ответах. Применяется к новым чатам.", diff --git a/src/renderer/src/i18n/locales/tr.json b/src/renderer/src/i18n/locales/tr.json index 2442c221309..84d6397c7f8 100644 --- a/src/renderer/src/i18n/locales/tr.json +++ b/src/renderer/src/i18n/locales/tr.json @@ -20656,7 +20656,7 @@ "scrollToLatestOnSendCopy": "Yukarı kaydırılmışken göndermek en son mesaja atlar ve yanıtı takip eder. Konumunuzu korumak için kapatın.", "scrollToLatestOnSendSearchPosition": "konum", "markNewMessagesTitle": "Bir sohbete dönünce yeni mesajları işaretle", - "markNewMessagesCopy": "Bir sohbete döndüğünüzde, kaçırdıklarınız uzunsa ya da başka biri yazdıysa bir Yeni çizgisi bunları işaretler ve oraya kaydırır. Gördükten sonra yavaşça solar.", + "markNewMessagesCopy": "Bir sohbete döndüğünüzde, siz yokken gelenlerin üstünde bir Yeni çizgisi gösterilir. En son mesajları okuyorduysanız sohbet oraya kaydırılır.", "markNewMessagesSearchUnread": "okunmamış", "inlineVisualsTitle": "Yanıtlarda görseller", "inlineVisualsCopy": "Ajanın yanıtlarında grafikler, diyagramlar ve taslaklar göstermesine izin verin. Yeni başlatılan sohbetlere uygulanır.", diff --git a/src/renderer/src/i18n/locales/zh-Hant.json b/src/renderer/src/i18n/locales/zh-Hant.json index fceccf2497b..9a250e4ea66 100644 --- a/src/renderer/src/i18n/locales/zh-Hant.json +++ b/src/renderer/src/i18n/locales/zh-Hant.json @@ -20364,7 +20364,7 @@ "scrollToLatestOnSendCopy": "向上捲動時傳送訊息會跳到最新訊息並跟隨回覆。關閉後會保留目前位置。", "scrollToLatestOnSendSearchPosition": "位置", "markNewMessagesTitle": "返回聊天時標記新訊息", - "markNewMessagesCopy": "返回聊天時,如果錯過的內容很長或有其他人發了訊息,會用「新訊息」分隔線標出你錯過的內容並捲動到那裡。看過之後它會淡出。", + "markNewMessagesCopy": "返回聊天時,在你離開期間收到的內容上方顯示「新訊息」分隔線;如果你之前在看最新訊息,會捲動到該處。", "markNewMessagesSearchUnread": "未讀", "inlineVisualsTitle": "內嵌視覺化", "inlineVisualsCopy": "允許代理程式在其回覆中顯示圖表、示意圖和原型圖。適用於新啟動的聊天。", diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json index d8a00d1e5ad..2d291616a23 100644 --- a/src/renderer/src/i18n/locales/zh.json +++ b/src/renderer/src/i18n/locales/zh.json @@ -20315,7 +20315,7 @@ "turnDiffsSearchChangedFiles": "已更改文件", "turnDiffsSearchEdits": "编辑", "markNewMessagesTitle": "返回聊天时标记新消息", - "markNewMessagesCopy": "返回聊天时,如果错过的内容很长或有其他人发了消息,会用“新消息”分隔线标出你错过的内容并滚动到那里。看过之后它会淡出。", + "markNewMessagesCopy": "返回聊天时,在你离开期间收到的内容上方显示“新消息”分隔线;如果你之前在看最新消息,会滚动到该处。", "markNewMessagesSearchUnread": "未读" }, "voice": { From 55753811c5b70fa839b0ae2b6a083992ee679946 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 20:29:15 -0800 Subject: [PATCH 006/178] test: trim issue labeler coverage to mutation reconciliation (#41810) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../issue-os-labeler-form-labels.test.mjs | 273 ------------------ .../issue-os-labeler-reconciliation.test.mjs | 180 ------------ ...issue-os-labeler-translation-note.test.mjs | 74 ----- 3 files changed, 527 deletions(-) delete mode 100644 config/scripts/ci/workflow-contracts/issue-os-labeler-form-labels.test.mjs delete mode 100644 config/scripts/ci/workflow-contracts/issue-os-labeler-translation-note.test.mjs diff --git a/config/scripts/ci/workflow-contracts/issue-os-labeler-form-labels.test.mjs b/config/scripts/ci/workflow-contracts/issue-os-labeler-form-labels.test.mjs deleted file mode 100644 index 32ba5766e42..00000000000 --- a/config/scripts/ci/workflow-contracts/issue-os-labeler-form-labels.test.mjs +++ /dev/null @@ -1,273 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - MANAGED_LABELS, - bugForm, - labelIssue, - readIssueForm, - renderBugBody, - renderFormBody, - renderSections -} from './issue-os-labeler-script-runner.mjs' - -const WHERE = 'Where were you using Orca?' -const OS = 'Operating system' -const REMOTE_OS = 'Remote host operating system' -const UNRELATED = ['bug', 'needs-triage'] -const STALE = [...UNRELATED, ...MANAGED_LABELS] - -const optionsOf = (id) => bugForm.body.find((field) => field.id === id).attributes.options - -// Reconciles from "no managed labels" and from "every managed label"; both must converge. -async function expectReconciled(body, managed) { - const fresh = await labelIssue(body, UNRELATED) - expect(fresh.labels).toEqual([...UNRELATED, ...managed]) - expect(fresh.writes).toEqual(managed.length > 0 ? [['addLabels', managed]] : []) - - const stale = await labelIssue(body, STALE) - expect(stale.labels).toEqual([...UNRELATED, ...managed]) - expect(stale.writes).toEqual( - MANAGED_LABELS.filter((label) => !managed.includes(label)).map((label) => [ - 'removeLabel', - label - ]) - ) -} - -async function expectUntouched(body) { - for (const labels of [UNRELATED, STALE]) { - expect(await labelIssue(body, labels)).toEqual({ labels, writes: [] }) - } -} - -describe('current bug form', () => { - it('recognises every option the shipped form offers', async () => { - // An unrecognised value leaves the issue untouched; a recognised one clears stale labels. - for (const [id, heading] of [ - ['where', WHERE], - ['os', OS], - ['remote_os', REMOTE_OS] - ]) { - for (const option of optionsOf(id)) { - const result = await labelIssue(renderBugBody({ [heading]: option }), STALE) - expect(result.writes, `${heading}=${option}`).not.toEqual([]) - } - } - }) - - it.each([ - ['Desktop only', 'macOS', ['os:macos']], - ['Desktop + Orca server', 'Windows', ['os:Windows']], - ['Desktop + SSH host', 'Linux', ['os:linux']], - ['Mobile app', 'iOS', ['os:mobile', 'mobile']], - ['Mobile app', 'Android', ['os:mobile', 'mobile']] - ])('Where=%s, OS=%s -> %j', async (where, os, managed) => { - await expectReconciled(renderBugBody({ [WHERE]: where, [OS]: os }), managed) - }) - - it('labels the client device, never the remote host or WSL distro', async () => { - for (const where of ['Desktop + SSH host', 'Desktop + WSL']) { - const body = renderBugBody({ [WHERE]: where, [OS]: 'Windows', [REMOTE_OS]: 'Linux' }) - await expectReconciled(body, ['os:Windows']) - } - }) - - it('keeps OS and Where independent when they disagree', async () => { - await expectReconciled(renderBugBody({ [WHERE]: 'Web browser', [OS]: 'iOS' }), ['os:mobile']) - await expectReconciled(renderBugBody({ [WHERE]: 'Mobile app', [OS]: 'macOS' }), [ - 'os:macos', - 'mobile' - ]) - }) - - it('drops mobile labels when a mobile report is edited to desktop', async () => { - const edited = renderBugBody({ [WHERE]: 'Desktop only', [OS]: 'macOS' }) - expect(await labelIssue(edited, ['bug', 'os:mobile', 'mobile', 'needs-triage'])).toEqual({ - labels: ['bug', 'needs-triage', 'os:macos'], - writes: [ - ['addLabels', ['os:macos']], - ['removeLabel', 'os:mobile'], - ['removeLabel', 'mobile'] - ] - }) - }) - - it.each([ - ['Other OS on mobile', { [WHERE]: 'Mobile app', [OS]: 'Other' }, ['mobile']], - ['unselected Where', { [OS]: 'Linux' }, ['os:linux']], - ['everything unselected', {}, []] - ])('%s cleans up stale managed labels', async (_name, values, managed) => { - await expectReconciled(renderBugBody(values), managed) - }) - - it('reads a truly empty section as unselected instead of the next heading', async () => { - const body = `### ${WHERE}\n\n### ${OS}\n\n\n### ${REMOTE_OS}\n### Orca version\n\n### Details\n\nx` - await expectReconciled(body, []) - }) - - it.each([ - ['CRLF line endings', (body) => body.replace(/\n/g, '\r\n')], - ['padded and differently cased values', (body) => body.replace('Mobile app', ' MOBILE App \t')] - ])('accepts %s', async (_name, transform) => { - const body = transform(renderBugBody({ [WHERE]: 'Mobile app', [OS]: 'Android' })) - await expectReconciled(body, ['os:mobile', 'mobile']) - }) -}) - -describe('retired option strings and headings', () => { - it.each([ - ['current heading, retired value', WHERE, 'Mobile (Android)', OS, ['os:linux', 'mobile']], - ['Client heading, retired value', 'Client', 'Desktop + SSH', OS, ['os:linux']], - [ - 'Client heading, bare value', - 'Client', - 'Mobile', - 'Host operating system', - ['os:linux', 'mobile'] - ] - ])('%s keeps its meaning', async (_name, whereHeading, where, osHeading, managed) => { - const body = renderSections([ - [whereHeading, where], - [osHeading, 'Linux'], - ['Orca version', ''], - ['Details', 'x'] - ]) - await expectReconciled(body, managed) - }) - - it('does not accept bare Mobile under the current Where heading', async () => { - await expectUntouched(renderBugBody({ [WHERE]: 'Mobile', [OS]: 'Linux' })) - }) - - it.each([ - [ - 'form on main', - [ - [OS, 'Windows'], - ['Orca version', 'v1.4.2'], - ['Details', 'x'] - ] - ], - [ - 'first form, ending at Reproduction steps', - [ - ['Summary', 'Crash on launch'], - [OS, 'Windows'], - ['Orca version', 'v0.9.0'], - ['Coding harness and version', 'Claude Code 2.0'], - ['Reproduction steps', `1. Open\n\n### ${OS}\n\nLinux`], - ['Expected behavior', 'Works'] - ] - ] - ])('%s without a Where field gets its OS label and no mobile label', async (_name, sections) => { - await expectReconciled(renderSections(sections), ['os:Windows']) - }) - - it('prefers the current heading by presence, even when it is blank', async () => { - const sections = (where, os) => - renderSections([ - [WHERE, where], - ['Client', 'Mobile'], - [OS, os], - ['Host operating system', 'Linux'], - ['Orca version', ''], - ['Details', 'x'] - ]) - await expectReconciled(sections('Desktop only', 'macOS'), ['os:macos']) - await expectReconciled(sections('', ''), []) - // A shadowed legacy value is ignored even when it is not an allowed option. - const shadowed = sections('Mobile app', 'iOS') - .replace('### Client\n\nMobile', '### Client\n\nSmart fridge') - .replace('### Host operating system\n\nLinux', '### Host operating system\n\nTempleOS') - await expectReconciled(shadowed, ['os:mobile', 'mobile']) - }) - - it('accepts the legacy heading before the current one in the same group', async () => { - const body = renderSections([ - ['Client', 'Mobile'], - [WHERE, 'Web browser'], - ['Host operating system', 'Linux'], - [OS, 'Windows'], - ['Orca version', ''], - ['Details', 'x'] - ]) - await expectReconciled(body, ['os:Windows']) - }) -}) - -describe('bodies that must never change labels', () => { - const valid = renderBugBody({ [WHERE]: 'Mobile app', [OS]: 'Linux', [REMOTE_OS]: 'Linux' }) - const fenced = `\`\`\`md\n${valid}\n\`\`\`` - - it.each(['TempleOS', '__proto__', 'constructor'])( - 'unknown or prototype-named value %s in any dropdown', - async (value) => { - await expectUntouched(renderBugBody({ [WHERE]: 'Mobile app', [OS]: value })) - await expectUntouched(renderBugBody({ [WHERE]: value, [OS]: 'Linux' })) - await expectUntouched(renderBugBody({ [OS]: 'Linux', [REMOTE_OS]: value })) - await expectUntouched( - renderSections([ - ['Client', value], - ['Host operating system', 'Linux'], - ['Orca version', ''], - ['Details', 'x'] - ]) - ) - } - ) - - it.each([ - [ - 'repeated OS heading', - valid.replace(`### ${REMOTE_OS}`, `### ${OS}\n\nmacOS\n\n### ${REMOTE_OS}`) - ], - [ - 'unknown heading', - valid.replace('### Orca version', '### Device model\n\nPixel\n\n### Orca version') - ], - [ - 'OS before Where', - renderSections([ - [OS, 'Linux'], - [WHERE, 'Mobile app'], - ['Orca version', ''], - ['Details', 'x'] - ]) - ], - ['multiline dropdown value', valid.replace(`### ${OS}\n\nLinux`, `### ${OS}\n\nLinux\nmacOS`)], - [ - 'missing free-text heading', - renderSections([ - [WHERE, 'Mobile app'], - [OS, 'Linux'], - ['Orca version', ''] - ]) - ], - ['leading prose', `Hello team,\n\n${valid}`], - ['leading code fence', fenced] - ])('%s', async (_name, body) => { - await expectUntouched(body) - }) - - it('Other and Feature forms, even with a bug form pasted into their text', async () => { - await expectUntouched(renderFormBody(readIssueForm('other'), { Details: valid })) - await expectUntouched( - renderFormBody(readIssueForm('feature_request'), { - 'Problem or use case': valid, - 'Proposed solution': fenced - }) - ) - }) -}) - -describe('free text after the metadata prefix', () => { - it.each([ - ['conflicting form example', `### ${OS}\n\nLinux\n\n### ${WHERE}\n\nDesktop only`], - [ - 'unknown, repeated and malformed headings', - `### Details\n\n### Device\n\n### ${OS}\nA\nB\n###` - ] - ])('ignores a %s in Details', async (_name, details) => { - const body = renderBugBody({ [WHERE]: 'Mobile app', [OS]: 'Android', Details: details }) - await expectReconciled(body, ['os:mobile', 'mobile']) - }) -}) diff --git a/config/scripts/ci/workflow-contracts/issue-os-labeler-reconciliation.test.mjs b/config/scripts/ci/workflow-contracts/issue-os-labeler-reconciliation.test.mjs index e9a7d60742e..00eebaf651b 100644 --- a/config/scripts/ci/workflow-contracts/issue-os-labeler-reconciliation.test.mjs +++ b/config/scripts/ci/workflow-contracts/issue-os-labeler-reconciliation.test.mjs @@ -1,6 +1,5 @@ import { expect, it } from 'vitest' import { - ISSUE_NUMBER, createFakeIssue, renderBugBody, runLabelerScript @@ -10,57 +9,9 @@ const WHERE = 'Where were you using Orca?' const OS = 'Operating system' const MOBILE = renderBugBody({ [WHERE]: 'Mobile app', [OS]: 'iOS' }) const DESKTOP = renderBugBody({ [WHERE]: 'Desktop only', [OS]: 'macOS' }) -const LINUX = renderBugBody({ [WHERE]: 'Desktop only', [OS]: 'Linux' }) -const methods = (issue) => issue.calls.map((call) => call.method) const writes = (issue) => issue.writes().map(({ method, args }) => [method, args.labels ?? args.name]) -const apiError = (status) => Object.assign(new Error(`HTTP ${status}`), { status }) -const failure = (pattern) => ({ message: expect.stringMatching(pattern) }) - -it('addresses every call to the event issue and verifies with a final read', async () => { - const issue = createFakeIssue({ body: DESKTOP, labels: ['bug', 'os:mobile', 'mobile'] }) - await runLabelerScript(issue) - - const target = { owner: 'owner', repo: 'repo', issue_number: ISSUE_NUMBER } - expect(issue.calls.map(({ method, args }) => [method, args])).toEqual([ - ['get', target], - ['addLabels', { ...target, labels: ['os:macos'] }], - ['removeLabel', { ...target, name: 'os:mobile' }], - ['removeLabel', { ...target, name: 'mobile' }], - ['get', target] - ]) - expect(issue.labels).toEqual(['bug', 'os:macos']) -}) - -it('ignores the webhook snapshot when a stale older event runs after a newer one', async () => { - const issue = createFakeIssue({ body: DESKTOP, labels: ['bug', 'os:mobile', 'mobile'] }) - const newerPayload = { number: ISSUE_NUMBER, body: DESKTOP, labels: [{ name: 'bug' }] } - const stalePayload = { - number: ISSUE_NUMBER, - body: LINUX, - labels: [{ name: 'bug' }, { name: 'wontfix' }] - } - - await runLabelerScript(issue, { payloadIssue: newerPayload }) - expect(issue.labels).toEqual(['bug', 'os:macos']) - - const writesBefore = issue.writes().length - await runLabelerScript(issue, { payloadIssue: stalePayload }) - expect(issue.labels).toEqual(['bug', 'os:macos']) - expect(issue.writes()).toHaveLength(writesBefore) -}) - -it('is idempotent across repeated events', async () => { - const issue = createFakeIssue({ body: MOBILE, labels: ['bug'] }) - await runLabelerScript(issue) - await runLabelerScript(issue) - await runLabelerScript(issue) - - expect(issue.labels).toEqual(['bug', 'os:mobile', 'mobile']) - expect(writes(issue)).toEqual([['addLabels', ['os:mobile', 'mobile']]]) - expect(methods(issue)).toEqual(['get', 'addLabels', 'get', 'get', 'get']) -}) it('retries against the fresh form when the body changes during mutation', async () => { const issue = createFakeIssue({ body: MOBILE, labels: ['bug', 'os:Windows'] }) @@ -80,134 +31,3 @@ it('retries against the fresh form when the body changes during mutation', async ['removeLabel', 'mobile'] ]) }) - -it('keeps unrelated labels that appear between its read and its writes', async () => { - const issue = createFakeIssue({ body: DESKTOP, labels: ['bug', 'os:mobile', 'mobile'] }) - issue.onCall = (call) => { - if (call.method !== 'get' && !issue.labels.includes('needs-repro')) { - issue.labels.push('needs-repro') - } - if (call.method === 'removeLabel' && call.args.name === 'mobile') { - issue.labels.push('priority:high') - } - } - await runLabelerScript(issue) - - expect(issue.labels).toEqual(['bug', 'needs-repro', 'os:macos', 'priority:high']) -}) - -it('re-adds a desired managed label that someone removes during the run', async () => { - const issue = createFakeIssue({ body: MOBILE, labels: ['bug', 'os:linux'] }) - let removedOnce = false - issue.onCall = (call) => { - if (call.method === 'removeLabel' && !removedOnce) { - removedOnce = true - issue.labels = issue.labels.filter((label) => label !== 'mobile') - } - } - await runLabelerScript(issue) - - expect(issue.labels).toEqual(['bug', 'os:mobile', 'mobile']) - expect(writes(issue)).toEqual([ - ['addLabels', ['os:mobile', 'mobile']], - ['removeLabel', 'os:linux'], - ['addLabels', ['mobile']] - ]) -}) - -it('tolerates 404 when a managed label was already removed', async () => { - const issue = createFakeIssue({ body: DESKTOP, labels: ['bug', 'os:mobile', 'mobile'] }) - issue.onCall = (call) => { - if (call.method === 'removeLabel' && call.args.name === 'os:mobile') { - issue.labels = issue.labels.filter((label) => label !== 'os:mobile') - } - } - await runLabelerScript(issue) - - expect(issue.labels).toEqual(['bug', 'os:macos']) - expect(methods(issue)).toEqual(['get', 'addLabels', 'removeLabel', 'removeLabel', 'get']) -}) - -it('does not report success when the issue disappears mid-run', async () => { - const issue = createFakeIssue({ body: DESKTOP, labels: ['bug', 'os:mobile'] }) - issue.onCall = (call) => { - if (call.method === 'removeLabel') { - issue.deleted = true - } - } - await expect(runLabelerScript(issue)).rejects.toMatchObject({ status: 404 }) - expect(methods(issue)).toEqual(['get', 'addLabels', 'removeLabel', 'get']) -}) - -it.each([ - ['get', 500, ['get']], - ['addLabels', 404, ['get', 'addLabels']], - ['removeLabel', 403, ['get', 'addLabels', 'removeLabel']] -])('fails the job when %s returns %i', async (method, status, expectedCalls) => { - const issue = createFakeIssue({ body: DESKTOP, labels: ['bug', 'os:mobile', 'mobile'] }) - issue.onCall = (call) => { - if (call.method === method) { - throw apiError(status) - } - } - await expect(runLabelerScript(issue)).rejects.toMatchObject({ status }) - expect(methods(issue)).toEqual(expectedCalls) - expect(issue.labels).toContain('bug') -}) - -it('fails with a rerun hint after three passes of a continuously edited issue', async () => { - const issue = createFakeIssue({ body: MOBILE, labels: ['bug', 'keep'] }) - const bodies = [DESKTOP, LINUX, MOBILE, DESKTOP, LINUX] - issue.onCall = (call) => { - if (call.method === 'addLabels') { - issue.body = bodies.shift() - } - } - await expect(runLabelerScript(issue)).rejects.toMatchObject(failure(/after 3 passes/)) - - expect(issue.calls.filter((call) => call.method === 'addLabels')).toHaveLength(3) - expect(issue.calls.filter((call) => call.method === 'get')).toHaveLength(4) - expect(methods(issue).at(-1)).toBe('get') - expect(issue.labels).toEqual(expect.arrayContaining(['bug', 'keep'])) -}) - -it('succeeds on the third pass when the issue settles', async () => { - const issue = createFakeIssue({ body: MOBILE, labels: ['bug'] }) - const bodies = [DESKTOP, LINUX] - issue.onCall = (call) => { - if (call.method === 'addLabels' && bodies.length > 0) { - issue.body = bodies.shift() - } - } - await runLabelerScript(issue) - expect(issue.labels).toEqual(['bug', 'os:linux']) -}) - -it('stops and fails when the body stops being a bug form after a write', async () => { - const issue = createFakeIssue({ body: DESKTOP, labels: ['bug', 'os:mobile', 'mobile'] }) - issue.onCall = (call) => { - if (call.method === 'removeLabel' && call.args.name === 'os:mobile') { - issue.body = 'Rewritten as free text.' - } - } - await expect(runLabelerScript(issue)).rejects.toMatchObject( - failure(/stopped matching the bug form/) - ) - - // Writes already issued in that pass complete; nothing further is attempted or rolled back. - expect(methods(issue)).toEqual(['get', 'addLabels', 'removeLabel', 'removeLabel', 'get']) - expect(issue.labels).toEqual(['bug', 'os:macos']) -}) - -it('treats an unrecognized body as a successful no-op before any write', async () => { - const issue = createFakeIssue({ - body: 'Just a question.', - labels: ['bug', 'os:mobile', 'mobile'] - }) - await runLabelerScript(issue, { - payloadIssue: { number: ISSUE_NUMBER, body: DESKTOP, labels: [] } - }) - - expect(methods(issue)).toEqual(['get']) - expect(issue.labels).toEqual(['bug', 'os:mobile', 'mobile']) -}) diff --git a/config/scripts/ci/workflow-contracts/issue-os-labeler-translation-note.test.mjs b/config/scripts/ci/workflow-contracts/issue-os-labeler-translation-note.test.mjs deleted file mode 100644 index e9a277d7432..00000000000 --- a/config/scripts/ci/workflow-contracts/issue-os-labeler-translation-note.test.mjs +++ /dev/null @@ -1,74 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { MANAGED_LABELS, labelIssue, renderSections } from './issue-os-labeler-script-runner.mjs' - -const OS = 'Operating system' -const WHERE = 'Where were you using Orca?' -const UNRELATED = ['bug', 'translated'] - -const note = (date = '2026-11-08') => - `> 🌐 **Translation note (machine-translated by an assistant on ${date}):** This issue's title was originally written in Chinese and has been translated to English (the body is unchanged). The original, untranslated text is preserved intact in the collapsible section below.` -const NOTE = note() - -// Shape of stablyai/orca#39697 with user content replaced: note, active form, then an archived -// copy of the original form (still saying Windows) inside Details. -function translatedIssue(activeOs) { - const form = (os, details) => - renderSections([ - [OS, os], - ['Orca version', 'v1.4.2.232'], - ['Details', details] - ]) - const archive = [ - '
Original text in Chinese — title and body, as first posted (before translation)', - '', - '**Original body:**', - '', - form('Windows', 'screenshot'), - '', - '
' - ].join('\n') - return `${NOTE}\n\n${form(activeOs, `screenshot\n\n---\n\n${archive}`)}` -} - -async function expectUntouched(body) { - for (const managed of [[], MANAGED_LABELS]) { - const labels = [...UNRELATED, ...managed] - expect(await labelIssue(body, labels)).toEqual({ labels, writes: [] }) - } -} - -describe('translation note before a bug form', () => { - it.each([ - ['LF', (body) => body], - ['CRLF', (body) => body.replace(/\n/g, '\r\n')] - ])('%s: labels follow the active form, not the archived original', async (_name, vary) => { - expect(await labelIssue(vary(translatedIssue('macOS')), [...UNRELATED, 'os:Windows'])).toEqual({ - labels: [...UNRELATED, 'os:macos'], - writes: [ - ['addLabels', ['os:macos']], - ['removeLabel', 'os:Windows'] - ] - }) - }) -}) - -describe('anything else before a bug form is rejected with zero writes', () => { - const form = renderSections([ - [WHERE, 'Mobile app'], - [OS, 'Linux'], - ['Remote host operating system', ''], - ['Orca version', ''], - ['Details', 'x'] - ]) - - it.each([ - ['arbitrary prose', `Translated by a bot.\n\n${form}`], - ['note with different wording', `${NOTE.replace('Chinese', 'Japanese')}\n\n${form}`], - ['heading injected in the date', `${note(`2026-11-08\n### ${OS}\n\nmacOS\n`)}\n\n${form}`], - ['extra sentence after the note', `${NOTE} Also relabel this.\n\n${form}`], - ['repeated note', `${NOTE}\n\n${NOTE}\n\n${form}`], - ['prose between note and form', `${NOTE}\n\nSee below.\n\n${form}`] - ])('%s', async (_name, body) => { - await expectUntouched(body) - }) -}) From 6484b7a2113b050c13f8d982331d5c9adb082129 Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 04:30:59 +0000 Subject: [PATCH 007/178] feat(workspace-cleanup): remove finished automation-run and released worker workspaces when their rules are on (#41808) --- .../cleanup/workspace-cleanup-run-evidence.ts | 14 +++++++++++--- .../workspace-cleanup-evaluation.test.ts | 18 ++++++++++++++++++ .../workspace-cleanup-evaluation.ts | 13 ++++++++++++- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/main/workspaces/cleanup/workspace-cleanup-run-evidence.ts b/src/main/workspaces/cleanup/workspace-cleanup-run-evidence.ts index d695275848d..bdcc8d4fd8c 100644 --- a/src/main/workspaces/cleanup/workspace-cleanup-run-evidence.ts +++ b/src/main/workspaces/cleanup/workspace-cleanup-run-evidence.ts @@ -77,7 +77,13 @@ function needsGitEvidence( if (summary.isMainWorktree || summary.isPinned) { return false } - if (rules.merged.enabled || rules.unchanged.enabled) { + // These reasons come only from the scan's Git and run/worker evidence. + if ( + rules.merged.enabled || + rules.unchanged.enabled || + rules.automationRuns.enabled || + rules.orchestrationWorkers.enabled + ) { return true } // Filesystem activity can only make a workspace newer, so the persisted stamp bounds the inactive rule. @@ -116,6 +122,7 @@ function dirtyEditorWorktreeIds(store: RuntimeStore, runtime: OrcaRuntimeService async function scanTargets( store: RuntimeStore, + runtime: OrcaRuntimeService, worktreeIds: string[], signal: AbortSignal | undefined ): Promise> { @@ -126,7 +133,8 @@ async function scanTargets( const result = await scanWorkspaceCleanup( store, { worktreeIds: batch, refreshActivity: true }, - { signal } + // Without the orchestration store the scan can't prove a worker was released. + { signal, getOrchestrationDb: () => runtime.getExistingOrchestrationDb() } ) for (const candidate of result.candidates) { // A same-id repo row on another host scans too; only this host's checkout is evidence here. @@ -195,7 +203,7 @@ export async function collectWorkspaceCleanupWorkspaces(args: { return needsGitEvidence(summary, rules, now) }) .map((summary) => summary.worktreeId) - const candidates = await scanTargets(store, gitTargets, signal) + const candidates = await scanTargets(store, runtime, gitTargets, signal) const dirtyEditors = dirtyEditorWorktreeIds(store, runtime) const scanned = new Set(gitTargets) return summaries.map((summary) => { diff --git a/src/shared/workspace-cleanup/workspace-cleanup-evaluation.test.ts b/src/shared/workspace-cleanup/workspace-cleanup-evaluation.test.ts index 220c3dbb1f0..d0dab784ea8 100644 --- a/src/shared/workspace-cleanup/workspace-cleanup-evaluation.test.ts +++ b/src/shared/workspace-cleanup/workspace-cleanup-evaluation.test.ts @@ -56,6 +56,24 @@ describe('evaluateWorkspaceCleanup', () => { ).toEqual({ action: 'keep', cause: 'terminal-running', reason: 'merged' }) }) + it('removes finished automation runs and released workers only when their rule is on', () => { + const run = workspace({ reasons: ['automation-run'] }) + expect(evaluateWorkspaceCleanup(run, MERGED_ON, NOW)).toEqual({ + action: 'keep', + cause: 'no-rule-matched' + }) + expect( + evaluateWorkspaceCleanup(run, policy({ automationRuns: { enabled: true } }), NOW) + ).toEqual({ action: 'delete', reason: 'automation-run' }) + expect( + evaluateWorkspaceCleanup( + workspace({ reasons: ['orchestration-worker'], blockers: ['unpushed-commits'] }), + policy({ orchestrationWorkers: { enabled: true } }), + NOW + ) + ).toEqual({ action: 'keep', cause: 'unpushed-commits', reason: 'orchestration-worker' }) + }) + it('keeps a merged workspace whose git status was never read', () => { const unread = workspace({ reasons: ['merged'], diff --git a/src/shared/workspace-cleanup/workspace-cleanup-evaluation.ts b/src/shared/workspace-cleanup/workspace-cleanup-evaluation.ts index 8cf37d0138c..0d1dfefc5d2 100644 --- a/src/shared/workspace-cleanup/workspace-cleanup-evaluation.ts +++ b/src/shared/workspace-cleanup/workspace-cleanup-evaluation.ts @@ -9,7 +9,12 @@ const DAY_MS = 24 * 60 * 60 * 1000 export const WORKSPACE_CLEANUP_RULE_GRACE_MS = 60 * 60 * 1000 /** The rule that would remove a workspace. */ -export type WorkspaceCleanupRuleReason = 'merged' | 'unchanged' | 'inactive' +export type WorkspaceCleanupRuleReason = + | 'merged' + | 'unchanged' + | 'inactive' + | 'automation-run' + | 'orchestration-worker' /** Why the runner leaves a workspace alone; one cause per workspace, the first that applies. */ export type WorkspaceCleanupKeepCause = @@ -93,6 +98,12 @@ function matchRules( if (rules.merged.enabled && input.reasons.includes('merged')) { matches.push({ reason: 'merged' }) } + if (rules.automationRuns.enabled && input.reasons.includes('automation-run')) { + matches.push({ reason: 'automation-run' }) + } + if (rules.orchestrationWorkers.enabled && input.reasons.includes('orchestration-worker')) { + matches.push({ reason: 'orchestration-worker' }) + } if (rules.unchanged.enabled && input.reasons.includes('unchanged')) { matches.push(timeRuleMatch('unchanged', rules.unchanged.afterDays, elapsedMs)) } From f5ab621250512e7d58b0930e6777a9bf54273e9a Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 20:31:27 -0800 Subject: [PATCH 008/178] test: remove low-value mobile and source control suites (#41811) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../use-mobile-file-tap-handlers.test.tsx | 35 ---- .../session-pane-return-grant.test.ts | 27 --- .../launched-agent-opener.test.ts | 180 ------------------ .../tabs/opened-mobile-session-tab.test.ts | 18 -- .../connection/client-authority-epoch.test.ts | 14 -- .../repositories/canonical-repo-key.test.ts | 47 ----- .../pr-refresh-budget-fairness.test.ts | 73 ------- .../merged-review-workspace-notice.test.tsx | 82 -------- .../settings-search-results.test.ts | 53 ------ .../merged-review-workspace-readiness.test.ts | 73 ------- 10 files changed, 602 deletions(-) delete mode 100644 mobile/src/mobile-web-shell/session-pane-return-grant.test.ts delete mode 100644 mobile/src/session/tabs/opened-mobile-session-tab.test.ts delete mode 100644 src/main/git/repositories/canonical-repo-key.test.ts delete mode 100644 src/main/github/pull-requests/refresh/pr-refresh-budget-fairness.test.ts delete mode 100644 src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.test.tsx delete mode 100644 src/renderer/src/components/settings/navigation/settings-search-results.test.ts delete mode 100644 src/shared/source-control/decisions/merged-review-workspace-readiness.test.ts diff --git a/mobile/src/files/session-documents/use-mobile-file-tap-handlers.test.tsx b/mobile/src/files/session-documents/use-mobile-file-tap-handlers.test.tsx index 094289cb6e5..771c4dc74ee 100644 --- a/mobile/src/files/session-documents/use-mobile-file-tap-handlers.test.tsx +++ b/mobile/src/files/session-documents/use-mobile-file-tap-handlers.test.tsx @@ -96,31 +96,6 @@ describe('useMobileFileTapHandlers workspace-root reply', () => { push.mockClear() }) - it('opens the linked workspace on the tapped host', async () => { - const chat = mountChat() - chat.tap() - await chat.reply() - - expect(push.mock.calls).toEqual([ - [ - { - pathname: '/h/[hostId]/session/[worktreeId]', - params: { hostId: 'host-a', worktreeId: 'wt-2' } - } - ] - ]) - }) - - it('drops the reply after the client is replaced on the same host', async () => { - const chat = mountChat() - chat.tap() - chat.render(chat.createClient(), 'host-a') - await chat.reply() - - expect(push).not.toHaveBeenCalled() - expect(chat.reportChatTapFailure).not.toHaveBeenCalled() - }) - it('drops the reply after the host changes away and back', async () => { const chat = mountChat() chat.tap() @@ -131,14 +106,4 @@ describe('useMobileFileTapHandlers workspace-root reply', () => { expect(push).not.toHaveBeenCalled() expect(chat.reportChatTapFailure).not.toHaveBeenCalled() }) - - it('drops the reply after the client generation moves with no render', async () => { - const chat = mountChat() - chat.tap() - chat.moveGeneration() - await chat.reply() - - expect(push).not.toHaveBeenCalled() - expect(chat.reportChatTapFailure).not.toHaveBeenCalled() - }) }) diff --git a/mobile/src/mobile-web-shell/session-pane-return-grant.test.ts b/mobile/src/mobile-web-shell/session-pane-return-grant.test.ts deleted file mode 100644 index 6c4522ed680..00000000000 --- a/mobile/src/mobile-web-shell/session-pane-return-grant.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { createFakeBridgePortPair } from './bridge/bridge-port-pair-test-harness' -import { routeViewOf } from './page-route-policy' - -describe('the session pane return grant', () => { - it('reaches a page whose route declares only navigate', async () => { - const routes = [ - { pathname: '/h/[hostId]/session/[worktreeId]', grants: ['navigate', 'haptics'] }, - { pathname: '/h/[hostId]/source-control/[worktreeId]', grants: ['navigate'] } - ] - const pathname = '/h/host-a/source-control/wt-a' - const view = routeViewOf(routes, pathname) - const pair = createFakeBridgePortPair({ - route: { pathname }, - pageRoutes: view.pageRoutes, - pageRouteGrants: view.pageRouteGrants, - routeGrants: view.routeGrants - }) - await pair.flush() - - const granted = pair.client.getShellSession()?.grants.native - expect(granted).toContain('sessionPaneReturn') - expect(granted).toContain('navigate') - // The route did not declare it, so the harness default of every shell grant is not in play. - expect(granted).not.toContain('haptics') - }) -}) diff --git a/mobile/src/session/agent-launch/launched-agent-opener.test.ts b/mobile/src/session/agent-launch/launched-agent-opener.test.ts index 8a3c6eb09e1..814652315b2 100644 --- a/mobile/src/session/agent-launch/launched-agent-opener.test.ts +++ b/mobile/src/session/agent-launch/launched-agent-opener.test.ts @@ -2,8 +2,6 @@ import { describe, expect, it, vi } from 'vitest' vi.mock('expo-router', () => ({})) -import { getStateFromPath } from 'expo-router/build/fork/getStateFromPath' -import { getMockConfig } from 'expo-router/build/testing-library/mock-config' import { planLaunchedAgentOpen } from './launched-agent-opener' const SESSION = '[hostId]/session/[worktreeId]' @@ -16,155 +14,7 @@ const sessionEntry = (worktreeId: string) => ({ }) const sourceControl = { name: SOURCE_CONTROL, params: { hostId: 'host-a', worktreeId: 'wt-a' } } -// The entry Expo Router's own parser leaves on the stack for an href. -function parsedEntry(href: string): { name: string; params?: object } { - const config = getMockConfig([ - '_layout.tsx', - 'h/_layout.tsx', - 'h/[hostId]/session/[worktreeId].tsx' - ]) - let entry = getStateFromPath(href, config)?.routes.at(-1) - while (entry?.state) { - entry = entry.state.routes.at(-1) - } - if (!entry) { - throw new Error(`no route for ${href}`) - } - return entry -} - describe('planLaunchedAgentOpen', () => { - it('keeps a workspace whose id is another’s percent-encoding apart from it', () => { - const args = { current: 1, paneKey, shellReturns: true } - expect( - planLaunchedAgentOpen({ - ...args, - stack: [sessionEntry('a%3Fb'), sourceControl], - session: { ...session, worktreeId: 'a?b' } - }) - ).toEqual({ - kind: 'shell', - href: '/h/host-a/session/a%3Fb?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - expect( - planLaunchedAgentOpen({ - ...args, - stack: [sessionEntry('a?b'), sourceControl], - session: { ...session, worktreeId: 'a%3Fb' } - }) - ).toEqual({ - kind: 'shell', - href: '/h/host-a/session/a%253Fb?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - expect( - planLaunchedAgentOpen({ - ...args, - stack: [{ ...sessionEntry('wt-a'), params: { hostId: 'h%3Fa', worktreeId: 'wt-a' } }], - current: 0, - session: { ...session, hostId: 'h?a' } - }) - ).toEqual({ - kind: 'shell', - href: '/h/h%3Fa/session/wt-a?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - }) - - it('returns to a percent-literal workspace through the entry Expo Router parsed for it', () => { - const args = { current: 1, paneKey, shellReturns: true } - const literal = parsedEntry('/h/host-a/session/a%253Fb?name=Workspace+A') - expect(literal.params).toEqual({ hostId: 'host-a', worktreeId: 'a%3Fb', name: 'Workspace A' }) - expect( - planLaunchedAgentOpen({ - ...args, - stack: [literal, sourceControl], - session: { ...session, worktreeId: 'a%3Fb' } - }) - ).toEqual({ - kind: 'dismiss-to', - href: '/h/host-a/session/a%253Fb?name=Workspace+A&paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - expect( - planLaunchedAgentOpen({ - ...args, - stack: [literal, sourceControl], - session: { ...session, worktreeId: 'a?b' } - }) - ).toEqual({ - kind: 'shell', - href: '/h/host-a/session/a%3Fb?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - expect( - planLaunchedAgentOpen({ - ...args, - stack: [parsedEntry('/h/host-a/session/a%3Fb'), sourceControl], - session: { ...session, worktreeId: 'a?b' } - }) - ).toEqual({ - kind: 'dismiss-to', - href: '/h/host-a/session/a%3Fb?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - }) - - it('returns to the session beneath this screen with its own params and the pane', () => { - expect( - planLaunchedAgentOpen({ - stack: [sessionEntry('wt-a'), sourceControl], - current: 1, - session, - paneKey, - shellReturns: true - }) - ).toEqual({ - kind: 'dismiss-to', - href: '/h/host-a/session/wt-a?name=Workspace+A&paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - }) - - it('sets the pane in place when the session is the current screen', () => { - expect( - planLaunchedAgentOpen({ - stack: [sessionEntry('wt-a')], - current: 0, - session, - paneKey, - shellReturns: false - }) - ).toEqual({ kind: 'set-params' }) - }) - - it('leaves another workspace’s session beneath it alone and asks the shell', () => { - expect( - planLaunchedAgentOpen({ - stack: [sessionEntry('wt-b'), sourceControl], - current: 1, - session, - paneKey, - shellReturns: true - }) - ).toEqual({ - kind: 'shell', - href: '/h/host-a/session/wt-a?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - }) - - it('treats the same workspace on another execution host as a different session', () => { - const remote = { - name: SESSION, - params: { hostId: 'host-a', worktreeId: 'wt-a', executionHost: 'ssh:box' } - } - const args = { stack: [remote, sourceControl], current: 1, paneKey, shellReturns: true } - expect(planLaunchedAgentOpen({ ...args, session })).toEqual({ - kind: 'shell', - href: '/h/host-a/session/wt-a?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - expect( - planLaunchedAgentOpen({ ...args, session: { ...session, executionHost: 'ssh:box' } }) - ).toEqual({ - kind: 'dismiss-to', - href: '/h/host-a/session/wt-a?executionHost=ssh%3Abox&paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - }) - it('returns only to the nearest session entry at or beneath this screen', () => { const shell = { kind: 'shell', @@ -191,34 +41,4 @@ describe('planLaunchedAgentOpen', () => { }) ).toEqual(shell) }) - - it('asks a shell that returns to the session, and offers nothing on one that would push', () => { - const args = { stack: [sourceControl], current: 0, session, paneKey } - expect( - planLaunchedAgentOpen({ - ...args, - session: { ...session, executionHost: 'ssh:box' as const }, - shellReturns: true - }) - ).toEqual({ - kind: 'shell', - href: '/h/host-a/session/wt-a?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111&executionHost=ssh%3Abox' - }) - expect(planLaunchedAgentOpen({ ...args, shellReturns: false })).toEqual({ kind: 'none' }) - }) - - it('pushes the session where there is no shell and no entry', () => { - expect( - planLaunchedAgentOpen({ - stack: [sourceControl], - current: 0, - session, - paneKey, - shellReturns: null - }) - ).toEqual({ - kind: 'push', - href: '/h/host-a/session/wt-a?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - }) - }) }) diff --git a/mobile/src/session/tabs/opened-mobile-session-tab.test.ts b/mobile/src/session/tabs/opened-mobile-session-tab.test.ts deleted file mode 100644 index 82566666b2d..00000000000 --- a/mobile/src/session/tabs/opened-mobile-session-tab.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { expect, it } from 'vitest' -import { shouldActivateOpenedMobileSessionTab } from './opened-mobile-session-tab' - -it('refuses a completion whose host authority is no longer current', () => { - const state = { - activated: false, - activationSeq: 1, - latestActivationSeq: 1, - sourceTerminalHandle: 'terminal-1', - activeTerminalHandle: 'terminal-1', - activeTabType: 'terminal' - } - - expect(shouldActivateOpenedMobileSessionTab(state)).toBe(true) - expect(shouldActivateOpenedMobileSessionTab({ ...state, sourceAuthorityCurrent: false })).toBe( - false - ) -}) diff --git a/mobile/src/transport/connection/client-authority-epoch.test.ts b/mobile/src/transport/connection/client-authority-epoch.test.ts index 4d68231d091..90693582755 100644 --- a/mobile/src/transport/connection/client-authority-epoch.test.ts +++ b/mobile/src/transport/connection/client-authority-epoch.test.ts @@ -2,20 +2,6 @@ import { describe, expect, it } from 'vitest' import { createClientAuthorityEpoch } from './client-authority-epoch' describe('createClientAuthorityEpoch', () => { - it('keeps a token stale after the host changes away and back with the same client', () => { - const epoch = createClientAuthorityEpoch() - const client = { getGeneration: () => 1 } - const hostA = { client, hostId: 'host-a', executionHost: undefined, worktreeId: 'wt-1' } - epoch.update(hostA) - const token = epoch.token() - expect(epoch.isCurrent(token)).toBe(true) - - epoch.update({ ...hostA, hostId: 'host-b' }) - epoch.update(hostA) - - expect(epoch.isCurrent(token)).toBe(false) - }) - it('expires a token when the client generation moves without a new update', () => { const epoch = createClientAuthorityEpoch() let generation = 1 diff --git a/src/main/git/repositories/canonical-repo-key.test.ts b/src/main/git/repositories/canonical-repo-key.test.ts deleted file mode 100644 index 13c93afe8e0..00000000000 --- a/src/main/git/repositories/canonical-repo-key.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type * as GitExecFile from '../command-runner/git-exec-file' - -const gitExecFileAsyncMock = vi.hoisted(() => vi.fn()) - -vi.mock('../command-runner/git-exec-file', async (importOriginal) => ({ - ...(await importOriginal()), - gitExecFileAsync: gitExecFileAsyncMock -})) - -import { - _resetCanonicalRepoKeyCacheForTests, - getCanonicalRepoKey, - peekGitCommonDir -} from './canonical-repo-key' - -beforeEach(() => { - vi.spyOn(process, 'platform', 'get').mockReturnValue('darwin') - _resetCanonicalRepoKeyCacheForTests() - gitExecFileAsyncMock.mockReset() -}) - -afterEach(() => { - vi.restoreAllMocks() -}) - -describe('getCanonicalRepoKey', () => { - it('exposes only verified common-directory answers to diagnostics', async () => { - gitExecFileAsyncMock.mockResolvedValue({ stdout: '/repo/.git\n', stderr: '' }) - - expect(peekGitCommonDir('/repo')).toBeUndefined() - expect(await getCanonicalRepoKey('/repo')).toBe('local::/repo/.git') - expect(peekGitCommonDir('/repo')).toBe('/repo/.git') - gitExecFileAsyncMock.mockRejectedValue(new Error('not a repository')) - expect(await getCanonicalRepoKey('/missing')).toBe('local::/missing') - expect(peekGitCommonDir('/missing')).toBeUndefined() - }) - - it('leaves ambiguous Windows drive identities unknown while retaining UNC provenance', async () => { - vi.spyOn(process, 'platform', 'get').mockReturnValue('win32') - gitExecFileAsyncMock.mockResolvedValue({ stdout: '/repo/.git\n', stderr: '' }) - await getCanonicalRepoKey('C:/repo') - expect(peekGitCommonDir('C:/repo')).toBeUndefined() - await getCanonicalRepoKey('//wsl$/Ubuntu/home/dev/repo') - expect(peekGitCommonDir('//wsl$/Ubuntu/home/dev/repo')).toBe('/repo/.git') - }) -}) diff --git a/src/main/github/pull-requests/refresh/pr-refresh-budget-fairness.test.ts b/src/main/github/pull-requests/refresh/pr-refresh-budget-fairness.test.ts deleted file mode 100644 index 3f3880ab775..00000000000 --- a/src/main/github/pull-requests/refresh/pr-refresh-budget-fairness.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { expect, it } from 'vitest' -import { PRWatchJobRunner } from './pr-refresh-watch-jobs' -import { PRRefreshRetryState } from './pr-refresh-retry-state' -import { PRRefreshPacing } from './pr-refresh-pacing' -import { PRRefreshQueue, type PRRefreshQueueEntry } from './pr-refresh-queue' -import { makeCandidate } from './pr-refresh-coordinator-test-harness' - -it('yields a selected list read to another ready review before restoring its priority', () => { - const pacing = new PRRefreshPacing() - const queue = new PRRefreshQueue(() => {}) - const entry = (key: string, selected: boolean, priority: number): PRRefreshQueueEntry => ({ - key, - candidate: makeCandidate({ cacheKey: key, isSelected: selected }), - aliases: new Map(), - reason: 'visible', - priority, - dueAt: 0, - queuedAt: 0 - }) - const selected = entry('selected', true, 80) - const other = entry('other', false, 40) - queue.set(selected.key, selected) - queue.set(other.key, other) - const next = () => queue.ordered((a, b) => pacing.order(a, b))[0].key - expect(next()).toBe('selected') - pacing.noteBackgroundStart(selected) - expect(next()).toBe('other') - pacing.noteBackgroundStart(other) - expect(next()).toBe('selected') - pacing.noteBackgroundStart(selected) - queue.delete(other.key) - expect(next()).toBe('selected') -}) - -it('preserves a visible list turn when selected reads share the budget with aged watches', () => { - const pacing = new PRRefreshPacing() - const queue = new PRRefreshQueue(() => {}) - const watches = new PRWatchJobRunner(queue, new PRRefreshRetryState(), { schedule() {} }) - const entry = (key: string, selected: boolean): PRRefreshQueueEntry => ({ - key, - candidate: makeCandidate({ isSelected: selected }), - aliases: new Map(), - reason: 'visible', - priority: selected ? 80 : 40, - dueAt: 0, - queuedAt: 0 - }) - const selected = entry('selected', true) - const other = entry('other', false) - const watch: PRRefreshQueueEntry = { - ...entry('watch', false), - reason: 'watch', - watch: { prKey: 'pr' } - } - for (const item of [selected, other, watch]) { - queue.set(item.key, item) - } - const next = () => { - watches.age(queue.values(), 120_000) - return queue.ordered((a, b) => pacing.order(a, b))[0].key - } - const started = (item: PRRefreshQueueEntry) => { - pacing.noteBackgroundStart(item) - watches.noteBudgetedStart(item) - } - expect(next()).toBe('selected') - started(selected) - expect(next()).toBe('watch') - started(watch) - expect(next()).toBe('selected') - started(selected) - expect(next()).toBe('other') -}) diff --git a/src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.test.tsx b/src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.test.tsx deleted file mode 100644 index 84de080ec37..00000000000 --- a/src/renderer/src/components/right-sidebar/source-control/panel/merged-review-workspace-notice.test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -// @vitest-environment happy-dom -import { act } from 'react' -import { createRoot } from 'react-dom/client' -import { expect, it, vi } from 'vitest' -import { mkdtemp, rm, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import path from 'node:path' -import { runProcess } from '@orca/process-host' - -const mocks = vi.hoisted(() => ({ runWorktreeDelete: vi.fn() })) -vi.mock('../../../sidebar/workspace-actions/deletion/delete-worktree-flow', () => mocks) -vi.mock('@/store/index', () => ({ - useAppStore: (selector: (state: { deleteStateByWorktreeId: object }) => unknown) => - selector({ deleteStateByWorktreeId: {} }) -})) - -import { SourceControlMergedReviewNotice } from './merged-review-workspace-notice' -import { makeWorktree } from '../../../worktree-jump/worktree-jump-palette-test-fixtures' - -it('requires the ignored-file confirmation for an ignored-only merged checkout', async () => { - const directory = await mkdtemp(path.join(tmpdir(), 'merged-notice-')) - const container = document.createElement('div') - const root = createRoot(container) - const git = async (args: string[]) => { - const result = await runProcess({ program: 'git', args, cwd: directory }) - if (result.code !== 0) { - throw new Error(result.stderr) - } - return result.stdout.trim() - } - try { - await git(['init', '--quiet']) - await writeFile(path.join(directory, '.gitignore'), '.env\n') - await git(['add', '.gitignore']) - await git([ - '-c', - 'user.name=Test', - '-c', - 'user.email=test@example.com', - 'commit', - '-m', - 'initial' - ]) - await writeFile(path.join(directory, '.env'), 'LOCAL_ONLY=value\n') - const head = await git(['rev-parse', 'HEAD']) - const status = await git(['status', '--porcelain']) - expect(status).toBe('') - const worktree = makeWorktree('ignored-only', 'Ignored only', { - path: directory, - instanceId: 'original' - }) - await act(async () => - root.render( - - ) - ) - await act(async () => container.querySelector('button')?.click()) - expect(mocks.runWorktreeDelete).toHaveBeenCalledWith('ignored-only', { - expectedHostId: undefined, - expectedInstanceId: 'original', - forceConfirm: true - }) - } finally { - await act(async () => root.unmount()) - await rm(directory, { recursive: true, force: true }) - } -}) diff --git a/src/renderer/src/components/settings/navigation/settings-search-results.test.ts b/src/renderer/src/components/settings/navigation/settings-search-results.test.ts deleted file mode 100644 index 9ae65679bad..00000000000 --- a/src/renderer/src/components/settings/navigation/settings-search-results.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Settings } from 'lucide-react' -import { expect, it } from 'vitest' -import type { SettingsNavSection } from './settings-navigation-types' -import { - buildSettingsSearchResults, - getSettingsSearchTitleMatchRanges, - rankSettingsSearchResults -} from './settings-search-results' - -function section( - id: string, - title: string, - searchEntries: SettingsNavSection['searchEntries'] -): SettingsNavSection { - return { id, title, description: '', icon: Settings, group: 'interface', searchEntries } -} - -it('ranks individual settings across panes and targets each one', () => { - const results = buildSettingsSearchResults( - [ - section('general', 'General', [ - { title: 'Auto update', keywords: ['release'] }, - { title: 'Auto update' }, - { title: 'Theme', keywords: ['update'] } - ]), - section('updates', 'Updates', [{ title: 'Channel', targetSectionId: 'updates-channel' }]), - section('repo-1', 'orca', [{ title: 'Update hooks' }]) - ], - (_section, entry) => (entry.title === 'Theme' ? 'general-theme' : undefined) - ) - - expect( - rankSettingsSearchResults('update', results).map((result) => [ - result.section.id, - result.entry.title, - result.targetId - ]) - ).toEqual([ - ['updates', 'Updates', 'updates'], - ['repo-1', 'Update hooks', 'settings-title:Update hooks'], - ['general', 'Auto update', 'settings-title:Auto update'], - ['general', 'Theme', 'general-theme'] - ]) - expect(rankSettingsSearchResults('channel', results)[0]?.targetId).toBe('updates-channel') - expect(rankSettingsSearchResults(' ', results)).toEqual([]) -}) - -it('marks the same text the ranking fold matched, including dotted capital I', () => { - expect(getSettingsSearchTitleMatchRanges('İstanbul', 'stan')).toEqual([{ start: 1, end: 5 }]) - expect(getSettingsSearchTitleMatchRanges('İstanbul', 'İst')).toEqual([{ start: 0, end: 3 }]) - expect(getSettingsSearchTitleMatchRanges('Line height', 'İ')).toEqual([{ start: 1, end: 2 }]) - expect(getSettingsSearchTitleMatchRanges('Theme', 'update')).toEqual([]) -}) diff --git a/src/shared/source-control/decisions/merged-review-workspace-readiness.test.ts b/src/shared/source-control/decisions/merged-review-workspace-readiness.test.ts deleted file mode 100644 index e0f4aaac528..00000000000 --- a/src/shared/source-control/decisions/merged-review-workspace-readiness.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { resolveMergedReviewWorkspaceReadiness } from './merged-review-workspace-readiness' - -const merged = { state: 'merged' as const, headSha: 'abc123' } - -describe('resolveMergedReviewWorkspaceReadiness', () => { - it('is safe to delete when HEAD is the merged head and the tree is clean', () => { - expect( - resolveMergedReviewWorkspaceReadiness({ - review: merged, - headOid: 'ABC123', - uncommittedCount: 0 - }) - ).toEqual({ kind: 'safe-to-delete' }) - }) - - it('is safe to delete when the provider confirmed HEAD is in the merged review', () => { - expect( - resolveMergedReviewWorkspaceReadiness({ - review: { ...merged, headSha: 'updated-on-github', confirmedContainedHeadOid: 'local' }, - headOid: 'local', - uncommittedCount: 0 - }) - ).toEqual({ kind: 'safe-to-delete' }) - }) - - it('reports uncommitted changes before HEAD identity', () => { - expect( - resolveMergedReviewWorkspaceReadiness({ - review: merged, - headOid: 'abc123', - uncommittedCount: 3 - }) - ).toEqual({ kind: 'uncommitted-changes', count: 3 }) - }) - - it('leaves a different checkout head unverified without assuming its commits are absent', () => { - expect( - resolveMergedReviewWorkspaceReadiness({ - review: merged, - headOid: 'def456', - uncommittedCount: 0 - }) - ).toEqual({ kind: 'head-unverified' }) - }) - - it('leaves missing checkout heads and missing review proof unverified', () => { - expect( - resolveMergedReviewWorkspaceReadiness({ - review: merged, - headOid: null, - uncommittedCount: 0 - }) - ).toEqual({ kind: 'head-unverified' }) - expect( - resolveMergedReviewWorkspaceReadiness({ - review: { state: 'merged' }, - headOid: 'abc123', - uncommittedCount: 0 - }) - ).toEqual({ kind: 'head-unverified' }) - }) - - it('says nothing for reviews that are not merged', () => { - expect( - resolveMergedReviewWorkspaceReadiness({ - review: { ...merged, state: 'open' }, - headOid: 'abc123', - uncommittedCount: 0 - }) - ).toBeNull() - }) -}) From 038793de64c38a360eff279d584e1784b2a44e7b Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 20:36:16 -0800 Subject: [PATCH 009/178] test: retire narrow runtime and navigation samples (#41813) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../review/pr-ai-triage-launch.test.ts | 87 ----------------- .../observability/git-operation-cause.test.ts | 94 ------------------- .../setup-runner-real-git.test.ts | 57 ----------- ...iff-editor-horizontal-wheel-scroll.test.ts | 87 ----------------- 4 files changed, 325 deletions(-) delete mode 100644 mobile/src/source-control/review/pr-ai-triage-launch.test.ts delete mode 100644 src/main/observability/git-operation-cause.test.ts delete mode 100644 src/main/workspace-hooks/setup-runner-real-git.test.ts diff --git a/mobile/src/source-control/review/pr-ai-triage-launch.test.ts b/mobile/src/source-control/review/pr-ai-triage-launch.test.ts deleted file mode 100644 index e7881b32398..00000000000 --- a/mobile/src/source-control/review/pr-ai-triage-launch.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { AgentLaunchResult } from '../../../../src/shared/agent-launch/contracts/agent-launch-intent' -import { notificationPaneTab } from '../../session/tabs/notification-pane-tab' -import type { MobileSessionTab } from '../../session/tabs/mobile-session-tabs' -import { launchedAgentOpens } from './pr-ai-triage-launch' - -function result(outcome: AgentLaunchResult['outcome']): AgentLaunchResult { - return { - outcome, - worktreeId: 'wt-a', - receipt: { - mode: outcome.kind, - preferred: outcome.kind, - reason: 'requested', - detail: 'Requested.' - } - } -} - -// The id format createStructuredAgentSessionId produces: the agent, then a UUID with underscores. -const SESSION_ID = 'claude_11111111_2222_4333_8444_555555555555' -const LEAF_ID = '11111111-1111-4111-8111-111111111111' - -const chatTab = (id: string, sessionId = SESSION_ID): MobileSessionTab => ({ - type: 'agent-session', - id, - title: 'Chat', - sessionId, - agent: 'claude', - isActive: false -}) -const otherChatTab = chatTab('tab-other', 'claude_99999999_2222_4333_8444_555555555555') - -// The session screen finds the tab to select from the pane key with notificationPaneTab. -describe('launchedAgentOpens', () => { - it('names a chat by the tab the host reported, which the session screen finds', () => { - const opens = launchedAgentOpens( - result({ kind: 'structured', sessionId: SESSION_ID, handle: 'h', tabId: 'tab-9' }) - ) - expect(opens).toEqual({ - worktreeId: 'wt-a', - paneKey: 'tab-9:7d731d20-5488-436f-ad53-d3e49333f320' - }) - expect(notificationPaneTab([otherChatTab, chatTab('tab-9')], opens.paneKey ?? '')?.id).toBe( - 'tab-9' - ) - }) - - it('derives a chat’s tab from its session on a host that reports none', () => { - const opens = launchedAgentOpens( - result({ kind: 'structured', sessionId: SESSION_ID, handle: 'h' }) - ) - expect(opens).toEqual({ - worktreeId: 'wt-a', - paneKey: - 'structured-agent-session-claude_11111111_2222_4333_8444_555555555555:7d731d20-5488-436f-ad53-d3e49333f320' - }) - // The host may publish the chat under another tab id; the session id still finds it. - expect( - notificationPaneTab([otherChatTab, chatTab('host-tab-3')], opens.paneKey ?? '')?.id - ).toBe('host-tab-3') - }) - - it('names a terminal by its pane, or by none when the host reported none', () => { - const paneKey = `tab-1:${LEAF_ID}` - const opens = launchedAgentOpens(result({ kind: 'terminal', handle: 'h', paneKey })) - expect(opens).toEqual({ worktreeId: 'wt-a', paneKey }) - const terminal = (id: string, leafId: string): MobileSessionTab => ({ - type: 'terminal', - id, - title: 'Terminal', - isActive: false, - terminal: null, - leafId - }) - expect( - notificationPaneTab( - [terminal('tab-2', LEAF_ID), terminal('tab-1', LEAF_ID)], - opens.paneKey ?? '' - )?.id - ).toBe('tab-1') - expect(launchedAgentOpens(result({ kind: 'terminal', handle: 'h' }))).toEqual({ - worktreeId: 'wt-a', - paneKey: null - }) - }) -}) diff --git a/src/main/observability/git-operation-cause.test.ts b/src/main/observability/git-operation-cause.test.ts deleted file mode 100644 index 5e83ea570fe..00000000000 --- a/src/main/observability/git-operation-cause.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { afterEach, expect, it } from 'vitest' -import { runWithGitCause } from './git-operation-cause' -import { startGitSpan, withGitSpan } from './instrumentation' -import { setActiveSink } from './tracer' - -afterEach(() => setActiveSink(null)) - -it('keeps concurrent Git causes isolated and retires a settled cause before detached work', async () => { - const records: unknown[] = [] - setActiveSink({ push: (record) => records.push(record), flush() {}, close() {} }) - const firstGate = Promise.withResolvers() - const secondGate = Promise.withResolvers() - const detachedGate = Promise.withResolvers() - let detached = Promise.resolve() - const first = runWithGitCause( - { family: 'catalog', reason: 'desktop-catalog', cache: 'miss' }, - { namespace: 'local', commonDir: '/fixture/common.git' }, - async () => { - detached = detachedGate.promise.then(() => - withGitSpan({ args: ['worktree', 'remove', 'PRIVATE_PATH'] }, async () => {}) - ) - await withGitSpan({ args: ['worktree', 'list', '--porcelain', '-z'] }, async () => { - await firstGate.promise - }) - } - ) - const second = runWithGitCause( - { family: 'fetch', reason: 'conflict-preview', cache: 'refresh' }, - { namespace: 'wsl:fixture', commonDir: '/fixture/common.git' }, - () => withGitSpan({ args: ['fetch', 'PRIVATE_REMOTE'] }, () => secondGate.promise) - ) - secondGate.resolve() - await second - firstGate.resolve() - await first - detachedGate.resolve() - await detached - - expect(records).toEqual([ - expect.objectContaining({ - attributes: expect.objectContaining({ - 'git.reason': 'conflict-preview', - 'git.cache': 'refresh', - 'git.common_dir_hash': '48e43725147afff2' - }) - }), - expect.objectContaining({ - attributes: expect.objectContaining({ - 'git.reason': 'desktop-catalog', - 'git.variant': 'worktree-list-z', - 'git.common_dir_hash': '39be98da86a94d54' - }) - }), - expect.objectContaining({ - attributes: expect.objectContaining({ - 'git.subcommand': 'worktree', - 'git.arg_count': 3, - 'git.variant': 'worktree-remove' - }) - }) - ]) - expect(records[2]).toEqual( - expect.objectContaining({ - attributes: expect.not.objectContaining({ 'git.reason': expect.any(String) }) - }) - ) - expect(JSON.stringify(records)).not.toContain('PRIVATE_') -}) - -it('records fixed failure categories without changing the original Git rejection', async () => { - const records: unknown[] = [] - setActiveSink({ push: (record) => records.push(record), flush() {}, close() {} }) - const error = new Error('PRIVATE_OUTPUT') - await expect( - withGitSpan({ args: ['for-each-ref', '--sort=-committerdate', 'PRIVATE_QUERY'] }, async () => { - throw error - }) - ).rejects.toBe(error) - const timeout = new Error('PRIVATE_TIMEOUT') - timeout.name = 'GitCommandTimeoutError' - startGitSpan({ args: ['fetch', 'PRIVATE_REMOTE'] }).fail(timeout) - const cancelled = new Error('PRIVATE_CANCEL') - cancelled.name = 'AbortError' - startGitSpan({ args: ['status'] }).fail(cancelled) - startGitSpan({ args: ['status'] }).interrupt('PRIVATE_INTERRUPT') - - expect(records).toEqual([ - expect.objectContaining({ exit: { _tag: 'Failure', cause: 'Git command failed' } }), - expect.objectContaining({ exit: { _tag: 'Failure', cause: 'Git command timed out' } }), - expect.objectContaining({ exit: { _tag: 'Failure', cause: 'Git command cancelled' } }), - expect.objectContaining({ exit: { _tag: 'Interrupted', cause: 'Git command cancelled' } }) - ]) - expect(JSON.stringify(records)).not.toContain('PRIVATE_') -}) diff --git a/src/main/workspace-hooks/setup-runner-real-git.test.ts b/src/main/workspace-hooks/setup-runner-real-git.test.ts deleted file mode 100644 index 7a4ce3ef955..00000000000 --- a/src/main/workspace-hooks/setup-runner-real-git.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { runProcess } from '@orca/process-host' -import { afterEach, expect, it } from 'vitest' -import { createSetupRunnerScript } from './worktree-runner-script' -import type { Repo } from '../../shared/repo-types' - -let directory: string | undefined -afterEach(async () => { - if (directory) { - await rm(directory, { recursive: true, force: true }) - directory = undefined - } -}) - -it('writes usable setup scripts for linked worktrees and rejects non-repositories', async () => { - const root = await mkdtemp(join(tmpdir(), 'orca-setup-path-')) - directory = root - const git = async (args: string[]) => { - const result = await runProcess({ program: 'git', args, cwd: root }) - if (result.code !== 0) { - throw new Error(result.stderr) - } - return result.stdout.trim() - } - await git(['init', '-q']) - await git(['config', 'user.name', 'Setup fixture']) - await git(['config', 'user.email', 'setup@example.invalid']) - await git(['config', 'commit.gpgsign', 'false']) - await git(['config', 'core.hooksPath', '']) - await git(['commit', '--allow-empty', '-qm', 'fixture']) - const linked = join(root, 'linked') - await git(['worktree', 'add', '--detach', linked, 'HEAD']) - const repo: Repo = { - id: 'setup-fixture', - displayName: 'fixture', - badgeColor: 'blue', - addedAt: 0, - path: root - } - const result = await createSetupRunnerScript(repo, linked, 'echo SETUP_PATH_OK') - expect(await readFile(result.runnerScriptPath, 'utf8')).toContain('echo SETUP_PATH_OK') - const execution = await runProcess({ - program: process.platform === 'win32' ? 'cmd.exe' : 'bash', - args: - process.platform === 'win32' ? ['/c', result.runnerScriptPath] : [result.runnerScriptPath], - cwd: linked - }) - expect(execution.code).toBe(0) - expect(execution.stdout).toContain('SETUP_PATH_OK') - const nonRepository = join(root, 'non-repository') - await mkdir(nonRepository) - // Stop Git discovery at an owned invalid marker, even when TMPDIR is inside a checkout. - await writeFile(join(nonRepository, '.git'), 'gitdir: missing-fixture-directory\n') - await expect(createSetupRunnerScript(repo, nonRepository, 'echo UNREACHABLE')).rejects.toThrow() -}) diff --git a/src/renderer/src/components/editor/diff/monaco-view/diff-editor-horizontal-wheel-scroll.test.ts b/src/renderer/src/components/editor/diff/monaco-view/diff-editor-horizontal-wheel-scroll.test.ts index b2fe38de405..ffe109b5607 100644 --- a/src/renderer/src/components/editor/diff/monaco-view/diff-editor-horizontal-wheel-scroll.test.ts +++ b/src/renderer/src/components/editor/diff/monaco-view/diff-editor-horizontal-wheel-scroll.test.ts @@ -52,44 +52,6 @@ afterEach(() => { }) describe('installDiffEditorHorizontalWheelScroll', () => { - it('scrolls only the pane under the pointer on a sideways swipe without Shift', () => { - const { original, modified } = installPanes(10) - - const event = dispatchWheel(original.container, { deltaX: 30, deltaY: 4 }) - - expect(original.setScrollLeft).toHaveBeenCalledWith(40) - expect(modified.setScrollLeft).not.toHaveBeenCalled() - expect(event.defaultPrevented).toBe(true) - }) - - it('leaves a mostly vertical swipe untouched for the outer list', () => { - const { original } = installPanes(10) - - const event = dispatchWheel(original.container, { deltaX: 5, deltaY: 40 }) - - expect(original.setScrollLeft).not.toHaveBeenCalled() - expect(original.getScrollLeft()).toBe(10) - expect(event.defaultPrevented).toBe(false) - }) - - it('scrolls sideways from a vertical wheel while Shift is held', () => { - const { original } = installPanes(10) - - const event = dispatchWheel(original.container, { deltaX: 5, deltaY: 40, shiftKey: true }) - - expect(original.getScrollLeft()).toBe(50) - expect(event.defaultPrevented).toBe(true) - }) - - it('hands the gesture to the outer list when the pane is already at its edge', () => { - const { original } = installPanes(800) - - const event = dispatchWheel(original.container, { deltaY: 30, shiftKey: true }) - - expect(original.setScrollLeft).not.toHaveBeenCalled() - expect(event.defaultPrevented).toBe(false) - }) - it('moves right by one pixel once quarter-pixel swipes add up to it', () => { const { original } = installPanes(10) @@ -98,53 +60,4 @@ describe('installDiffEditorHorizontalWheelScroll', () => { expect(original.getScrollLeft()).toBe(11) expect(events.map((event) => event.defaultPrevented)).toEqual([true, true, true, true]) }) - - it('moves left by one pixel once quarter-pixel swipes add up to it', () => { - const { original } = installPanes(10) - - swipe(original.container, -0.25, 4) - - expect(original.getScrollLeft()).toBe(9) - }) - - it('starts over when the swipe changes direction', () => { - const { original } = installPanes(10) - - swipe(original.container, 0.25, 3) - swipe(original.container, -0.25, 3) - expect(original.getScrollLeft()).toBe(10) - - swipe(original.container, -0.25, 1) - expect(original.getScrollLeft()).toBe(9) - }) - - it('drops a partial pixel left over from an earlier gesture', () => { - const { original } = installPanes(10) - - dispatchWheel(original.container, { deltaX: 0.75, timeStamp: 1000 }) - dispatchWheel(original.container, { deltaX: 0.25, timeStamp: 1151 }) - expect(original.getScrollLeft()).toBe(10) - - dispatchWheel(original.container, { deltaX: 0.75, timeStamp: 1301 }) - expect(original.getScrollLeft()).toBe(11) - }) - - it('carries a partial pixel when the pointer crosses to the other pane', () => { - const { original, modified } = installPanes(10) - - swipe(original.container, 0.25, 2) - swipe(modified.container, 0.25, 2) - - expect(modified.getScrollLeft()).toBe(11) - expect(original.getScrollLeft()).toBe(10) - }) - - it('hands partial-pixel swipes at the edge to the outer list', () => { - const { original } = installPanes(800) - - const events = swipe(original.container, 0.25, 4) - - expect(original.getScrollLeft()).toBe(800) - expect(events.map((event) => event.defaultPrevented)).toEqual([false, false, false, false]) - }) }) From 0f80eed95a9c7b093137fd060c33d736d2ac81e4 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 20:39:03 -0800 Subject: [PATCH 010/178] perf(plugins): pause a plugin's automations in one batched write (#41787) * perf(plugins): pause a plugin's automations in one batched write * perf(plugins): limit the automation batch path to pause fields; test batch/single parity --- .../persistence-automations.test.ts | 77 ++++++ .../automation-enablement-transitions.ts | 17 ++ .../automation-persistence.ts | 13 + .../automation-definition-operations.ts | 173 +------------ .../automation-definition-update.ts | 245 ++++++++++++++++++ .../plugin-automation-ownership.test.ts | 51 +++- .../plugins/plugin-automation-ownership.ts | 33 ++- .../construction/runtime-store-contract.ts | 1 + .../runtime-automation-batch-update.ts | 39 +++ .../runtime-automation-controller.ts | 9 + .../automation-api-operations.ts | 32 +++ 11 files changed, 504 insertions(+), 186 deletions(-) create mode 100644 src/main/persistence/scheduling-automations/automation-definition-update.ts create mode 100644 src/main/runtime/integrations/automations/runtime-automation-batch-update.ts diff --git a/src/main/persistence/applying-settings/persistence-automations.test.ts b/src/main/persistence/applying-settings/persistence-automations.test.ts index 4831c74f568..c112bceef4c 100644 --- a/src/main/persistence/applying-settings/persistence-automations.test.ts +++ b/src/main/persistence/applying-settings/persistence-automations.test.ts @@ -120,6 +120,83 @@ describe('Store', () => { expect(store.updateAutomation(existing.id, { workspaceId: undefined }).workspaceId).toBe('wt1') }) + it('applies a batch of updates, reporting a missing row without blocking the rest', async () => { + const store = await createStore() + store.addRepo(makeRepo()) + const create = (name: string) => + store.createAutomation({ + name, + prompt: 'Run checks', + agentId: 'claude', + projectId: 'r1', + workspaceMode: 'new_per_run', + timezone: 'UTC', + rrule: 'FREQ=DAILY;BYHOUR=9;BYMINUTE=0', + dtstart: new Date('2026-05-13T00:00:00Z').getTime() + }) + const [a, b, c] = [create('A'), create('B'), create('C')] + + const results = store.updateAutomations([ + { id: a.id, updates: { enabled: false } }, + { id: 'missing', updates: { enabled: false } }, + { id: b.id, updates: { enabled: false } } + ]) + + expect(results.map((result) => ('automation' in result ? 'ok' : 'error'))).toEqual([ + 'ok', + 'error', + 'ok' + ]) + expect(store.listAutomations().map(({ name, enabled }) => [name, enabled])).toEqual([ + ['A', false], + ['B', false], + ['C', true] + ]) + expect(store.automationEnablementRevision(a.id)).toBe(1) + expect(store.automationEnablementRevision(c.id)).toBe(0) + }) + + it('writes the same record from a batch as from a single update', async () => { + vi.useFakeTimers({ now: new Date('2026-06-01T00:00:00Z'), toFake: ['Date'] }) + try { + const store = await createStore() + store.addRepo(makeRepo()) + const createdBy = { kind: 'plugin', pluginKey: 'p', name: 'P' } as const + const create = () => + store.createAutomation({ + name: 'Same', + prompt: 'Run checks', + agentId: 'claude', + projectId: 'r1', + workspaceMode: 'new_per_run', + timezone: 'UTC', + rrule: 'FREQ=DAILY;BYHOUR=9;BYMINUTE=0', + dtstart: new Date('2026-05-13T00:00:00Z').getTime(), + createdBy + }) + const [single, batched] = [create(), create()] + vi.setSystemTime(new Date('2026-06-02T00:00:00Z')) + const updates = { enabled: false, createdBy: { ...createdBy, endedAt: 42 } } + + const fromSingle = store.updateAutomation(single.id, updates) + const [fromBatch] = store.updateAutomations([{ id: batched.id, updates }]) + + const { id: _singleId, ...expected } = fromSingle + expect('automation' in fromBatch && fromBatch.automation).toEqual({ + ...expected, + id: batched.id + }) + const stored = store.listAutomations() + const { id: _storedSingleId, ...storedSingle } = stored[0] + expect(stored[1]).toEqual({ ...storedSingle, id: batched.id }) + expect(store.automationEnablementRevision(batched.id)).toBe( + store.automationEnablementRevision(single.id) + ) + } finally { + vi.useRealTimers() + } + }) + it('stores its own contexts over a client-perspective create runContext', async () => { const store = await createStore() store.addRepo(makeRepo({ upstream: { owner: 'stablyai', repo: 'orca' } })) diff --git a/src/main/persistence/loading-store/automation-state/automation-enablement-transitions.ts b/src/main/persistence/loading-store/automation-state/automation-enablement-transitions.ts index b149bc63db7..c7d6f40fc0b 100644 --- a/src/main/persistence/loading-store/automation-state/automation-enablement-transitions.ts +++ b/src/main/persistence/loading-store/automation-state/automation-enablement-transitions.ts @@ -21,4 +21,21 @@ export class AutomationEnablementTransitions { } return updated } + + /** `track` for a batch: counts each updated row whose `enabled` flipped. */ + trackMany( + stored: readonly Automation[], + update: () => R[] + ): R[] { + const wasEnabled = new Map(stored.map((entry) => [entry.id, entry.enabled])) + const results = update() + for (const result of results) { + const before = wasEnabled.get(result.id) + if (result.automation && before !== undefined && result.automation.enabled !== before) { + this.counts.set(result.id, this.revision(result.id) + 1) + wasEnabled.set(result.id, result.automation.enabled) + } + } + return results + } } diff --git a/src/main/persistence/loading-store/automation-state/automation-persistence.ts b/src/main/persistence/loading-store/automation-state/automation-persistence.ts index f8f719ced03..7704e845f3b 100644 --- a/src/main/persistence/loading-store/automation-state/automation-persistence.ts +++ b/src/main/persistence/loading-store/automation-state/automation-persistence.ts @@ -35,6 +35,11 @@ import { type AutomationDefinitionOperations } from '../../scheduling-automations/automation-definition-operations' import { deleteAutomation as deleteAutomationOperation } from '../../scheduling-automations/automation-definition-removal' +import { + updateAutomations as updateAutomationsOperation, + type AutomationBatchUpdate, + type AutomationBatchUpdateResult +} from '../../scheduling-automations/automation-definition-update' import { createAutomationRun as createAutomationRunOperation, listAutomationRuns as listAutomationRunsOperation, @@ -170,6 +175,14 @@ export class AutomationPersistence { ) } + /** `updateAutomation` for many rows with one list write and one flush. */ + updateAutomations(batch: readonly AutomationBatchUpdate[]): AutomationBatchUpdateResult[] { + const stored = this[automationPersistenceContext].runtime.state.automations ?? [] + return this[automationEnablement].trackMany(stored, () => + updateAutomationsOperation(getAutomationDefinitionOperations(this), batch) + ) + } + /** Changes when an update pauses or re-enables the automation, never on run bookkeeping. */ automationEnablementRevision(id: string): number { return this[automationEnablement].revision(id) diff --git a/src/main/persistence/scheduling-automations/automation-definition-operations.ts b/src/main/persistence/scheduling-automations/automation-definition-operations.ts index 8e561ecbdcb..630c5d9a65d 100644 --- a/src/main/persistence/scheduling-automations/automation-definition-operations.ts +++ b/src/main/persistence/scheduling-automations/automation-definition-operations.ts @@ -1,4 +1,3 @@ -import { retainAutomationReselectionContexts } from './automation-host-reselection' import { automationCreateIdentity } from './automation-create-identity' import { getRepoSshConnectionId } from '../../../shared/execution-host' import type { Repo } from '../../../shared/repo-types' @@ -7,12 +6,10 @@ import { applyAutomationLaunchSetupPatch, automationWithLaunchSetup } from '../../../shared/automations/launch/automation-launch-setup' -import { automationCanonicalLaunchSetup } from '../../../shared/automations/launch/automation-canonical-launch-setup' import { randomUUID } from 'node:crypto' import type { Automation, - AutomationCreateInput, - AutomationUpdateInput + AutomationCreateInput } from '../../../shared/automations/automation-definition' import type { AutomationRun } from '../../../shared/automations/history/automation-run' import type { PersistedState } from '../../../shared/persisted-state-types' @@ -22,22 +19,14 @@ import { storedExtraAgentArgs } from '../../../shared/automations/launch/automation-extra-agent-args-record' import { nextAutomationOccurrenceAfter } from '../../../shared/automations/scheduling/automation-schedule-occurrences' -import { - applyAutomationExecutionTarget, - deriveAutomationExecutionTargetForCreate, - deriveAutomationExecutionTargetForUpdate -} from '../../../shared/automations/ownership/automation-execution-target' +import { deriveAutomationExecutionTargetForCreate } from '../../../shared/automations/ownership/automation-execution-target' import { assertAutomationDestination, - assertAutomationOwnerFence, - assertAutomationWorkspaceModePrecondition, assertExecutionTargetMatchesDestination, - type AutomationDestination, - type AutomationUpdatePreconditions + type AutomationDestination } from '../../../shared/automations/ownership/automation-owner-precondition' import { getAutomationContextsForRepo, - automationContextAfterUpdate, getAutomationSchedulerOwner, applyAutomationSetupTrust, normalizeAutomationSessionReuse, @@ -154,159 +143,5 @@ export function createAutomation( return automationWithLaunchSetup(automation) } -export function updateAutomation( - operations: AutomationDefinitionOperations, - id: string, - updates: AutomationUpdateInput, - options?: AutomationUpdatePreconditions & { resolvedRepo?: Repo } -): Automation { - const index = (operations.state.automations ?? []).findIndex((entry) => entry.id === id) - if (index === -1) { - throw new Error('Automation not found.') - } - const current = operations.state.automations[index] - const projectionContext = automationProjectionContext( - operations.state, - operations.storageAuthority, - false - ) - assertAutomationOwnerFence({ - automation: current, - expectedOwner: options?.expectedOwner, - operation: 'mutate', - context: projectionContext - }) - if (options?.destination) { - assertAutomationDestination(options.destination, projectionContext) - } - assertAutomationWorkspaceModePrecondition(current, options?.expectedWorkspaceMode) - const retainContexts = retainAutomationReselectionContexts( - operations.state, - current, - updates, - options?.destination, - projectionContext - ) - const launchSetupUpdate = applyAutomationLaunchSetupPatch(updates, current) - // Ignore undefined values preserved by structuredClone; explicit clears use null. - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: filtering removes only undefined entries from the validated patch and retains every remaining field's type. - const definedUpdates = Object.fromEntries( - Object.entries(launchSetupUpdate).filter(([, value]) => value !== undefined) - ) as Omit & Pick - const repoId = updates.projectId ?? current.projectId - const selectorMoveRequested = - updates.projectId !== undefined || options?.destination !== undefined - const repo = findAutomationRepoForWrite( - operations.state.repos, - repoId, - updates.runContext ?? (selectorMoveRequested ? undefined : current.runContext), - options?.resolvedRepo, - !!updates.runContext - ) - const schedulerOwner = - selectorMoveRequested && repo ? getAutomationSchedulerOwner(repo) : current.schedulerOwner - const contexts = getAutomationContextsForRepo(repo, operations.state.projectHostSetups ?? []) - const rrule = updates.rrule ?? current.rrule - const dtstart = updates.dtstart ?? current.dtstart - const scheduleChanged = updates.rrule !== undefined || updates.dtstart !== undefined - const workspaceMode = updates.workspaceMode ?? current.workspaceMode - const merged: Automation = { - ...current, - ...definedUpdates, - name: updates.name !== undefined ? updates.name.trim() || 'Untitled automation' : current.name, - precheck: Object.hasOwn(definedUpdates, 'precheck') - ? normalizeAutomationPrecheck(definedUpdates.precheck) - : normalizeAutomationPrecheck(current.precheck), - projectId: repoId, - // Why the wire object is ignored: contexts are the storing authority's own - // registry speaking. A move restates them from that registry, anything else - // keeps the stored value; an explicit null still clears. - runContext: retainContexts - ? current.runContext - : automationContextAfterUpdate( - current.runContext, - definedUpdates.runContext, - contexts.runContext, - selectorMoveRequested - ), - sourceContext: retainContexts - ? current.sourceContext - : automationContextAfterUpdate( - current.sourceContext, - definedUpdates.sourceContext, - contexts.sourceContext, - selectorMoveRequested - ), - schedulerOwner, - workspaceMode, - workspaceId: - workspaceMode === 'existing' - ? Object.hasOwn(definedUpdates, 'workspaceId') - ? (definedUpdates.workspaceId ?? null) - : current.workspaceId - : null, - baseBranch: - workspaceMode === 'new_per_run' - ? Object.hasOwn(definedUpdates, 'baseBranch') - ? (definedUpdates.baseBranch ?? null) - : (current.baseBranch ?? null) - : null, - setupDecision: normalizeAutomationSetupDecisionForWorkspaceMode( - workspaceMode, - Object.hasOwn(definedUpdates, 'setupDecision') - ? definedUpdates.setupDecision - : current.setupDecision - ), - reuseSession: - workspaceMode === 'existing' - ? (updates.reuseSession ?? current.reuseSession ?? false) - : false, - rrule, - dtstart, - nextRunAt: scheduleChanged - ? nextAutomationOccurrenceAfter(rrule, dtstart, Date.now()) - : current.nextRunAt, - updatedAt: Date.now() - } - if (retainContexts && !Object.hasOwn(current, 'sourceContext')) { - delete merged.sourceContext - } - applyAutomationSetupTrust(merged, definedUpdates, current) - delete merged.launchSetup - for (const key of ['surface', 'launchPreferences', 'agentArgs', 'extraAgentArgs'] as const) { - delete merged[key] - } - Object.assign(merged, automationCanonicalLaunchSetup(launchSetupUpdate)) - assertAutomationExtraAgentArgs(merged) - const previousPin = automationWorkspaceSshPin(operations.state, current.workspaceId) - const workspaceSshPin = automationWorkspaceSshPin(operations.state, merged.workspaceId) - const workspaceSshPinMoved = previousPin?.targetId !== workspaceSshPin?.targetId - const executionTarget = deriveAutomationExecutionTargetForUpdate({ - current, - repo, - selectorMoveRequested, - sshTargetGeneration: sshTargetGenerationForConnection( - operations.state, - repo ? getRepoSshConnectionId(repo) : undefined - ), - workspaceSshPin, - workspaceSshPinMoved - }) - refuseManagedOrcadHost(operations.state, executionTarget, workspaceSshPin, current, previousPin) - if (options?.destination) { - assertExecutionTargetMatchesDestination(executionTarget, options.destination, workspaceSshPin) - } - const updated = applyAutomationExecutionTarget( - merged, - executionTarget, - workspaceSshPinMoved ? undefined : workspaceSshPin - ) - // Replace the array to invalidate identity-keyed list projections. - operations.state.automations = operations.state.automations.map((entry) => - entry.id === id ? updated : entry - ) - operations.flush() - return automationWithLaunchSetup(updated) -} - +export { updateAutomation } from './automation-definition-update' export { deleteAutomation } from './automation-definition-removal' diff --git a/src/main/persistence/scheduling-automations/automation-definition-update.ts b/src/main/persistence/scheduling-automations/automation-definition-update.ts new file mode 100644 index 00000000000..0f95d88d7dd --- /dev/null +++ b/src/main/persistence/scheduling-automations/automation-definition-update.ts @@ -0,0 +1,245 @@ +import { retainAutomationReselectionContexts } from './automation-host-reselection' +import { getRepoSshConnectionId } from '../../../shared/execution-host' +import type { Repo } from '../../../shared/repo-types' +import { findAutomationRepoForWrite } from '../../../shared/automations/ownership/automation-repo-target' +import { + applyAutomationLaunchSetupPatch, + automationWithLaunchSetup +} from '../../../shared/automations/launch/automation-launch-setup' +import { automationCanonicalLaunchSetup } from '../../../shared/automations/launch/automation-canonical-launch-setup' +import type { + Automation, + AutomationUpdateInput +} from '../../../shared/automations/automation-definition' +import { normalizeAutomationPrecheck } from '../../../shared/automations/launch/automation-precheck' +import { assertAutomationExtraAgentArgs } from '../../../shared/automations/launch/automation-extra-agent-args-record' +import { nextAutomationOccurrenceAfter } from '../../../shared/automations/scheduling/automation-schedule-occurrences' +import { + applyAutomationExecutionTarget, + deriveAutomationExecutionTargetForUpdate +} from '../../../shared/automations/ownership/automation-execution-target' +import { + assertAutomationDestination, + assertAutomationOwnerFence, + assertAutomationWorkspaceModePrecondition, + assertExecutionTargetMatchesDestination, + type AutomationUpdatePreconditions +} from '../../../shared/automations/ownership/automation-owner-precondition' +import { + getAutomationContextsForRepo, + automationContextAfterUpdate, + getAutomationSchedulerOwner, + applyAutomationSetupTrust, + normalizeAutomationSetupDecisionForWorkspaceMode +} from './automation-context-migration' +import { refuseManagedOrcadHost } from '../../automations/ownership/orcad-fenced-automation' +import { + automationProjectionContext, + automationWorkspaceSshPin, + sshTargetGenerationForConnection +} from './automation-owner-projection' +import type { AutomationDefinitionOperations } from './automation-definition-operations' + +export type AutomationBatchUpdate = { id: string; updates: AutomationUpdateInput } + +export type AutomationBatchUpdateResult = + | { id: string; automation: Automation } + | { id: string; error: unknown } + +/** + * Applies each update as `updateAutomation` would, but replaces the list and flushes once for the + * whole batch. Why: per-row writes re-copy every stored automation once per row. One row's refusal + * (missing, fenced) is its own result and never blocks the others. + */ +export function updateAutomations( + operations: AutomationDefinitionOperations, + batch: readonly AutomationBatchUpdate[] +): AutomationBatchUpdateResult[] { + const byId = new Map((operations.state.automations ?? []).map((entry) => [entry.id, entry])) + const written = new Map() + const results = batch.map(({ id, updates }): AutomationBatchUpdateResult => { + const current = written.get(id) ?? byId.get(id) + if (!current) { + return { id, error: new Error('Automation not found.') } + } + try { + const updated = mergeAutomationUpdate(operations, current, updates) + written.set(id, updated) + return { id, automation: automationWithLaunchSetup(updated) } + } catch (error) { + return { id, error } + } + }) + if (written.size > 0) { + // Replace the array to invalidate identity-keyed list projections. + operations.state.automations = operations.state.automations.map( + (entry) => written.get(entry.id) ?? entry + ) + operations.flush() + } + return results +} + +export function updateAutomation( + operations: AutomationDefinitionOperations, + id: string, + updates: AutomationUpdateInput, + options?: AutomationUpdatePreconditions & { resolvedRepo?: Repo } +): Automation { + const current = (operations.state.automations ?? []).find((entry) => entry.id === id) + if (!current) { + throw new Error('Automation not found.') + } + const updated = mergeAutomationUpdate(operations, current, updates, options) + // Replace the array to invalidate identity-keyed list projections. + operations.state.automations = operations.state.automations.map((entry) => + entry.id === id ? updated : entry + ) + operations.flush() + return automationWithLaunchSetup(updated) +} + +/** The stored record `updates` makes of `current`, checked but not yet written. */ +export function mergeAutomationUpdate( + operations: AutomationDefinitionOperations, + current: Automation, + updates: AutomationUpdateInput, + options?: AutomationUpdatePreconditions & { resolvedRepo?: Repo } +): Automation { + const projectionContext = automationProjectionContext( + operations.state, + operations.storageAuthority, + false + ) + assertAutomationOwnerFence({ + automation: current, + expectedOwner: options?.expectedOwner, + operation: 'mutate', + context: projectionContext + }) + if (options?.destination) { + assertAutomationDestination(options.destination, projectionContext) + } + assertAutomationWorkspaceModePrecondition(current, options?.expectedWorkspaceMode) + const retainContexts = retainAutomationReselectionContexts( + operations.state, + current, + updates, + options?.destination, + projectionContext + ) + const launchSetupUpdate = applyAutomationLaunchSetupPatch(updates, current) + // Ignore undefined values preserved by structuredClone; explicit clears use null. + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: filtering removes only undefined entries from the validated patch and retains every remaining field's type. + const definedUpdates = Object.fromEntries( + Object.entries(launchSetupUpdate).filter(([, value]) => value !== undefined) + ) as Omit & Pick + const repoId = updates.projectId ?? current.projectId + const selectorMoveRequested = + updates.projectId !== undefined || options?.destination !== undefined + const repo = findAutomationRepoForWrite( + operations.state.repos, + repoId, + updates.runContext ?? (selectorMoveRequested ? undefined : current.runContext), + options?.resolvedRepo, + !!updates.runContext + ) + const schedulerOwner = + selectorMoveRequested && repo ? getAutomationSchedulerOwner(repo) : current.schedulerOwner + const contexts = getAutomationContextsForRepo(repo, operations.state.projectHostSetups ?? []) + const rrule = updates.rrule ?? current.rrule + const dtstart = updates.dtstart ?? current.dtstart + const scheduleChanged = updates.rrule !== undefined || updates.dtstart !== undefined + const workspaceMode = updates.workspaceMode ?? current.workspaceMode + const merged: Automation = { + ...current, + ...definedUpdates, + name: updates.name !== undefined ? updates.name.trim() || 'Untitled automation' : current.name, + precheck: Object.hasOwn(definedUpdates, 'precheck') + ? normalizeAutomationPrecheck(definedUpdates.precheck) + : normalizeAutomationPrecheck(current.precheck), + projectId: repoId, + // Why the wire object is ignored: contexts are the storing authority's own + // registry speaking. A move restates them from that registry, anything else + // keeps the stored value; an explicit null still clears. + runContext: retainContexts + ? current.runContext + : automationContextAfterUpdate( + current.runContext, + definedUpdates.runContext, + contexts.runContext, + selectorMoveRequested + ), + sourceContext: retainContexts + ? current.sourceContext + : automationContextAfterUpdate( + current.sourceContext, + definedUpdates.sourceContext, + contexts.sourceContext, + selectorMoveRequested + ), + schedulerOwner, + workspaceMode, + workspaceId: + workspaceMode === 'existing' + ? Object.hasOwn(definedUpdates, 'workspaceId') + ? (definedUpdates.workspaceId ?? null) + : current.workspaceId + : null, + baseBranch: + workspaceMode === 'new_per_run' + ? Object.hasOwn(definedUpdates, 'baseBranch') + ? (definedUpdates.baseBranch ?? null) + : (current.baseBranch ?? null) + : null, + setupDecision: normalizeAutomationSetupDecisionForWorkspaceMode( + workspaceMode, + Object.hasOwn(definedUpdates, 'setupDecision') + ? definedUpdates.setupDecision + : current.setupDecision + ), + reuseSession: + workspaceMode === 'existing' + ? (updates.reuseSession ?? current.reuseSession ?? false) + : false, + rrule, + dtstart, + nextRunAt: scheduleChanged + ? nextAutomationOccurrenceAfter(rrule, dtstart, Date.now()) + : current.nextRunAt, + updatedAt: Date.now() + } + if (retainContexts && !Object.hasOwn(current, 'sourceContext')) { + delete merged.sourceContext + } + applyAutomationSetupTrust(merged, definedUpdates, current) + delete merged.launchSetup + for (const key of ['surface', 'launchPreferences', 'agentArgs', 'extraAgentArgs'] as const) { + delete merged[key] + } + Object.assign(merged, automationCanonicalLaunchSetup(launchSetupUpdate)) + assertAutomationExtraAgentArgs(merged) + const previousPin = automationWorkspaceSshPin(operations.state, current.workspaceId) + const workspaceSshPin = automationWorkspaceSshPin(operations.state, merged.workspaceId) + const workspaceSshPinMoved = previousPin?.targetId !== workspaceSshPin?.targetId + const executionTarget = deriveAutomationExecutionTargetForUpdate({ + current, + repo, + selectorMoveRequested, + sshTargetGeneration: sshTargetGenerationForConnection( + operations.state, + repo ? getRepoSshConnectionId(repo) : undefined + ), + workspaceSshPin, + workspaceSshPinMoved + }) + refuseManagedOrcadHost(operations.state, executionTarget, workspaceSshPin, current, previousPin) + if (options?.destination) { + assertExecutionTargetMatchesDestination(executionTarget, options.destination, workspaceSshPin) + } + return applyAutomationExecutionTarget( + merged, + executionTarget, + workspaceSshPinMoved ? undefined : workspaceSshPin + ) +} diff --git a/src/main/plugins/plugin-automation-ownership.test.ts b/src/main/plugins/plugin-automation-ownership.test.ts index 6d965bf3807..8e2fd82f418 100644 --- a/src/main/plugins/plugin-automation-ownership.test.ts +++ b/src/main/plugins/plugin-automation-ownership.test.ts @@ -20,13 +20,16 @@ function automation(id: string, pluginKey: string, createdAt = 1): Automation { function automationStore(initial: Automation[]) { let stored = initial - const updateAutomation = vi.fn(async (id: string, updates: Partial) => { - stored = stored.map((entry) => (entry.id === id ? { ...entry, ...updates } : entry)) - return stored.find((entry) => entry.id === id)! - }) + const updateAutomations = vi.fn( + async (entries: readonly { id: string; updates: Partial }[]) => { + const patches = new Map(entries.map(({ id, updates }) => [id, updates])) + stored = stored.map((entry) => ({ ...entry, ...patches.get(entry.id) })) + return entries.map(({ id }) => ({ id, automation: stored.find((entry) => entry.id === id)! })) + } + ) return { - source: { listAutomationsForScope: () => ({ automations: stored }), updateAutomation }, - updateAutomation, + source: { listAutomationsForScope: () => ({ automations: stored }), updateAutomations }, + updateAutomations, get: (id: string) => stored.find((entry) => entry.id === id) } } @@ -55,11 +58,45 @@ describe('automations of a plugin the person took away', () => { await service.refresh() await settle() expect(service.getDiscovered()).toEqual([]) - expect(store.updateAutomation).not.toHaveBeenCalled() + expect(store.updateAutomations).not.toHaveBeenCalled() expect(store.get('a-installed')).toMatchObject({ enabled: true }) } finally { await service.dispose() await rm(userData, { recursive: true, force: true }) } }) + + it('pauses every automation of a disabled plugin in one write', async () => { + const store = automationStore([ + automation('a-1', 'acme.gone'), + automation('a-2', 'acme.gone'), + automation('a-3', 'acme.gone'), + automation('b-1', 'acme.kept') + ]) + installPluginAutomationOwnershipEnd( + { + options: { + isPluginSystemEnabled: () => true, + getDisabledPlugins: () => ['acme.gone'] + }, + whenReady: () => Promise.resolve(), + onChanged: () => () => {} + }, + store.source + ) + await settle() + expect(store.updateAutomations).toHaveBeenCalledTimes(1) + expect(store.updateAutomations.mock.calls[0][0].map(({ id }) => id)).toEqual([ + 'a-1', + 'a-2', + 'a-3' + ]) + for (const id of ['a-1', 'a-2', 'a-3']) { + expect(store.get(id)).toMatchObject({ + enabled: false, + createdBy: { endedAt: expect.any(Number) } + }) + } + expect(store.get('b-1')).toMatchObject({ enabled: true }) + }) }) diff --git a/src/main/plugins/plugin-automation-ownership.ts b/src/main/plugins/plugin-automation-ownership.ts index f49be92c510..82744e78174 100644 --- a/src/main/plugins/plugin-automation-ownership.ts +++ b/src/main/plugins/plugin-automation-ownership.ts @@ -16,7 +16,12 @@ type PluginStateSource = { type AutomationSource = { listAutomationsForScope(params: Record): { automations: readonly Automation[] } - updateAutomation(id: string, updates: AutomationUpdateInput): Promise + updateAutomations( + entries: readonly { + id: string + updates: Pick + }[] + ): Promise } /** @@ -72,19 +77,27 @@ export function installPluginAutomationOwnershipEnd( createdBy: automation.createdBy }) ) + if (ended.length === 0) { + continue + } const endedAt = Date.now() - await Promise.all( - ended.map((automation) => - automations - .updateAutomation(automation.id, { + // Why one batch: per-row updates rewrite and persist the whole list once per automation. + const results = await automations + .updateAutomations( + ended.map((automation) => ({ + id: automation.id, + updates: { enabled: false, ...(automation.createdBy ? { createdBy: { ...automation.createdBy, endedAt } } : {}) - }) - .catch((error: unknown) => - log(`[plugins] could not pause automation ${automation.id}: ${String(error)}`) - ) + } + })) ) - ) + .catch((error: unknown) => ended.map(({ id }) => ({ id, error }))) + for (const result of results) { + if ('error' in result) { + log(`[plugins] could not pause automation ${result.id}: ${String(result.error)}`) + } + } } while (again) } finally { running = false diff --git a/src/main/runtime/construction/runtime-store-contract.ts b/src/main/runtime/construction/runtime-store-contract.ts index f80776e8be8..3d976eede42 100644 --- a/src/main/runtime/construction/runtime-store-contract.ts +++ b/src/main/runtime/construction/runtime-store-contract.ts @@ -104,6 +104,7 @@ export type RuntimeStore = { listAutomationRunsPage?: Store['listAutomationRunsPage'] createAutomation?: Store['createAutomation'] updateAutomation?: Store['updateAutomation'] + updateAutomations?: Store['updateAutomations'] deleteAutomation?: Store['deleteAutomation'] getSparsePresets?: Store['getSparsePresets'] saveSparsePreset?: Store['saveSparsePreset'] diff --git a/src/main/runtime/integrations/automations/runtime-automation-batch-update.ts b/src/main/runtime/integrations/automations/runtime-automation-batch-update.ts new file mode 100644 index 00000000000..cdd10f0ecd2 --- /dev/null +++ b/src/main/runtime/integrations/automations/runtime-automation-batch-update.ts @@ -0,0 +1,39 @@ +import type { AutomationUpdateInput } from '../../../../shared/automations/automation-definition' +import type { AutomationBatchUpdateResult } from '../../../persistence/scheduling-automations/automation-definition-update' +import type { RuntimeStore } from '../../construction/runtime-store-contract' + +/** Why only these fields: target, run-context and session changes need the checks only a single + * `update` performs, so the batch path cannot carry them. */ +export type RuntimeAutomationBatchUpdate = { + id: string + updates: Pick +} + +/** + * The controller's `update` for many automations at once: one store write and one flush instead + * of one per row. + */ +export async function updateRuntimeAutomations( + store: RuntimeStore | null, + entries: readonly RuntimeAutomationBatchUpdate[] +): Promise { + if (!store?.updateAutomations) { + throw new Error('runtime_unavailable') + } + if (entries.length === 0) { + return [] + } + const results = store.updateAutomations( + entries.map(({ id, updates }) => ({ + id, + updates: { + ...(updates.enabled !== undefined ? { enabled: updates.enabled } : {}), + ...(updates.createdBy !== undefined ? { createdBy: updates.createdBy } : {}) + } + })) + ) + if (results.some((result) => 'automation' in result)) { + await store.flushPendingOrThrowAsync?.({ drainToStableGeneration: false }) + } + return results +} diff --git a/src/main/runtime/integrations/automations/runtime-automation-controller.ts b/src/main/runtime/integrations/automations/runtime-automation-controller.ts index 6839b0935e4..64c5fdd0d07 100644 --- a/src/main/runtime/integrations/automations/runtime-automation-controller.ts +++ b/src/main/runtime/integrations/automations/runtime-automation-controller.ts @@ -21,6 +21,10 @@ import type { import { runAutomationNowFenced } from '../../../automations/dispatch/refused-manual-run' import { paginateAutomationRuns } from '../../../../shared/automations/history/automation-run-cursor' import { hasRuntimeAutomationUpdateValue } from './runtime-automation-update-value' +import { + updateRuntimeAutomations, + type RuntimeAutomationBatchUpdate +} from './runtime-automation-batch-update' import { assertAutomationRunContextMatchesTarget, type RuntimeAutomationTargetResolvers, @@ -192,6 +196,11 @@ export class RuntimeAutomationController { return automation } + /** `update` for many automations with one store write and one flush; moves need `update`. */ + updateMany(entries: readonly RuntimeAutomationBatchUpdate[]) { + return updateRuntimeAutomations(this.store, entries) + } + async delete( id: string, expectedOwner?: AutomationOwnerPrecondition diff --git a/src/main/runtime/workspaces/persisted-surfaces/automation-api-operations.ts b/src/main/runtime/workspaces/persisted-surfaces/automation-api-operations.ts index c017621dcf7..f094a8752d7 100644 --- a/src/main/runtime/workspaces/persisted-surfaces/automation-api-operations.ts +++ b/src/main/runtime/workspaces/persisted-surfaces/automation-api-operations.ts @@ -16,6 +16,8 @@ import { type AutomationListResult } from '../../../../shared/automations/ownership/automation-list-scope' import type { AutomationService } from '../../../automations/automation-service' +import type { RuntimeAutomationBatchUpdate } from '../../integrations/automations/runtime-automation-batch-update' +import type { AutomationBatchUpdateResult } from '../../../persistence/scheduling-automations/automation-definition-update' import { OrcaRuntimeWithCrewOperations } from './crew-operations' export abstract class OrcaRuntimeWithAutomationApiOperations extends OrcaRuntimeWithCrewOperations { @@ -119,6 +121,36 @@ export abstract class OrcaRuntimeWithAutomationApiOperations extends OrcaRuntime }) } + /** Many updates with one write, and one change event per affected host instead of per row. */ + updateAutomations( + entries: readonly RuntimeAutomationBatchUpdate[] + ): Promise { + return this.automation.withExternalProbePriority(() => { + const sources = new Map(entries.map(({ id }) => [id, this.automationChangeSelector(id)])) + return this.automation.updateMany(entries).then((results) => { + const published = new Set() + for (const result of results) { + if (!('automation' in result)) { + continue + } + const before = sources.get(result.id) ?? null + const after = this.automationChangeSelector(result.automation.id) + for (const selector of automationChangePublications(before, after)) { + const key = JSON.stringify(selector ?? null) + if (!published.has(key)) { + published.add(key) + this.notifyAutomationsChanged({ + reason: 'definition', + ...(selector ? { selector } : {}) + }) + } + } + } + return results + }) + }) + } + deleteAutomation( id: string, expectedOwner?: AutomationOwnerPrecondition From 2050d9fbc6e5d92c5992cb5eef5aee0c6b6f9fd9 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 20:41:35 -0800 Subject: [PATCH 011/178] refactor: group source-control modules by operation ownership (#41812) --- config/flaky-test-pattern-baseline.txt | 2 +- config/main-sync-fs-baseline.txt | 2 +- .../scripts/ci/sharding/ci-shard-timings.json | 58 ++++----- .../git-diff-blob-concurrency-benchmark.mjs | 2 +- .../branch-change-entries.ts | 10 +- .../branch-change-list-cache-real-git.test.ts | 0 .../branch-change-list-cache.ts | 16 +-- .../{ => comparisons}/branch-compare.ts | 10 +- .../{ => comparisons}/branch-diff.ts | 14 +- .../{ => comparisons}/commit-compare.ts | 8 +- .../{ => comparisons}/commit-diff.ts | 18 ++- .../{ => comparisons}/commit-pair-cache.ts | 11 +- .../{ => comparisons}/compare-ref-oids.ts | 4 +- .../status-branch-compare-real-ref.test.ts | 0 .../{ => diffs}/blob-absence-real-git.test.ts | 6 +- .../source-control/{ => diffs}/diff-result.ts | 6 +- .../diff-textconv-real-git.test.ts | 2 +- .../diff-too-large-real-git.test.ts | 4 +- .../source-control/{ => diffs}/file-diff.ts | 23 ++-- .../{ => diffs}/git-blob-read.ts | 16 +-- .../staged-rename-diff-real-git.test.ts | 2 +- .../{ => diffs}/status-diff.test.ts | 16 +-- .../{ => diffs}/status-test-harness.ts | 2 +- .../{ => diffs}/textconv-driver.ts | 6 +- .../drive-checkout-suspect-paths.test.ts | 0 .../drive-checkout-suspect-paths.ts | 2 +- .../drive-checkout-suspect-status.ts | 24 ++-- .../drive-checkout-suspect-writes.ts | 6 +- .../git-read-cache-invalidation.ts | 8 +- .../git-upstream-status-read-owner.test.ts | 121 ------------------ .../{ => ignores}/check-ignored-paths.ts | 6 +- .../{ => ignores}/huge-folder-ignore.test.ts | 0 .../{ => ignores}/huge-folder-ignore.ts | 2 +- .../deadline-bounded-stamp.ts | 4 +- .../gitattributes-stamp-paths.ts | 0 .../{ => input-stamps}/stamped-git-config.ts | 16 +-- .../{ => input-stamps}/worktree-diff-stamp.ts | 6 +- .../{ => mutations}/commit-changes.ts | 6 +- .../complete-merge-real-git.test.ts | 0 .../{ => mutations}/discard-changes.ts | 16 +-- .../{ => mutations}/git-conflict-operation.ts | 10 +- .../stage-worktree-changes.test.ts | 4 +- .../{ => mutations}/staged-commit-context.ts | 10 +- .../staging-discard-index-safety.test.ts | 0 .../source-control/{ => mutations}/staging.ts | 22 ++-- .../status-discard-symlink.test.ts | 0 .../status-pathspec-literals.test.ts | 0 .../git/source-control/settled-diff-cache.ts | 2 +- .../{ => status}/get-status-options.ts | 2 +- .../{ => status}/porcelain-v1-records.test.ts | 0 .../{ => status}/porcelain-v1-records.ts | 0 ...us-branch-line-total-exec-contract.test.ts | 32 ++--- .../status-branch-line-total-input.ts | 12 +- .../status-branch-line-total-real-git.test.ts | 4 +- ...s-branch-line-total-untracked-base.test.ts | 4 +- .../{ => status}/status-cquoted-paths.test.ts | 2 +- .../{ => status}/status-index-lock-wait.ts | 8 +- .../status-line-stats-host-paths.test.ts | 14 +- ...status-line-stats-renames-real-git.test.ts | 2 +- .../{ => status}/status-line-stats.ts | 22 ++-- .../{ => status}/status-porcelain-stream.ts | 8 +- .../status-read-failure-real-git.test.ts | 2 +- .../{ => status}/status-read.ts | 32 ++--- .../status-shared-symlinks.test.ts | 0 .../status-untracked-directories.ts | 8 +- .../{ => submodules}/status-submodule.test.ts | 20 +-- .../{ => submodules}/submodule-diff.ts | 8 +- .../{ => submodules}/submodule-gitlink-oid.ts | 8 +- ...ubmodule-gitlink-pathspec-real-git.test.ts | 2 +- .../{ => submodules}/submodule-paths.ts | 10 +- ...submodule-restore-recurse-real-git.test.ts | 6 +- .../submodule-restore-recurse.ts | 8 +- .../{ => submodules}/submodule-status.ts | 19 +-- .../effective-upstream-status-cache.ts | 6 +- .../effective-upstream-status-probe.ts | 10 +- .../git-upstream-status-read-owner.ts | 9 +- .../resolved-upstream-name-cache.ts | 0 .../status-upstream-ref.test.ts | 0 .../{ => upstream}/status-upstream-ref.ts | 2 +- ...upstream-deferred-fork-remote-real.test.ts | 2 +- .../{ => upstream}/upstream.test.ts | 2 +- .../source-control/{ => upstream}/upstream.ts | 18 +-- .../checkout/branch-switch-real-git.test.ts | 2 +- .../branch-switch-target-real-git.test.ts | 2 +- .../checkout/commit-actions-real-git.test.ts | 2 +- .../git/worktrees/checkout/commit-actions.ts | 2 +- .../content-paths/worktree-include-file.ts | 2 +- .../worktree-shared-directories.ts | 2 +- ...lesystem-git-commit-generation-handlers.ts | 2 +- .../filesystem-git-commit-handlers.ts | 2 +- .../filesystem-git-diff-handlers.ts | 4 +- .../filesystem-git-index-handlers.ts | 7 +- .../filesystem-git-status-handlers.ts | 12 +- .../filesystem-git-status-staging.test.ts | 20 +-- ...pull-request-linked-issue-lifetime.test.ts | 20 +-- .../filesystem-wsl-stopped-distro-git.test.ts | 20 +-- ...ilesystem-wsl-stopped-distro-reads.test.ts | 20 +-- src/main/ipc/filesystem/filesystem.test.ts | 20 +-- .../filesystem/git-remote/compare-handlers.ts | 4 +- .../filesystem/git-remote/sync-handlers.ts | 2 +- ...orkspace-cleanup-document-lifetime.test.ts | 2 +- ...ace-cleanup-execution-host-routing.test.ts | 24 +--- .../workspace-cleanup-git-evidence.ts | 2 +- ...e-base-directory-git-status-events.test.ts | 2 +- .../git-status-upstream-ref-watch-request.ts | 2 +- src/main/providers/git/local-provider.ts | 29 +++-- .../git/runtime-git-diff-commands.ts | 2 +- .../runtime-git-generation-admission.test.ts | 2 +- .../git/runtime-git-generation-commands.ts | 2 +- ...ository-project-operations-part-02.spec.ts | 2 +- .../runtime-test-module-registration.spec.ts | 2 +- .../runtime-repository-issue-command.test.ts | 2 +- ...hosted-review-creation-eligibility.test.ts | 4 +- .../hosted-review-creation-git-state.ts | 4 +- .../hosted-review-creation.test.ts | 2 +- ...sted-review-execution-host-routing.test.ts | 4 +- .../workspace-hooks/issue-command-file.ts | 4 +- .../issue-command-wsl-stopped-distro.test.ts | 10 +- .../tools/branch-line-total-renderer.test.mjs | 72 ----------- 119 files changed, 446 insertions(+), 657 deletions(-) rename src/main/git/source-control/{ => comparisons}/branch-change-entries.ts (71%) rename src/main/git/source-control/{ => comparisons}/branch-change-list-cache-real-git.test.ts (100%) rename src/main/git/source-control/{ => comparisons}/branch-change-list-cache.ts (87%) rename src/main/git/source-control/{ => comparisons}/branch-compare.ts (91%) rename src/main/git/source-control/{ => comparisons}/branch-diff.ts (76%) rename src/main/git/source-control/{ => comparisons}/commit-compare.ts (84%) rename src/main/git/source-control/{ => comparisons}/commit-diff.ts (77%) rename src/main/git/source-control/{ => comparisons}/commit-pair-cache.ts (93%) rename src/main/git/source-control/{ => comparisons}/compare-ref-oids.ts (93%) rename src/main/git/source-control/{ => comparisons}/status-branch-compare-real-ref.test.ts (100%) rename src/main/git/source-control/{ => diffs}/blob-absence-real-git.test.ts (95%) rename src/main/git/source-control/{ => diffs}/diff-result.ts (87%) rename src/main/git/source-control/{ => diffs}/diff-textconv-real-git.test.ts (98%) rename src/main/git/source-control/{ => diffs}/diff-too-large-real-git.test.ts (92%) rename src/main/git/source-control/{ => diffs}/file-diff.ts (91%) rename src/main/git/source-control/{ => diffs}/git-blob-read.ts (91%) rename src/main/git/source-control/{ => diffs}/staged-rename-diff-real-git.test.ts (96%) rename src/main/git/source-control/{ => diffs}/status-diff.test.ts (79%) rename src/main/git/source-control/{ => diffs}/status-test-harness.ts (96%) rename src/main/git/source-control/{ => diffs}/textconv-driver.ts (88%) rename src/main/git/source-control/{ => drive-checkout}/drive-checkout-suspect-paths.test.ts (100%) rename src/main/git/source-control/{ => drive-checkout}/drive-checkout-suspect-paths.ts (98%) rename src/main/git/source-control/{ => drive-checkout}/drive-checkout-suspect-status.ts (89%) rename src/main/git/source-control/{ => drive-checkout}/drive-checkout-suspect-writes.ts (92%) delete mode 100644 src/main/git/source-control/git-upstream-status-read-owner.test.ts rename src/main/git/source-control/{ => ignores}/check-ignored-paths.ts (88%) rename src/main/git/source-control/{ => ignores}/huge-folder-ignore.test.ts (100%) rename src/main/git/source-control/{ => ignores}/huge-folder-ignore.ts (96%) rename src/main/git/source-control/{ => input-stamps}/deadline-bounded-stamp.ts (82%) rename src/main/git/source-control/{ => input-stamps}/gitattributes-stamp-paths.ts (100%) rename src/main/git/source-control/{ => input-stamps}/stamped-git-config.ts (93%) rename src/main/git/source-control/{ => input-stamps}/worktree-diff-stamp.ts (97%) rename src/main/git/source-control/{ => mutations}/commit-changes.ts (90%) rename src/main/git/source-control/{ => mutations}/complete-merge-real-git.test.ts (100%) rename src/main/git/source-control/{ => mutations}/discard-changes.ts (88%) rename src/main/git/source-control/{ => mutations}/git-conflict-operation.ts (85%) rename src/main/git/source-control/{ => mutations}/stage-worktree-changes.test.ts (98%) rename src/main/git/source-control/{ => mutations}/staged-commit-context.ts (80%) rename src/main/git/source-control/{ => mutations}/staging-discard-index-safety.test.ts (100%) rename src/main/git/source-control/{ => mutations}/staging.ts (86%) rename src/main/git/source-control/{ => mutations}/status-discard-symlink.test.ts (100%) rename src/main/git/source-control/{ => mutations}/status-pathspec-literals.test.ts (100%) rename src/main/git/source-control/{ => status}/get-status-options.ts (87%) rename src/main/git/source-control/{ => status}/porcelain-v1-records.test.ts (100%) rename src/main/git/source-control/{ => status}/porcelain-v1-records.ts (100%) rename src/main/git/source-control/{ => status}/status-branch-line-total-exec-contract.test.ts (86%) rename src/main/git/source-control/{ => status}/status-branch-line-total-input.ts (85%) rename src/main/git/source-control/{ => status}/status-branch-line-total-real-git.test.ts (96%) rename src/main/git/source-control/{ => status}/status-branch-line-total-untracked-base.test.ts (96%) rename src/main/git/source-control/{ => status}/status-cquoted-paths.test.ts (96%) rename src/main/git/source-control/{ => status}/status-index-lock-wait.ts (89%) rename src/main/git/source-control/{ => status}/status-line-stats-host-paths.test.ts (87%) rename src/main/git/source-control/{ => status}/status-line-stats-renames-real-git.test.ts (97%) rename src/main/git/source-control/{ => status}/status-line-stats.ts (86%) rename src/main/git/source-control/{ => status}/status-porcelain-stream.ts (93%) rename src/main/git/source-control/{ => status}/status-read-failure-real-git.test.ts (96%) rename src/main/git/source-control/{ => status}/status-read.ts (87%) rename src/main/git/source-control/{ => status}/status-shared-symlinks.test.ts (100%) rename src/main/git/source-control/{ => status}/status-untracked-directories.ts (94%) rename src/main/git/source-control/{ => submodules}/status-submodule.test.ts (83%) rename src/main/git/source-control/{ => submodules}/submodule-diff.ts (91%) rename src/main/git/source-control/{ => submodules}/submodule-gitlink-oid.ts (84%) rename src/main/git/source-control/{ => submodules}/submodule-gitlink-pathspec-real-git.test.ts (98%) rename src/main/git/source-control/{ => submodules}/submodule-paths.ts (92%) rename src/main/git/source-control/{ => submodules}/submodule-restore-recurse-real-git.test.ts (91%) rename src/main/git/source-control/{ => submodules}/submodule-restore-recurse.ts (76%) rename src/main/git/source-control/{ => submodules}/submodule-status.ts (84%) rename src/main/git/source-control/{ => upstream}/effective-upstream-status-cache.ts (95%) rename src/main/git/source-control/{ => upstream}/effective-upstream-status-probe.ts (92%) rename src/main/git/source-control/{ => upstream}/git-upstream-status-read-owner.ts (82%) rename src/main/git/source-control/{ => upstream}/resolved-upstream-name-cache.ts (100%) rename src/main/git/source-control/{ => upstream}/status-upstream-ref.test.ts (100%) rename src/main/git/source-control/{ => upstream}/status-upstream-ref.ts (93%) rename src/main/git/source-control/{ => upstream}/upstream-deferred-fork-remote-real.test.ts (96%) rename src/main/git/source-control/{ => upstream}/upstream.test.ts (92%) rename src/main/git/source-control/{ => upstream}/upstream.ts (81%) delete mode 100644 tests/tools/branch-line-total-renderer.test.mjs diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index 901b5901906..7661ea2fa67 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -425,7 +425,7 @@ 1 test-no-wall-clock:src/main/git/command-runner/hosted-cli/runner-gh-account-binding.test.ts 1 test-no-wall-clock:src/main/git/refs/maintenance/pack-refs-lock-ownership.test.ts 2 test-no-wall-clock:src/main/git/refs/maintenance/repo-ref-maintenance-real-git.test.ts -1 test-no-wall-clock:src/main/git/source-control/blob-absence-real-git.test.ts +1 test-no-wall-clock:src/main/git/source-control/diffs/blob-absence-real-git.test.ts 2 test-no-wall-clock:src/main/git/worktrees/catalog/worktree-created-description-real-git.test.ts 3 test-no-wall-clock:src/main/github/pull-requests/refresh/pr-refresh-coordinator-active-visible-priority.test.ts 8 test-no-wall-clock:src/main/github/pull-requests/refresh/pr-refresh-coordinator-alias-coalescing.test.ts diff --git a/config/main-sync-fs-baseline.txt b/config/main-sync-fs-baseline.txt index 2d6a2a36a5a..98994ee5617 100644 --- a/config/main-sync-fs-baseline.txt +++ b/config/main-sync-fs-baseline.txt @@ -251,7 +251,7 @@ 1 src/main/foreign-sqlite-readers/foreign-sqlite-reader-spawn.ts 1 src/main/foreign-sqlite-readers/readers/cursor-profile.ts 1 src/main/foreign-sqlite-readers/readers/opencode-binder-sessions.ts -1 src/main/git/source-control/huge-folder-ignore.ts +1 src/main/git/source-control/ignores/huge-folder-ignore.ts 5 src/main/git/repositories/discovery/repo-detection.ts 13 src/main/git/repositories/discovery/repo-git-marker-scan.ts 3 src/main/git/worktrees/catalog/worktree-git-path.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 0070140e8b6..592b553b375 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -1686,16 +1686,15 @@ "src/main/git/command-runner/git-capability-state.test.ts": 60, "src/main/git/command-runner/git-environment/git-network-safety-real-git.test.ts": 378, "src/shared/git/status/git-status-read-lease-owner.test.ts": 49, - "src/main/git/source-control/git-upstream-status-read-owner.test.ts": 45, "src/main/git/command-runner/git-worktree-mutation-state.test.ts": 74, "src/main/git/remotes/hosted-remote-url.test.ts": 43, - "src/main/git/source-control/huge-folder-ignore.test.ts": 58, + "src/main/git/source-control/ignores/huge-folder-ignore.test.ts": 58, "src/main/git/objects/local-git-object-quarantine.test.ts": 108, "src/main/git/refs/maintenance/local-repo-ref-maintenance-wsl-stopped.test.ts": 271, "src/main/git/refs/maintenance/local-repo-ref-maintenance.test.ts": 339, "src/main/git/objects/merge-tree-object-quarantine-real-git.test.ts": 2561, "src/main/git/refs/maintenance/pack-refs-lock-ownership.test.ts": 59, - "src/main/git/source-control/porcelain-v1-records.test.ts": 138, + "src/main/git/source-control/status/porcelain-v1-records.test.ts": 138, "src/main/git/remotes/remote-name-listing.test.ts": 398, "src/main/git/remotes/remote-ref-probe-cache.test.ts": 414, "src/main/git/remotes/remote.test.ts": 352, @@ -1731,38 +1730,38 @@ "src/main/git/command-runner/host-routing/runner-wsl-login-shell-capture.test.ts": 222, "src/main/git/command-runner/git-environment/runner-wsl-network-ssh-policy.test.ts": 260, "src/main/git/repositories/cloning/saved-clone-target.test.ts": 890, - "src/main/git/source-control/blob-absence-real-git.test.ts": 629, - "src/main/git/source-control/complete-merge-real-git.test.ts": 958, - "src/main/git/source-control/diff-textconv-real-git.test.ts": 520, - "src/main/git/source-control/diff-too-large-real-git.test.ts": 446, - "src/main/git/source-control/drive-checkout-suspect-paths.test.ts": 49, + "src/main/git/source-control/diffs/blob-absence-real-git.test.ts": 629, + "src/main/git/source-control/mutations/complete-merge-real-git.test.ts": 958, + "src/main/git/source-control/diffs/diff-textconv-real-git.test.ts": 520, + "src/main/git/source-control/diffs/diff-too-large-real-git.test.ts": 446, + "src/main/git/source-control/drive-checkout/drive-checkout-suspect-paths.test.ts": 49, "src/main/git/source-control/drive-checkout-suspect-real-git.test.ts": 406, "src/main/git/source-control/drive-checkout-suspect-writes-real-git.test.ts": 691, - "src/main/git/source-control/stage-worktree-changes.test.ts": 1737, - "src/main/git/source-control/staged-rename-diff-real-git.test.ts": 362, - "src/main/git/source-control/staging-discard-index-safety.test.ts": 689, - "src/main/git/source-control/submodule-gitlink-pathspec-real-git.test.ts": 337, - "src/main/git/source-control/submodule-restore-recurse-real-git.test.ts": 398, - "src/main/git/source-control/status-branch-compare-real-ref.test.ts": 593, + "src/main/git/source-control/mutations/stage-worktree-changes.test.ts": 1737, + "src/main/git/source-control/diffs/staged-rename-diff-real-git.test.ts": 362, + "src/main/git/source-control/mutations/staging-discard-index-safety.test.ts": 689, + "src/main/git/source-control/submodules/submodule-gitlink-pathspec-real-git.test.ts": 337, + "src/main/git/source-control/submodules/submodule-restore-recurse-real-git.test.ts": 398, + "src/main/git/source-control/comparisons/status-branch-compare-real-ref.test.ts": 593, "src/main/git/source-control/status-branch-compare.test.ts": 251, - "src/main/git/source-control/status-branch-line-total-exec-contract.test.ts": 1033, - "src/main/git/source-control/status-branch-line-total-real-git.test.ts": 785, - "src/main/git/source-control/status-branch-line-total-untracked-base.test.ts": 700, - "src/main/git/source-control/status-cquoted-paths.test.ts": 348, - "src/main/git/source-control/status-diff.test.ts": 327, + "src/main/git/source-control/status/status-branch-line-total-exec-contract.test.ts": 1033, + "src/main/git/source-control/status/status-branch-line-total-real-git.test.ts": 785, + "src/main/git/source-control/status/status-branch-line-total-untracked-base.test.ts": 700, + "src/main/git/source-control/status/status-cquoted-paths.test.ts": 348, + "src/main/git/source-control/diffs/status-diff.test.ts": 327, "src/main/git/source-control/status-discard-and-bulk-staging.test.ts": 289, - "src/main/git/source-control/status-discard-symlink.test.ts": 456, - "src/main/git/source-control/status-line-stats-host-paths.test.ts": 77, - "src/main/git/source-control/status-line-stats-renames-real-git.test.ts": 631, - "src/main/git/source-control/status-pathspec-literals.test.ts": 442, + "src/main/git/source-control/mutations/status-discard-symlink.test.ts": 456, + "src/main/git/source-control/status/status-line-stats-host-paths.test.ts": 77, + "src/main/git/source-control/status/status-line-stats-renames-real-git.test.ts": 631, + "src/main/git/source-control/mutations/status-pathspec-literals.test.ts": 442, "src/shared/git/status/git-status-porcelain-parser.test.ts": 52, "src/main/git/source-control/status-read-coalescing.test.ts": 258, - "src/main/git/source-control/status-read-failure-real-git.test.ts": 307, - "src/main/git/source-control/status-shared-symlinks.test.ts": 746, - "src/main/git/source-control/status-submodule.test.ts": 306, - "src/main/git/source-control/status-upstream-ref.test.ts": 52, - "src/main/git/source-control/upstream-deferred-fork-remote-real.test.ts": 319, - "src/main/git/source-control/upstream.test.ts": 406, + "src/main/git/source-control/status/status-read-failure-real-git.test.ts": 307, + "src/main/git/source-control/status/status-shared-symlinks.test.ts": 746, + "src/main/git/source-control/submodules/status-submodule.test.ts": 306, + "src/main/git/source-control/upstream/status-upstream-ref.test.ts": 52, + "src/main/git/source-control/upstream/upstream-deferred-fork-remote-real.test.ts": 319, + "src/main/git/source-control/upstream/upstream.test.ts": 406, "src/main/git/worktrees/creation/worktree-add-local-base-refresh.test.ts": 362, "src/main/git/worktrees/creation/worktree-add-pending-removal.test.ts": 346, "src/main/git/worktrees/creation/worktree-add-remote-branch-upstream-real-git.test.ts": 786, @@ -7026,7 +7025,6 @@ "tests/e2e/task-page-linear-refresh-authority.unit.test.ts": 5548, "tests/e2e/terminal-perf-presentation.unit.test.ts": 60, "tests/e2e/worktree-status-warning.unit.test.ts": 2331, - "tests/tools/branch-line-total-renderer.test.mjs": 1222, "tests/tools/omp-close-lifecycle.test.mjs": 403, "tests/tools/omp-config-root-relay-shell.test.mjs": 297, "tests/tools/relay-bench/phone-e2ee-desktop-parity.test.mjs": 111, diff --git a/config/scripts/performance/source-control/git-diff-blob-concurrency-benchmark.mjs b/config/scripts/performance/source-control/git-diff-blob-concurrency-benchmark.mjs index 094372b3162..744c126a09f 100644 --- a/config/scripts/performance/source-control/git-diff-blob-concurrency-benchmark.mjs +++ b/config/scripts/performance/source-control/git-diff-blob-concurrency-benchmark.mjs @@ -70,7 +70,7 @@ const parent = `${head}~1` // Span file sizes to cover both fixed spawn and size-dependent read costs. const CANDIDATES = [ - 'src/main/git/source-control/file-diff.ts', + 'src/main/git/source-control/diffs/file-diff.ts', 'src/shared/agent-hook-listener/normalization/normalize-hook-payload.ts', 'src/renderer/src/components/task-page/TaskPage.tsx' ] diff --git a/src/main/git/source-control/branch-change-entries.ts b/src/main/git/source-control/comparisons/branch-change-entries.ts similarity index 71% rename from src/main/git/source-control/branch-change-entries.ts rename to src/main/git/source-control/comparisons/branch-change-entries.ts index dd3061a02f3..4bc55d51912 100644 --- a/src/main/git/source-control/branch-change-entries.ts +++ b/src/main/git/source-control/comparisons/branch-change-entries.ts @@ -1,12 +1,12 @@ -import type { GitBranchChangeEntry } from '../../../shared/git/diff/git-diff-compare-types' -import { gitChangeListArgs, parseGitChangeList } from '../../../shared/git/diff/git-change-list' +import type { GitBranchChangeEntry } from '../../../../shared/git/diff/git-diff-compare-types' +import { gitChangeListArgs, parseGitChangeList } from '../../../../shared/git/diff/git-change-list' import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' -import { MAX_GIT_SHOW_BYTES } from './git-show-max-bytes' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' +import { MAX_GIT_SHOW_BYTES } from '../git-show-max-bytes' export async function loadBranchChanges( worktreePath: string, diff --git a/src/main/git/source-control/branch-change-list-cache-real-git.test.ts b/src/main/git/source-control/comparisons/branch-change-list-cache-real-git.test.ts similarity index 100% rename from src/main/git/source-control/branch-change-list-cache-real-git.test.ts rename to src/main/git/source-control/comparisons/branch-change-list-cache-real-git.test.ts diff --git a/src/main/git/source-control/branch-change-list-cache.ts b/src/main/git/source-control/comparisons/branch-change-list-cache.ts similarity index 87% rename from src/main/git/source-control/branch-change-list-cache.ts rename to src/main/git/source-control/comparisons/branch-change-list-cache.ts index c0bd508277c..0b53be1bc81 100644 --- a/src/main/git/source-control/branch-change-list-cache.ts +++ b/src/main/git/source-control/comparisons/branch-change-list-cache.ts @@ -1,13 +1,13 @@ -import type { GitBranchChangeEntry } from '../../../shared/git/diff/git-diff-compare-types' -import { gitChangeListArgs } from '../../../shared/git/diff/git-change-list' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' +import type { GitBranchChangeEntry } from '../../../../shared/git/diff/git-diff-compare-types' +import { gitChangeListArgs } from '../../../../shared/git/diff/git-change-list' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' import { loadBranchChanges } from './branch-change-entries' import type { CommitPairScope } from './commit-pair-cache' -import { readStampWithinDeadline, type StampProbes } from './deadline-bounded-stamp' -import { addGitattributesStampPaths } from './gitattributes-stamp-paths' -import { SettledDiffCache, type SettledDiffCacheLimits } from './settled-diff-cache' -import { readStampedGitConfig } from './stamped-git-config' -import { readWorktreePathsDiffStamp } from './worktree-diff-stamp' +import { readStampWithinDeadline, type StampProbes } from '../input-stamps/deadline-bounded-stamp' +import { addGitattributesStampPaths } from '../input-stamps/gitattributes-stamp-paths' +import { SettledDiffCache, type SettledDiffCacheLimits } from '../settled-diff-cache' +import { readStampedGitConfig } from '../input-stamps/stamped-git-config' +import { readWorktreePathsDiffStamp } from '../input-stamps/worktree-diff-stamp' type CachedBranchChanges = { entries: GitBranchChangeEntry[] diff --git a/src/main/git/source-control/branch-compare.ts b/src/main/git/source-control/comparisons/branch-compare.ts similarity index 91% rename from src/main/git/source-control/branch-compare.ts rename to src/main/git/source-control/comparisons/branch-compare.ts index b4afdc7bff0..09d1cb9e16c 100644 --- a/src/main/git/source-control/branch-compare.ts +++ b/src/main/git/source-control/comparisons/branch-compare.ts @@ -1,11 +1,11 @@ import type { GitBranchCompareResult, GitBranchCompareSummary -} from '../../../shared/git/diff/git-diff-compare-types' -import { readBranchCompareHead } from '../../../shared/git/branches/git-branch-compare-head' -import { resolveWorktreeAddBaseRef } from '../../../shared/worktree/base-ref' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' -import { resolveWorktreeBaseCommitOid } from '../worktrees/creation/worktree-base-ref-probe' +} from '../../../../shared/git/diff/git-diff-compare-types' +import { readBranchCompareHead } from '../../../../shared/git/branches/git-branch-compare-head' +import { resolveWorktreeAddBaseRef } from '../../../../shared/worktree/base-ref' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' +import { resolveWorktreeBaseCommitOid } from '../../worktrees/creation/worktree-base-ref-probe' import { loadBranchChangesThroughCache } from './branch-change-list-cache' import { commitPairDivergences, diff --git a/src/main/git/source-control/branch-diff.ts b/src/main/git/source-control/comparisons/branch-diff.ts similarity index 76% rename from src/main/git/source-control/branch-diff.ts rename to src/main/git/source-control/comparisons/branch-diff.ts index fc83e142efa..087e893d97d 100644 --- a/src/main/git/source-control/branch-diff.ts +++ b/src/main/git/source-control/comparisons/branch-diff.ts @@ -1,10 +1,10 @@ -import type { GitDiffResult } from '../../../shared/git/diff/git-diff-compare-types' -import { stableInFlightKey } from '../../../shared/in-flight-promise-dedupe' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' -import { gitRuntimeOptionsKey } from './git-runtime-options-cache-key' -import { gitDiffReadDedupe } from './git-read-cache-invalidation' -import { buildDiffResult } from './diff-result' -import { blobConversion, readGitBlobAtOidPath } from './git-blob-read' +import type { GitDiffResult } from '../../../../shared/git/diff/git-diff-compare-types' +import { stableInFlightKey } from '../../../../shared/in-flight-promise-dedupe' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' +import { gitRuntimeOptionsKey } from '../git-runtime-options-cache-key' +import { gitDiffReadDedupe } from '../git-read-cache-invalidation' +import { buildDiffResult } from '../diffs/diff-result' +import { blobConversion, readGitBlobAtOidPath } from '../diffs/git-blob-read' export async function getBranchDiff( worktreePath: string, diff --git a/src/main/git/source-control/commit-compare.ts b/src/main/git/source-control/comparisons/commit-compare.ts similarity index 84% rename from src/main/git/source-control/commit-compare.ts rename to src/main/git/source-control/comparisons/commit-compare.ts index ad2e3615183..c4af6dce1e6 100644 --- a/src/main/git/source-control/commit-compare.ts +++ b/src/main/git/source-control/comparisons/commit-compare.ts @@ -1,11 +1,11 @@ -import type { GitCommitCompareResult } from '../../../shared/git/diff/git-diff-compare-types' -import { parseGitRevListFirstParentOid } from '../../../shared/git/refs/git-rev-list-output' +import type { GitCommitCompareResult } from '../../../../shared/git/diff/git-diff-compare-types' +import { parseGitRevListFirstParentOid } from '../../../../shared/git/refs/git-rev-list-output' import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' import { loadCommitChanges } from './branch-change-entries' import { resolveRefOid } from './compare-ref-oids' diff --git a/src/main/git/source-control/commit-diff.ts b/src/main/git/source-control/comparisons/commit-diff.ts similarity index 77% rename from src/main/git/source-control/commit-diff.ts rename to src/main/git/source-control/comparisons/commit-diff.ts index 6ffbf8ec524..bb113202b0d 100644 --- a/src/main/git/source-control/commit-diff.ts +++ b/src/main/git/source-control/comparisons/commit-diff.ts @@ -1,10 +1,14 @@ -import type { GitDiffResult } from '../../../shared/git/diff/git-diff-compare-types' -import { stableInFlightKey } from '../../../shared/in-flight-promise-dedupe' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' -import { gitRuntimeOptionsKey } from './git-runtime-options-cache-key' -import { gitDiffReadDedupe } from './git-read-cache-invalidation' -import { buildDiffResult } from './diff-result' -import { blobConversion, readGitBlobAtOidPath, type GitBlobReadResult } from './git-blob-read' +import type { GitDiffResult } from '../../../../shared/git/diff/git-diff-compare-types' +import { stableInFlightKey } from '../../../../shared/in-flight-promise-dedupe' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' +import { gitRuntimeOptionsKey } from '../git-runtime-options-cache-key' +import { gitDiffReadDedupe } from '../git-read-cache-invalidation' +import { buildDiffResult } from '../diffs/diff-result' +import { + blobConversion, + readGitBlobAtOidPath, + type GitBlobReadResult +} from '../diffs/git-blob-read' export async function getCommitDiff( worktreePath: string, diff --git a/src/main/git/source-control/commit-pair-cache.ts b/src/main/git/source-control/comparisons/commit-pair-cache.ts similarity index 93% rename from src/main/git/source-control/commit-pair-cache.ts rename to src/main/git/source-control/comparisons/commit-pair-cache.ts index 60df2fb643f..28099258fd5 100644 --- a/src/main/git/source-control/commit-pair-cache.ts +++ b/src/main/git/source-control/comparisons/commit-pair-cache.ts @@ -1,9 +1,12 @@ import { readFile, stat } from 'node:fs/promises' import * as path from 'node:path' -import { resolveWorktreeHostPath } from '../../../shared/git/metadata/git-metadata-path' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' -import { resolveGitCommonDir, resolveGitDir } from './resolve-git-dir' -import { canProveUnchangedByStamp, readFileStampComponent } from './worktree-diff-stamp' +import { resolveWorktreeHostPath } from '../../../../shared/git/metadata/git-metadata-path' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' +import { resolveGitCommonDir, resolveGitDir } from '../resolve-git-dir' +import { + canProveUnchangedByStamp, + readFileStampComponent +} from '../input-stamps/worktree-diff-stamp' /** Opaque object-store identity under which a commit pair's graph answers stay valid. */ export type CommitPairScope = string diff --git a/src/main/git/source-control/compare-ref-oids.ts b/src/main/git/source-control/comparisons/compare-ref-oids.ts similarity index 93% rename from src/main/git/source-control/compare-ref-oids.ts rename to src/main/git/source-control/comparisons/compare-ref-oids.ts index a21a21df521..4cb30419015 100644 --- a/src/main/git/source-control/compare-ref-oids.ts +++ b/src/main/git/source-control/comparisons/compare-ref-oids.ts @@ -1,8 +1,8 @@ import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' export async function resolveCompareRef( worktreePath: string, diff --git a/src/main/git/source-control/status-branch-compare-real-ref.test.ts b/src/main/git/source-control/comparisons/status-branch-compare-real-ref.test.ts similarity index 100% rename from src/main/git/source-control/status-branch-compare-real-ref.test.ts rename to src/main/git/source-control/comparisons/status-branch-compare-real-ref.test.ts diff --git a/src/main/git/source-control/blob-absence-real-git.test.ts b/src/main/git/source-control/diffs/blob-absence-real-git.test.ts similarity index 95% rename from src/main/git/source-control/blob-absence-real-git.test.ts rename to src/main/git/source-control/diffs/blob-absence-real-git.test.ts index 840c9de4fe3..3f42e48c372 100644 --- a/src/main/git/source-control/blob-absence-real-git.test.ts +++ b/src/main/git/source-control/diffs/blob-absence-real-git.test.ts @@ -6,10 +6,10 @@ import * as path from 'node:path' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { runProcess } from '@orca/process-host' import { readGitBlobAtIndexPath, readGitBlobAtOidPath } from './git-blob-read' -import { getCommitDiff } from './commit-diff' +import { getCommitDiff } from '../comparisons/commit-diff' import { getDiff } from './file-diff' -import { invalidateGitReadCaches, settledDiffCache } from './git-read-cache-invalidation' -import { readWorktreeDiffStamp } from './worktree-diff-stamp' +import { invalidateGitReadCaches, settledDiffCache } from '../git-read-cache-invalidation' +import { readWorktreeDiffStamp } from '../input-stamps/worktree-diff-stamp' function gitInit(dir: string): void { execFileSync('git', ['init'], { cwd: dir, stdio: 'pipe' }) diff --git a/src/main/git/source-control/diff-result.ts b/src/main/git/source-control/diffs/diff-result.ts similarity index 87% rename from src/main/git/source-control/diff-result.ts rename to src/main/git/source-control/diffs/diff-result.ts index 99a7a758e8d..c52c3d1b6e7 100644 --- a/src/main/git/source-control/diff-result.ts +++ b/src/main/git/source-control/diffs/diff-result.ts @@ -1,9 +1,9 @@ -import type { GitDiffResult } from '../../../shared/git/diff/git-diff-compare-types' -import { getLargeDiffRenderLimit } from '../../../shared/large-diff-render-limit' +import type { GitDiffResult } from '../../../../shared/git/diff/git-diff-compare-types' +import { getLargeDiffRenderLimit } from '../../../../shared/large-diff-render-limit' import { IMAGE_AND_PDF_PREVIEW_MIME_TYPES, filePreviewExtension -} from '../../../shared/file-preview/file-preview-kind' +} from '../../../../shared/file-preview/file-preview-kind' export function buildDiffResult( originalContent: string, diff --git a/src/main/git/source-control/diff-textconv-real-git.test.ts b/src/main/git/source-control/diffs/diff-textconv-real-git.test.ts similarity index 98% rename from src/main/git/source-control/diff-textconv-real-git.test.ts rename to src/main/git/source-control/diffs/diff-textconv-real-git.test.ts index 682654ada0d..b2e7b1fdc1d 100644 --- a/src/main/git/source-control/diff-textconv-real-git.test.ts +++ b/src/main/git/source-control/diffs/diff-textconv-real-git.test.ts @@ -5,7 +5,7 @@ import { tmpdir } from 'node:os' import * as path from 'node:path' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { getDiff } from './file-diff' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' describe('diff drivers with textconv', () => { let repo: string diff --git a/src/main/git/source-control/diff-too-large-real-git.test.ts b/src/main/git/source-control/diffs/diff-too-large-real-git.test.ts similarity index 92% rename from src/main/git/source-control/diff-too-large-real-git.test.ts rename to src/main/git/source-control/diffs/diff-too-large-real-git.test.ts index 1dc261a3bd8..7ec2bce486a 100644 --- a/src/main/git/source-control/diff-too-large-real-git.test.ts +++ b/src/main/git/source-control/diffs/diff-too-large-real-git.test.ts @@ -5,8 +5,8 @@ import { tmpdir } from 'node:os' import * as path from 'node:path' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { getDiff } from './file-diff' -import { MAX_GIT_SHOW_BYTES } from './git-show-max-bytes' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' +import { MAX_GIT_SHOW_BYTES } from '../git-show-max-bytes' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' function git(dir: string, args: string[]): void { execFileSync('git', args, { cwd: dir, stdio: 'pipe' }) diff --git a/src/main/git/source-control/file-diff.ts b/src/main/git/source-control/diffs/file-diff.ts similarity index 91% rename from src/main/git/source-control/file-diff.ts rename to src/main/git/source-control/diffs/file-diff.ts index 9cac0c93646..c573b2dea75 100644 --- a/src/main/git/source-control/file-diff.ts +++ b/src/main/git/source-control/diffs/file-diff.ts @@ -1,11 +1,11 @@ import * as path from 'node:path' -import type { GitDiffResult } from '../../../shared/git/diff/git-diff-compare-types' -import { resolveWorktreeHostPath } from '../../../shared/git/metadata/git-metadata-path' -import { stableInFlightKey } from '../../../shared/in-flight-promise-dedupe' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' -import { gitRuntimeOptionsKey } from './git-runtime-options-cache-key' -import { gitDiffReadDedupe, settledDiffCache } from './git-read-cache-invalidation' -import { readWorktreeDiffStamp } from './worktree-diff-stamp' +import type { GitDiffResult } from '../../../../shared/git/diff/git-diff-compare-types' +import { resolveWorktreeHostPath } from '../../../../shared/git/metadata/git-metadata-path' +import { stableInFlightKey } from '../../../../shared/in-flight-promise-dedupe' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' +import { gitRuntimeOptionsKey } from '../git-runtime-options-cache-key' +import { gitDiffReadDedupe, settledDiffCache } from '../git-read-cache-invalidation' +import { readWorktreeDiffStamp } from '../input-stamps/worktree-diff-stamp' import { buildDiffResult } from './diff-result' import { blobConversion, @@ -19,13 +19,16 @@ import { findContainingSubmodule, listSubmodulePaths, resolveSubmoduleWorktreePath -} from './submodule-paths' +} from '../submodules/submodule-paths' import { readGitlinkOidFromIndex, readGitlinkOidFromTree, readWorkingSubmoduleHead -} from './submodule-gitlink-oid' -import { buildSubmoduleInnerCommitRangeDiff, buildSubmodulePointerDiff } from './submodule-diff' +} from '../submodules/submodule-gitlink-oid' +import { + buildSubmoduleInnerCommitRangeDiff, + buildSubmodulePointerDiff +} from '../submodules/submodule-diff' type FileDiffOptions = GitRuntimeOptions & { oldPath?: string } diff --git a/src/main/git/source-control/git-blob-read.ts b/src/main/git/source-control/diffs/git-blob-read.ts similarity index 91% rename from src/main/git/source-control/git-blob-read.ts rename to src/main/git/source-control/diffs/git-blob-read.ts index ebf0ae9f467..c24d392627d 100644 --- a/src/main/git/source-control/git-blob-read.ts +++ b/src/main/git/source-control/diffs/git-blob-read.ts @@ -1,16 +1,16 @@ import { readFile, stat } from 'node:fs/promises' -import { isBinaryBuffer } from '../../../shared/file-preview/binary-buffer' -import { isMissingGitBlobPath } from '../../../shared/git/diff/git-blob-absence' -import { readGitCommandFailureStderr } from '../../../shared/git/commands/git-command-failure-text' +import { isBinaryBuffer } from '../../../../shared/file-preview/binary-buffer' +import { isMissingGitBlobPath } from '../../../../shared/git/diff/git-blob-absence' +import { readGitCommandFailureStderr } from '../../../../shared/git/commands/git-command-failure-text' import { type GitRuntimeOptions, gitReadOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsyncBuffer } from '../command-runner/git-exec-file' -import { isMaxBufferOverflowError } from '../command-runner/process-capture/max-buffer-overflow' -import { MAX_GIT_SHOW_BYTES } from './git-show-max-bytes' -import { literalPathspec } from './git-pathspec' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsyncBuffer } from '../../command-runner/git-exec-file' +import { isMaxBufferOverflowError } from '../../command-runner/process-capture/max-buffer-overflow' +import { MAX_GIT_SHOW_BYTES } from '../git-show-max-bytes' +import { literalPathspec } from '../git-pathspec' import { hasTextconvDriver, isImageOrPdfPreviewPath } from './textconv-driver' export type GitBlobReadResult = { diff --git a/src/main/git/source-control/staged-rename-diff-real-git.test.ts b/src/main/git/source-control/diffs/staged-rename-diff-real-git.test.ts similarity index 96% rename from src/main/git/source-control/staged-rename-diff-real-git.test.ts rename to src/main/git/source-control/diffs/staged-rename-diff-real-git.test.ts index 19625b87676..64693542a73 100644 --- a/src/main/git/source-control/staged-rename-diff-real-git.test.ts +++ b/src/main/git/source-control/diffs/staged-rename-diff-real-git.test.ts @@ -4,7 +4,7 @@ import * as path from 'node:path' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { runProcess } from '@orca/process-host' import { getDiff } from './file-diff' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' const OLD_PATH = 'old.txt' const NEW_PATH = 'new.txt' diff --git a/src/main/git/source-control/status-diff.test.ts b/src/main/git/source-control/diffs/status-diff.test.ts similarity index 79% rename from src/main/git/source-control/status-diff.test.ts rename to src/main/git/source-control/diffs/status-diff.test.ts index 3529b005198..6ff2d0d7c95 100644 --- a/src/main/git/source-control/status-diff.test.ts +++ b/src/main/git/source-control/diffs/status-diff.test.ts @@ -1,8 +1,8 @@ -import type * as GitExecFileModule from '../command-runner/git-exec-file' -import type * as GitStreamStdoutModule from '../command-runner/git-stream-stdout' -import type * as GitProcessEnvModule from '../command-runner/git-environment/git-process-env' +import type * as GitExecFileModule from '../../command-runner/git-exec-file' +import type * as GitStreamStdoutModule from '../../command-runner/git-stream-stdout' +import type * as GitProcessEnvModule from '../../command-runner/git-environment/git-process-env' import { beforeEach, describe, expect, it, vi } from 'vitest' -import type * as BoundedFileReader from '../../../shared/file-content/node-bounded-file-reader' +import type * as BoundedFileReader from '../../../../shared/file-content/node-bounded-file-reader' import path from 'node:path' import { @@ -35,17 +35,17 @@ const { existsSyncMock: vi.fn() })) -vi.mock('../command-runner/git-exec-file', async (importOriginal) => ({ +vi.mock('../../command-runner/git-exec-file', async (importOriginal) => ({ ...(await importOriginal()), ...createGitExecFileModuleMock({ gitExecFileAsyncMock, gitExecFileAsyncBufferMock }) })) -vi.mock('../command-runner/git-stream-stdout', async (importOriginal) => ({ +vi.mock('../../command-runner/git-stream-stdout', async (importOriginal) => ({ ...(await importOriginal()), ...createGitStreamStdoutModuleMock({ gitExecFileAsyncMock, gitStreamOptionsMock }) })) -vi.mock('../command-runner/git-environment/git-process-env', async (importOriginal) => ({ +vi.mock('../../command-runner/git-environment/git-process-env', async (importOriginal) => ({ ...(await importOriginal()), ...createGitProcessEnvModuleMock() })) @@ -58,7 +58,7 @@ vi.mock('fs', () => ({ existsSync: existsSyncMock })) -vi.mock('../../../shared/file-content/node-bounded-file-reader', async (importOriginal) => +vi.mock('../../../../shared/file-content/node-bounded-file-reader', async (importOriginal) => createBoundedFileReaderModuleMock(await importOriginal(), { readFileMock, statMock diff --git a/src/main/git/source-control/status-test-harness.ts b/src/main/git/source-control/diffs/status-test-harness.ts similarity index 96% rename from src/main/git/source-control/status-test-harness.ts rename to src/main/git/source-control/diffs/status-test-harness.ts index 3d62cb019a2..41ce6eca20b 100644 --- a/src/main/git/source-control/status-test-harness.ts +++ b/src/main/git/source-control/diffs/status-test-harness.ts @@ -1,5 +1,5 @@ import type { Mock } from 'vitest' -import type * as BoundedFileReader from '../../../shared/file-content/node-bounded-file-reader' +import type * as BoundedFileReader from '../../../../shared/file-content/node-bounded-file-reader' type MockFn = Mock diff --git a/src/main/git/source-control/textconv-driver.ts b/src/main/git/source-control/diffs/textconv-driver.ts similarity index 88% rename from src/main/git/source-control/textconv-driver.ts rename to src/main/git/source-control/diffs/textconv-driver.ts index 1007804cc59..f784b6ff127 100644 --- a/src/main/git/source-control/textconv-driver.ts +++ b/src/main/git/source-control/diffs/textconv-driver.ts @@ -1,13 +1,13 @@ import { IMAGE_AND_PDF_PREVIEW_MIME_TYPES, filePreviewExtension -} from '../../../shared/file-preview/file-preview-kind' +} from '../../../../shared/file-preview/file-preview-kind' import { type GitRuntimeOptions, gitReadOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' /** Image/PDF diffs preview the stored bytes, so a diff driver must not replace them. */ export function isImageOrPdfPreviewPath(filePath: string): boolean { diff --git a/src/main/git/source-control/drive-checkout-suspect-paths.test.ts b/src/main/git/source-control/drive-checkout/drive-checkout-suspect-paths.test.ts similarity index 100% rename from src/main/git/source-control/drive-checkout-suspect-paths.test.ts rename to src/main/git/source-control/drive-checkout/drive-checkout-suspect-paths.test.ts diff --git a/src/main/git/source-control/drive-checkout-suspect-paths.ts b/src/main/git/source-control/drive-checkout/drive-checkout-suspect-paths.ts similarity index 98% rename from src/main/git/source-control/drive-checkout-suspect-paths.ts rename to src/main/git/source-control/drive-checkout/drive-checkout-suspect-paths.ts index b328329568c..a88799829a9 100644 --- a/src/main/git/source-control/drive-checkout-suspect-paths.ts +++ b/src/main/git/source-control/drive-checkout/drive-checkout-suspect-paths.ts @@ -1,5 +1,5 @@ import * as path from 'node:path' -import { isWindowsAbsolutePathLike } from '../../../shared/cross-platform-path' +import { isWindowsAbsolutePathLike } from '../../../../shared/cross-platform-path' // Minimal checkStat can miss same-size writes preserving whole-second mtime; recheck watcher paths with default stat. diff --git a/src/main/git/source-control/drive-checkout-suspect-status.ts b/src/main/git/source-control/drive-checkout/drive-checkout-suspect-status.ts similarity index 89% rename from src/main/git/source-control/drive-checkout-suspect-status.ts rename to src/main/git/source-control/drive-checkout/drive-checkout-suspect-status.ts index 58eff81613f..6367f5bbd7f 100644 --- a/src/main/git/source-control/drive-checkout-suspect-status.ts +++ b/src/main/git/source-control/drive-checkout/drive-checkout-suspect-status.ts @@ -1,24 +1,24 @@ import * as path from 'node:path' import { lstat } from 'node:fs/promises' -import type { GitStatusEntry } from '../../../shared/git/status/git-status-types' -import { StatusPorcelainParser } from '../../../shared/git/status/git-status-porcelain-parser' -import { isWindowsAbsolutePathLike } from '../../../shared/cross-platform-path' -import { mapWithConcurrency } from '../../../shared/map-with-concurrency' +import type { GitStatusEntry } from '../../../../shared/git/status/git-status-types' +import { StatusPorcelainParser } from '../../../../shared/git/status/git-status-porcelain-parser' +import { isWindowsAbsolutePathLike } from '../../../../shared/cross-platform-path' +import { mapWithConcurrency } from '../../../../shared/map-with-concurrency' import { findCoveringLocalWatchRoot, observeLocalWatcherEvents -} from '../../ipc/filesystem/watchers/local-watcher-event-observers' -import { gitOptionalLocksDisabledEnv } from '../command-runner/git-environment/git-process-env' -import { gitStreamStdout } from '../command-runner/git-stream-stdout' -import { usesHostGitForWslLinkedWorktree } from '../command-runner/host-routing/wsl-linked-worktree-git-routing' +} from '../../../ipc/filesystem/watchers/local-watcher-event-observers' +import { gitOptionalLocksDisabledEnv } from '../../command-runner/git-environment/git-process-env' +import { gitStreamStdout } from '../../command-runner/git-stream-stdout' +import { usesHostGitForWslLinkedWorktree } from '../../command-runner/host-routing/wsl-linked-worktree-git-routing' import { withWslWindowsCheckoutGitConfig, wslWindowsCheckoutKey -} from '../command-runner/host-routing/wsl-windows-checkout-git-config' +} from '../../command-runner/host-routing/wsl-windows-checkout-git-config' import { DriveCheckoutSuspectPaths } from './drive-checkout-suspect-paths' -import { bulkPathspecCommands } from './git-pathspec' -import type { GetStatusOptions } from './get-status-options' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' +import { bulkPathspecCommands } from '../git-pathspec' +import type { GetStatusOptions } from '../status/get-status-options' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' // Why: the later `-c` overrides the injected `core.checkStat=minimal` for just these paths. const TARGETED_STATUS_ARGS = [ diff --git a/src/main/git/source-control/drive-checkout-suspect-writes.ts b/src/main/git/source-control/drive-checkout/drive-checkout-suspect-writes.ts similarity index 92% rename from src/main/git/source-control/drive-checkout-suspect-writes.ts rename to src/main/git/source-control/drive-checkout/drive-checkout-suspect-writes.ts index 2c6a6dae98d..3976b88d0b0 100644 --- a/src/main/git/source-control/drive-checkout-suspect-writes.ts +++ b/src/main/git/source-control/drive-checkout/drive-checkout-suspect-writes.ts @@ -1,10 +1,10 @@ import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' import { driveCheckoutSuspectsWithin } from './drive-checkout-suspect-status' -import { bulkPathspecCommands } from './git-pathspec' +import { bulkPathspecCommands } from '../git-pathspec' /** The write a path operation redoes for its suspects; pathspecs are appended after `--`. */ export type DriveCheckoutSuspectWrite = diff --git a/src/main/git/source-control/git-read-cache-invalidation.ts b/src/main/git/source-control/git-read-cache-invalidation.ts index ded54b4e7ed..1d226037f6c 100644 --- a/src/main/git/source-control/git-read-cache-invalidation.ts +++ b/src/main/git/source-control/git-read-cache-invalidation.ts @@ -8,13 +8,13 @@ import { } from '../../../shared/git/status/git-status-line-stats-cache' import { invalidateGitBranchLineTotalInFlight } from '../../../shared/git/status/git-branch-line-total' import { GitStatusReadLeaseOwner } from '../../../shared/git/status/git-status-read-lease-owner' -import { invalidateGitUpstreamStatusReads } from './upstream' -import { clearSubmodulePathsCache } from './submodule-paths' -import { resolvedUpstreamNameCache } from './resolved-upstream-name-cache' +import { invalidateGitUpstreamStatusReads } from './upstream/upstream' +import { clearSubmodulePathsCache } from './submodules/submodule-paths' +import { resolvedUpstreamNameCache } from './upstream/resolved-upstream-name-cache' import { getStatusLineStatsCacheKey, isStatusKeyForWorktreePath -} from './status-branch-line-total-input' +} from './status/status-branch-line-total-input' import { FILE_DIFF_CACHE_LIMITS, NUMSTAT_CACHE_LIMITS, diff --git a/src/main/git/source-control/git-upstream-status-read-owner.test.ts b/src/main/git/source-control/git-upstream-status-read-owner.test.ts deleted file mode 100644 index c044983529d..00000000000 --- a/src/main/git/source-control/git-upstream-status-read-owner.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import type { GitUpstreamStatus } from '../../../shared/git/status/git-status-types' -import type { GitPushTarget } from '../../../shared/worktree/types' -import { - GitUpstreamStatusReadOwner, - type GitUpstreamStatusExecutionIdentity -} from './git-upstream-status-read-owner' - -const upstreamStatus: GitUpstreamStatus = { - hasUpstream: true, - upstreamName: 'origin/main', - ahead: 1, - behind: 0 -} - -function deferredPromise(): { - promise: Promise - resolve: (value: T) => void - reject: (error: unknown) => void -} { - let resolve!: (value: T) => void - let reject!: (error: unknown) => void - const promise = new Promise((innerResolve, innerReject) => { - resolve = innerResolve - reject = innerReject - }) - return { promise, resolve, reject } -} - -describe('GitUpstreamStatusReadOwner', () => { - it('shares one live read across ten identical callers', async () => { - const owner = new GitUpstreamStatusReadOwner() - const pending = deferredPromise() - const load = vi.fn(() => pending.promise) - - const reads = Array.from({ length: 10 }, () => - owner.read({ kind: 'native' }, '/repo', undefined, load) - ) - await Promise.resolve() - - expect(load).toHaveBeenCalledTimes(1) - pending.resolve(upstreamStatus) - await expect(Promise.all(reads)).resolves.toEqual( - Array.from({ length: 10 }, () => upstreamStatus) - ) - }) - - it('isolates paths, execution hosts, and every explicit target field', async () => { - const owner = new GitUpstreamStatusReadOwner() - const load = vi.fn().mockResolvedValue(upstreamStatus) - const baseTarget: GitPushTarget = { - remoteName: 'fork', - branchName: 'feature' - } - const cases: [GitUpstreamStatusExecutionIdentity, string, GitPushTarget | undefined][] = [ - [{ kind: 'native' }, '/repo-a', undefined], - [{ kind: 'native' }, '/repo-b', undefined], - [{ kind: 'wsl', distro: 'Ubuntu' }, '/repo-a', undefined], - [{ kind: 'wsl', distro: 'Debian' }, '/repo-a', undefined], - [{ kind: 'native' }, '/repo-a', baseTarget], - [{ kind: 'native' }, '/repo-a', { ...baseTarget, remoteName: 'origin' }], - [{ kind: 'native' }, '/repo-a', { ...baseTarget, branchName: 'other' }], - [ - { kind: 'native' }, - '/repo-a', - { ...baseTarget, remoteUrl: 'https://github.com/example/fork.git' } - ], - [{ kind: 'native' }, '/repo-a', { ...baseTarget, remoteCreated: false }], - [{ kind: 'native' }, '/repo-a', { ...baseTarget, remoteCreated: true }], - [{ kind: 'ssh-provider' }, '/repo-a', baseTarget] - ] - - await Promise.all( - cases.map(([identity, worktreePath, target]) => - owner.read(identity, worktreePath, target, load) - ) - ) - - expect(load).toHaveBeenCalledTimes(cases.length) - }) - - it('runs fresh work after success and rejection', async () => { - const owner = new GitUpstreamStatusReadOwner() - const failure = new Error('upstream failed') - const load = vi - .fn<() => Promise>() - .mockResolvedValueOnce(upstreamStatus) - .mockRejectedValueOnce(failure) - .mockResolvedValueOnce(upstreamStatus) - - await expect(owner.read({ kind: 'native' }, '/repo', undefined, load)).resolves.toBe( - upstreamStatus - ) - await expect(owner.read({ kind: 'native' }, '/repo', undefined, load)).rejects.toBe(failure) - await expect(owner.read({ kind: 'native' }, '/repo', undefined, load)).resolves.toBe( - upstreamStatus - ) - expect(load).toHaveBeenCalledTimes(3) - }) - - it('fences before, during, and after mutation generations', async () => { - const owner = new GitUpstreamStatusReadOwner() - const pendingReads = Array.from({ length: 3 }, () => deferredPromise()) - const load = vi.fn(() => pendingReads[load.mock.calls.length - 1].promise) - - const before = owner.read({ kind: 'native' }, '/repo', undefined, load) - await Promise.resolve() - owner.invalidate() - const during = owner.read({ kind: 'native' }, '/repo', undefined, load) - await Promise.resolve() - owner.invalidate() - const after = owner.read({ kind: 'native' }, '/repo', undefined, load) - await Promise.resolve() - - pendingReads.forEach((pending) => pending.resolve(upstreamStatus)) - await expect(Promise.all([before, during, after])).resolves.toEqual( - Array.from({ length: 3 }, () => upstreamStatus) - ) - expect(load).toHaveBeenCalledTimes(3) - }) -}) diff --git a/src/main/git/source-control/check-ignored-paths.ts b/src/main/git/source-control/ignores/check-ignored-paths.ts similarity index 88% rename from src/main/git/source-control/check-ignored-paths.ts rename to src/main/git/source-control/ignores/check-ignored-paths.ts index 1b38e17ea52..fea9b4c9a55 100644 --- a/src/main/git/source-control/check-ignored-paths.ts +++ b/src/main/git/source-control/ignores/check-ignored-paths.ts @@ -1,15 +1,15 @@ import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' import { encodeGitCheckIgnorePaths, GIT_CHECK_IGNORE_STDIN_ARGS, GIT_CHECK_IGNORE_TIMEOUT_MS, parseGitCheckIgnorePaths, splitGitCheckIgnorePathsByStdinBytes -} from '../../../shared/git/paths/git-check-ignore-stdio' +} from '../../../../shared/git/paths/git-check-ignore-stdio' type GitExecError = Error & { stdout?: string; code?: number | string } diff --git a/src/main/git/source-control/huge-folder-ignore.test.ts b/src/main/git/source-control/ignores/huge-folder-ignore.test.ts similarity index 100% rename from src/main/git/source-control/huge-folder-ignore.test.ts rename to src/main/git/source-control/ignores/huge-folder-ignore.test.ts diff --git a/src/main/git/source-control/huge-folder-ignore.ts b/src/main/git/source-control/ignores/huge-folder-ignore.ts similarity index 96% rename from src/main/git/source-control/huge-folder-ignore.ts rename to src/main/git/source-control/ignores/huge-folder-ignore.ts index 927785a48dc..7143bd79d4a 100644 --- a/src/main/git/source-control/huge-folder-ignore.ts +++ b/src/main/git/source-control/ignores/huge-folder-ignore.ts @@ -2,7 +2,7 @@ import { existsSync } from 'node:fs' import { appendFile, readFile, stat } from 'node:fs/promises' import * as path from 'node:path' import { checkIgnoredPaths } from './check-ignored-paths' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' // Offer common dependency/build folders when unignored content saturates status. const KNOWN_HUGE_FOLDER_NAMES = [ diff --git a/src/main/git/source-control/deadline-bounded-stamp.ts b/src/main/git/source-control/input-stamps/deadline-bounded-stamp.ts similarity index 82% rename from src/main/git/source-control/deadline-bounded-stamp.ts rename to src/main/git/source-control/input-stamps/deadline-bounded-stamp.ts index 4b83f7eb3bb..edaea098c72 100644 --- a/src/main/git/source-control/deadline-bounded-stamp.ts +++ b/src/main/git/source-control/input-stamps/deadline-bounded-stamp.ts @@ -1,5 +1,5 @@ -import { withTimeout } from '../../../shared/promise-timeout-fallback' -import { runCoalescedProbe, type CoalescedProbes } from '../remotes/coalesced-probe' +import { withTimeout } from '../../../../shared/promise-timeout-fallback' +import { runCoalescedProbe, type CoalescedProbes } from '../../remotes/coalesced-probe' import type { WorktreeDiffStamp } from './worktree-diff-stamp' // A dead home or network mount can stall stat; skip reuse after this instead of stalling the poll. diff --git a/src/main/git/source-control/gitattributes-stamp-paths.ts b/src/main/git/source-control/input-stamps/gitattributes-stamp-paths.ts similarity index 100% rename from src/main/git/source-control/gitattributes-stamp-paths.ts rename to src/main/git/source-control/input-stamps/gitattributes-stamp-paths.ts diff --git a/src/main/git/source-control/stamped-git-config.ts b/src/main/git/source-control/input-stamps/stamped-git-config.ts similarity index 93% rename from src/main/git/source-control/stamped-git-config.ts rename to src/main/git/source-control/input-stamps/stamped-git-config.ts index 52b50e9fcfd..6bfe9b7f22f 100644 --- a/src/main/git/source-control/stamped-git-config.ts +++ b/src/main/git/source-control/input-stamps/stamped-git-config.ts @@ -1,18 +1,18 @@ import { access } from 'node:fs/promises' import { homedir } from 'node:os' import * as path from 'node:path' -import { BoundedMap } from '../../../shared/bounded-map' -import { resolveWorktreeHostPath } from '../../../shared/git/metadata/git-metadata-path' -import { InFlightPromiseDedupe } from '../../../shared/in-flight-promise-dedupe' -import { iterateNulDelimitedFields } from '../../../shared/nul-delimited-fields' -import { wslDistroForCommand } from '../../../shared/wsl-paths' +import { BoundedMap } from '../../../../shared/bounded-map' +import { resolveWorktreeHostPath } from '../../../../shared/git/metadata/git-metadata-path' +import { InFlightPromiseDedupe } from '../../../../shared/in-flight-promise-dedupe' +import { iterateNulDelimitedFields } from '../../../../shared/nul-delimited-fields' +import { wslDistroForCommand } from '../../../../shared/wsl-paths' import { gitReadOptionsForWorktree, type GitRuntimeOptions -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' import { readStampWithinDeadline, type StampProbes } from './deadline-bounded-stamp' -import { resolveGitCommonDir, resolveGitDir } from './resolve-git-dir' +import { resolveGitCommonDir, resolveGitDir } from '../resolve-git-dir' import { canProveUnchangedByStamp, readHostFilesStamp, diff --git a/src/main/git/source-control/worktree-diff-stamp.ts b/src/main/git/source-control/input-stamps/worktree-diff-stamp.ts similarity index 97% rename from src/main/git/source-control/worktree-diff-stamp.ts rename to src/main/git/source-control/input-stamps/worktree-diff-stamp.ts index 6d1dc50cc89..dd97a49d001 100644 --- a/src/main/git/source-control/worktree-diff-stamp.ts +++ b/src/main/git/source-control/input-stamps/worktree-diff-stamp.ts @@ -1,8 +1,8 @@ import { readFile, stat } from 'node:fs/promises' import * as path from 'node:path' -import { resolveWorktreeHostPath } from '../../../shared/git/metadata/git-metadata-path' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' -import { resolveGitCommonDir, resolveGitDir } from './resolve-git-dir' +import { resolveWorktreeHostPath } from '../../../../shared/git/metadata/git-metadata-path' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' +import { resolveGitCommonDir, resolveGitDir } from '../resolve-git-dir' /** Stamp HEAD, index, routing metadata and working-file inputs without Git; null forbids caching. */ export type WorktreeDiffStamp = { diff --git a/src/main/git/source-control/commit-changes.ts b/src/main/git/source-control/mutations/commit-changes.ts similarity index 90% rename from src/main/git/source-control/commit-changes.ts rename to src/main/git/source-control/mutations/commit-changes.ts index 380e63b826b..cfb4fa6d454 100644 --- a/src/main/git/source-control/commit-changes.ts +++ b/src/main/git/source-control/mutations/commit-changes.ts @@ -1,10 +1,10 @@ import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' import { detectConflictOperation } from './git-conflict-operation' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' export function commitChanges( worktreePath: string, diff --git a/src/main/git/source-control/complete-merge-real-git.test.ts b/src/main/git/source-control/mutations/complete-merge-real-git.test.ts similarity index 100% rename from src/main/git/source-control/complete-merge-real-git.test.ts rename to src/main/git/source-control/mutations/complete-merge-real-git.test.ts diff --git a/src/main/git/source-control/discard-changes.ts b/src/main/git/source-control/mutations/discard-changes.ts similarity index 88% rename from src/main/git/source-control/discard-changes.ts rename to src/main/git/source-control/mutations/discard-changes.ts index 83c0713862c..e955663300a 100644 --- a/src/main/git/source-control/discard-changes.ts +++ b/src/main/git/source-control/mutations/discard-changes.ts @@ -2,18 +2,18 @@ import * as path from 'node:path' import { removeSafeUntrackedDiscardTarget, removeSafeUntrackedDiscardTargets -} from '../../../shared/git/paths/git-discard-path-safety' -import { partitionTrackedPathSpecs } from '../../../shared/git/paths/git-tracked-pathspecs' +} from '../../../../shared/git/paths/git-discard-path-safety' +import { partitionTrackedPathSpecs } from '../../../../shared/git/paths/git-tracked-pathspecs' import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' -import { bulkPathspecCommands, literalPathspec } from './git-pathspec' -import { encodeGitPathspecs } from '../../../shared/git/paths/git-pathspec-stdin' -import { redoWriteForDriveCheckoutSuspects } from './drive-checkout-suspect-writes' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' +import { bulkPathspecCommands, literalPathspec } from '../git-pathspec' +import { encodeGitPathspecs } from '../../../../shared/git/paths/git-pathspec-stdin' +import { redoWriteForDriveCheckoutSuspects } from '../drive-checkout/drive-checkout-suspect-writes' const DISCARD_WRITE = ['restore', '--worktree'] as const diff --git a/src/main/git/source-control/git-conflict-operation.ts b/src/main/git/source-control/mutations/git-conflict-operation.ts similarity index 85% rename from src/main/git/source-control/git-conflict-operation.ts rename to src/main/git/source-control/mutations/git-conflict-operation.ts index 549ea395f75..5a2dcade9f1 100644 --- a/src/main/git/source-control/git-conflict-operation.ts +++ b/src/main/git/source-control/mutations/git-conflict-operation.ts @@ -1,14 +1,14 @@ import { access } from 'node:fs/promises' import * as path from 'node:path' -import type { GitConflictOperation } from '../../../shared/git/status/git-status-types' +import type { GitConflictOperation } from '../../../../shared/git/status/git-status-types' import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' -import { runWithGitReadCacheInvalidation } from './git-read-cache-invalidation' -import { resolveGitDir } from './resolve-git-dir' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' +import { runWithGitReadCacheInvalidation } from '../git-read-cache-invalidation' +import { resolveGitDir } from '../resolve-git-dir' // HEAD detection races may yield unknown for one poll; rebase directories persist more reliably than REBASE_HEAD. export async function detectConflictOperation( diff --git a/src/main/git/source-control/stage-worktree-changes.test.ts b/src/main/git/source-control/mutations/stage-worktree-changes.test.ts similarity index 98% rename from src/main/git/source-control/stage-worktree-changes.test.ts rename to src/main/git/source-control/mutations/stage-worktree-changes.test.ts index 812439a0913..cf678ddb4a8 100644 --- a/src/main/git/source-control/stage-worktree-changes.test.ts +++ b/src/main/git/source-control/mutations/stage-worktree-changes.test.ts @@ -3,8 +3,8 @@ import { mkdir, mkdtemp, rm, symlink, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import * as path from 'node:path' import { afterEach, describe, expect, it } from 'vitest' -import { DEFAULT_GIT_STATUS_LIMIT } from '../../../shared/git/status/git-status-limit' -import { getStatus } from './status-read' +import { DEFAULT_GIT_STATUS_LIMIT } from '../../../../shared/git/status/git-status-limit' +import { getStatus } from '../status/status-read' import { bulkStageFiles, stageWorktreeChanges } from './staging' const tempRoots: string[] = [] diff --git a/src/main/git/source-control/staged-commit-context.ts b/src/main/git/source-control/mutations/staged-commit-context.ts similarity index 80% rename from src/main/git/source-control/staged-commit-context.ts rename to src/main/git/source-control/mutations/staged-commit-context.ts index 4979bf7ff13..fae2dcfe6ef 100644 --- a/src/main/git/source-control/staged-commit-context.ts +++ b/src/main/git/source-control/mutations/staged-commit-context.ts @@ -1,15 +1,15 @@ -import type { CommitMessageDraftContext } from '../../../shared/source-control/ai/generation/commit-message-generation' +import type { CommitMessageDraftContext } from '../../../../shared/source-control/ai/generation/commit-message-generation' import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' import { describeMaxBufferOverflowError, isMaxBufferOverflowError -} from '../command-runner/process-capture/max-buffer-overflow' -import { MAX_STAGED_COMMIT_CONTEXT_BYTES } from './git-show-max-bytes' +} from '../../command-runner/process-capture/max-buffer-overflow' +import { MAX_STAGED_COMMIT_CONTEXT_BYTES } from '../git-show-max-bytes' export async function getStagedCommitContext( worktreePath: string, diff --git a/src/main/git/source-control/staging-discard-index-safety.test.ts b/src/main/git/source-control/mutations/staging-discard-index-safety.test.ts similarity index 100% rename from src/main/git/source-control/staging-discard-index-safety.test.ts rename to src/main/git/source-control/mutations/staging-discard-index-safety.test.ts diff --git a/src/main/git/source-control/staging.ts b/src/main/git/source-control/mutations/staging.ts similarity index 86% rename from src/main/git/source-control/staging.ts rename to src/main/git/source-control/mutations/staging.ts index 6a190acf676..aa999cf5178 100644 --- a/src/main/git/source-control/staging.ts +++ b/src/main/git/source-control/mutations/staging.ts @@ -1,24 +1,24 @@ import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' -import { gitReportedPath, literalPathspec } from './git-pathspec' -import { encodeGitPathspecs } from '../../../shared/git/paths/git-pathspec-stdin' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' +import { gitReportedPath, literalPathspec } from '../git-pathspec' +import { encodeGitPathspecs } from '../../../../shared/git/paths/git-pathspec-stdin' import { getConflictResolutions, type ConflictResolution -} from '../../../shared/source-control/listing/source-control-entry-actions' -import { parseUnmergedEntry } from '../../../shared/git/status/git-status-conflict-entries' +} from '../../../../shared/source-control/listing/source-control-entry-actions' +import { parseUnmergedEntry } from '../../../../shared/git/status/git-status-conflict-entries' import { stageGitWorktreeScope, type GitStageWorktreeScope, type GitStageWorktreeScopeReceipt -} from '../../../shared/git/paths/git-stage-worktree-scope' -import { findExistingWorktreeSymlinkPaths } from '../worktrees/content-paths/worktree-symlink-detection' -import { redoWriteForDriveCheckoutSuspects } from './drive-checkout-suspect-writes' -import { runWithGitWorktreeOperationLock } from '../../../shared/git/worktrees/git-worktree-operation-lock' +} from '../../../../shared/git/paths/git-stage-worktree-scope' +import { findExistingWorktreeSymlinkPaths } from '../../worktrees/content-paths/worktree-symlink-detection' +import { redoWriteForDriveCheckoutSuspects } from '../drive-checkout/drive-checkout-suspect-writes' +import { runWithGitWorktreeOperationLock } from '../../../../shared/git/worktrees/git-worktree-operation-lock' const STAGE_WRITE = ['add', '--update'] as const diff --git a/src/main/git/source-control/status-discard-symlink.test.ts b/src/main/git/source-control/mutations/status-discard-symlink.test.ts similarity index 100% rename from src/main/git/source-control/status-discard-symlink.test.ts rename to src/main/git/source-control/mutations/status-discard-symlink.test.ts diff --git a/src/main/git/source-control/status-pathspec-literals.test.ts b/src/main/git/source-control/mutations/status-pathspec-literals.test.ts similarity index 100% rename from src/main/git/source-control/status-pathspec-literals.test.ts rename to src/main/git/source-control/mutations/status-pathspec-literals.test.ts diff --git a/src/main/git/source-control/settled-diff-cache.ts b/src/main/git/source-control/settled-diff-cache.ts index fa4d2546c9f..a1474d88e23 100644 --- a/src/main/git/source-control/settled-diff-cache.ts +++ b/src/main/git/source-control/settled-diff-cache.ts @@ -5,7 +5,7 @@ import { canProveUnchangedByStamp, isDiffStampClockSkewed, type WorktreeDiffStamp -} from './worktree-diff-stamp' +} from './input-stamps/worktree-diff-stamp' /** Reuse diffs only with matching input stamps; decline unknown, recent, failed or mutation-overlapped reads. */ diff --git a/src/main/git/source-control/get-status-options.ts b/src/main/git/source-control/status/get-status-options.ts similarity index 87% rename from src/main/git/source-control/get-status-options.ts rename to src/main/git/source-control/status/get-status-options.ts index 1e8287e7895..a781c7694e8 100644 --- a/src/main/git/source-control/get-status-options.ts +++ b/src/main/git/source-control/status/get-status-options.ts @@ -1,4 +1,4 @@ -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' export type GetStatusOptions = GitRuntimeOptions & { includeIgnored?: boolean diff --git a/src/main/git/source-control/porcelain-v1-records.test.ts b/src/main/git/source-control/status/porcelain-v1-records.test.ts similarity index 100% rename from src/main/git/source-control/porcelain-v1-records.test.ts rename to src/main/git/source-control/status/porcelain-v1-records.test.ts diff --git a/src/main/git/source-control/porcelain-v1-records.ts b/src/main/git/source-control/status/porcelain-v1-records.ts similarity index 100% rename from src/main/git/source-control/porcelain-v1-records.ts rename to src/main/git/source-control/status/porcelain-v1-records.ts diff --git a/src/main/git/source-control/status-branch-line-total-exec-contract.test.ts b/src/main/git/source-control/status/status-branch-line-total-exec-contract.test.ts similarity index 86% rename from src/main/git/source-control/status-branch-line-total-exec-contract.test.ts rename to src/main/git/source-control/status/status-branch-line-total-exec-contract.test.ts index c1e9f179989..0e7fe6e6207 100644 --- a/src/main/git/source-control/status-branch-line-total-exec-contract.test.ts +++ b/src/main/git/source-control/status/status-branch-line-total-exec-contract.test.ts @@ -1,13 +1,13 @@ -import type * as GitExecFileModule from '../command-runner/git-exec-file' -import type * as GitStreamStdoutModule from '../command-runner/git-stream-stdout' +import type * as GitExecFileModule from '../../command-runner/git-exec-file' +import type * as GitStreamStdoutModule from '../../command-runner/git-stream-stdout' import { execFileSync } from 'node:child_process' import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import * as path from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { invalidateGitBranchLineTotalInFlight } from '../../../shared/git/status/git-branch-line-total' +import { invalidateGitBranchLineTotalInFlight } from '../../../../shared/git/status/git-branch-line-total' -import type * as BranchLineTotal from '../../../shared/git/status/git-branch-line-total' +import type * as BranchLineTotal from '../../../../shared/git/status/git-branch-line-total' // Spy on real Git argv to validate opt-in and coalescing without stubbing results. const { gitExecCalls, execHooks, coalescerJoins } = vi.hoisted(() => ({ @@ -16,7 +16,7 @@ const { gitExecCalls, execHooks, coalescerJoins } = vi.hoisted(() => ({ coalescerJoins: { count: 0, onJoin: undefined as undefined | (() => void) } })) -vi.mock('../../../shared/git/status/git-branch-line-total', async (importOriginal) => { +vi.mock('../../../../shared/git/status/git-branch-line-total', async (importOriginal) => { const actual = await importOriginal() return { ...actual, @@ -32,7 +32,7 @@ vi.mock('../../../shared/git/status/git-branch-line-total', async (importOrigina } }) -vi.mock('../command-runner/git-exec-file', async (importOriginal) => { +vi.mock('../../command-runner/git-exec-file', async (importOriginal) => { const actual = await importOriginal() return { ...actual, @@ -48,7 +48,7 @@ vi.mock('../command-runner/git-exec-file', async (importOriginal) => { } }) -vi.mock('../command-runner/git-stream-stdout', async (importOriginal) => { +vi.mock('../../command-runner/git-stream-stdout', async (importOriginal) => { const actual = await importOriginal() return { ...actual, @@ -59,10 +59,10 @@ vi.mock('../command-runner/git-stream-stdout', async (importOriginal) => { } }) -import { clearEffectiveUpstreamStatusCacheForTests } from './effective-upstream-status-cache' -import { clearSubmodulePathsCacheForTests } from './submodule-paths' +import { clearEffectiveUpstreamStatusCacheForTests } from '../upstream/effective-upstream-status-cache' +import { clearSubmodulePathsCacheForTests } from '../submodules/submodule-paths' import { getStatus } from './status-read' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' const BOGUS_MERGE_BASE = 'deadbeef'.repeat(5) const NO_LINES = { added: 0, removed: 0 } @@ -181,18 +181,6 @@ describe('branch line total completeness', () => { }) }) -describe('branch line total merge-base gate', () => { - it('never leaks a flag-shaped merge base into any git argv', async () => { - const { repo } = await createFixtureRepo() - - await getStatus(repo, { branchLineTotalMergeBase: '--upload-pack=/tmp/evil' }) - - for (const args of gitExecCalls) { - expect(args).not.toContain('--upload-pack=/tmp/evil') - } - }) -}) - describe('branch line total exec budget', () => { it('shares one ranged diff across status passes the status lease cannot merge', async () => { const { repo, mergeBase } = await createFixtureRepo() diff --git a/src/main/git/source-control/status-branch-line-total-input.ts b/src/main/git/source-control/status/status-branch-line-total-input.ts similarity index 85% rename from src/main/git/source-control/status-branch-line-total-input.ts rename to src/main/git/source-control/status/status-branch-line-total-input.ts index d19e5494571..01fb2dcc89c 100644 --- a/src/main/git/source-control/status-branch-line-total-input.ts +++ b/src/main/git/source-control/status/status-branch-line-total-input.ts @@ -1,20 +1,20 @@ import type { GitStatusEntry, GitBranchLineTotal -} from '../../../shared/git/status/git-status-types' +} from '../../../../shared/git/status/git-status-types' import { computeGitBranchLineTotal, readGitBranchLineTotalMergeBaseParam, GIT_BRANCH_LINE_TOTAL_TIMEOUT_MS -} from '../../../shared/git/status/git-branch-line-total' +} from '../../../../shared/git/status/git-branch-line-total' import { type GitRuntimeOptions, gitReadOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' -import { gitOptionalLocksDisabledEnv } from '../command-runner/git-environment/git-process-env' -import { resolveWorktreeFilesystemPath } from './worktree-filesystem-path' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' +import { gitOptionalLocksDisabledEnv } from '../../command-runner/git-environment/git-process-env' +import { resolveWorktreeFilesystemPath } from '../worktree-filesystem-path' import type { GetStatusOptions } from './get-status-options' export function createBranchLineTotalInput( diff --git a/src/main/git/source-control/status-branch-line-total-real-git.test.ts b/src/main/git/source-control/status/status-branch-line-total-real-git.test.ts similarity index 96% rename from src/main/git/source-control/status-branch-line-total-real-git.test.ts rename to src/main/git/source-control/status/status-branch-line-total-real-git.test.ts index 464a5be1fb1..c0cf4420fb0 100644 --- a/src/main/git/source-control/status-branch-line-total-real-git.test.ts +++ b/src/main/git/source-control/status/status-branch-line-total-real-git.test.ts @@ -3,9 +3,9 @@ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import * as path from 'node:path' import { afterEach, beforeEach, describe, expect, it } from 'vitest' -import { invalidateGitBranchLineTotalInFlight } from '../../../shared/git/status/git-branch-line-total' +import { invalidateGitBranchLineTotalInFlight } from '../../../../shared/git/status/git-branch-line-total' import { getStatus } from './status-read' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' const tempRoots: string[] = [] diff --git a/src/main/git/source-control/status-branch-line-total-untracked-base.test.ts b/src/main/git/source-control/status/status-branch-line-total-untracked-base.test.ts similarity index 96% rename from src/main/git/source-control/status-branch-line-total-untracked-base.test.ts rename to src/main/git/source-control/status/status-branch-line-total-untracked-base.test.ts index ea242f4ae88..3afb62b5687 100644 --- a/src/main/git/source-control/status-branch-line-total-untracked-base.test.ts +++ b/src/main/git/source-control/status/status-branch-line-total-untracked-base.test.ts @@ -3,9 +3,9 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, expect, it } from 'vitest' -import { getBranchCompare } from './branch-compare' +import { getBranchCompare } from '../comparisons/branch-compare' import { getStatus } from './status-read' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' const roots: string[] = [] diff --git a/src/main/git/source-control/status-cquoted-paths.test.ts b/src/main/git/source-control/status/status-cquoted-paths.test.ts similarity index 96% rename from src/main/git/source-control/status-cquoted-paths.test.ts rename to src/main/git/source-control/status/status-cquoted-paths.test.ts index 55ff7ebe166..ac047cc18bf 100644 --- a/src/main/git/source-control/status-cquoted-paths.test.ts +++ b/src/main/git/source-control/status/status-cquoted-paths.test.ts @@ -4,7 +4,7 @@ import { tmpdir } from 'node:os' import * as path from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { getStatus } from './status-read' -import { stageFile } from './staging' +import { stageFile } from '../mutations/staging' const tempRoots: string[] = [] diff --git a/src/main/git/source-control/status-index-lock-wait.ts b/src/main/git/source-control/status/status-index-lock-wait.ts similarity index 89% rename from src/main/git/source-control/status-index-lock-wait.ts rename to src/main/git/source-control/status/status-index-lock-wait.ts index 7f7e52104fa..4a722241f5a 100644 --- a/src/main/git/source-control/status-index-lock-wait.ts +++ b/src/main/git/source-control/status/status-index-lock-wait.ts @@ -1,9 +1,9 @@ import { access, stat } from 'node:fs/promises' import * as path from 'node:path' -import { BoundedMap } from '../../../shared/bounded-map' -import { delayUnlessAborted } from '../../../shared/abort-aware-delay' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' -import { resolveGitDir } from './resolve-git-dir' +import { BoundedMap } from '../../../../shared/bounded-map' +import { delayUnlessAborted } from '../../../../shared/abort-aware-delay' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' +import { resolveGitDir } from '../resolve-git-dir' const INDEX_LOCK_POLL_MS = 50 // Commits and staging release the lock well within this; a crashed Git can leave it forever. diff --git a/src/main/git/source-control/status-line-stats-host-paths.test.ts b/src/main/git/source-control/status/status-line-stats-host-paths.test.ts similarity index 87% rename from src/main/git/source-control/status-line-stats-host-paths.test.ts rename to src/main/git/source-control/status/status-line-stats-host-paths.test.ts index fa775b98f3e..f2287dd53fa 100644 --- a/src/main/git/source-control/status-line-stats-host-paths.test.ts +++ b/src/main/git/source-control/status/status-line-stats-host-paths.test.ts @@ -2,9 +2,9 @@ import * as path from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type * as FsPromises from 'node:fs/promises' -import type * as BoundedFileReader from '../../../shared/file-content/node-bounded-file-reader' -import type { GitStatusEntry } from '../../../shared/git/status/git-status-types' -import { invalidateGitBranchLineTotalInFlight } from '../../../shared/git/status/git-branch-line-total' +import type * as BoundedFileReader from '../../../../shared/file-content/node-bounded-file-reader' +import type { GitStatusEntry } from '../../../../shared/git/status/git-status-types' +import { invalidateGitBranchLineTotalInFlight } from '../../../../shared/git/status/git-branch-line-total' const { lstatPaths, untrackedFiles } = vi.hoisted(() => ({ lstatPaths: [] as string[], @@ -33,7 +33,7 @@ vi.mock('node:fs/promises', async (importOriginal) => { } }) -vi.mock('../../../shared/file-content/node-bounded-file-reader', async (importOriginal) => { +vi.mock('../../../../shared/file-content/node-bounded-file-reader', async (importOriginal) => { const actual = await importOriginal() return { ...actual, @@ -49,18 +49,18 @@ vi.mock('../../../shared/file-content/node-bounded-file-reader', async (importOr const { gitExecFileAsyncMock } = vi.hoisted(() => ({ gitExecFileAsyncMock: vi.fn() })) -vi.mock('../command-runner/git-exec-file', async (importOriginal) => ({ +vi.mock('../../command-runner/git-exec-file', async (importOriginal) => ({ ...(await importOriginal>()), gitExecFileAsync: gitExecFileAsyncMock, gitExecFileAsyncBuffer: vi.fn() })) -vi.mock('../command-runner/git-environment/git-process-env', async (importOriginal) => ({ +vi.mock('../../command-runner/git-environment/git-process-env', async (importOriginal) => ({ ...(await importOriginal>()), gitOptionalLocksDisabledEnv: (env: NodeJS.ProcessEnv = process.env) => env })) -vi.mock('../command-runner/git-stream-stdout', async (importOriginal) => ({ +vi.mock('../../command-runner/git-stream-stdout', async (importOriginal) => ({ ...(await importOriginal>()), gitStreamStdout: vi.fn() })) diff --git a/src/main/git/source-control/status-line-stats-renames-real-git.test.ts b/src/main/git/source-control/status/status-line-stats-renames-real-git.test.ts similarity index 97% rename from src/main/git/source-control/status-line-stats-renames-real-git.test.ts rename to src/main/git/source-control/status/status-line-stats-renames-real-git.test.ts index 1107be7f120..578f7b29c7c 100644 --- a/src/main/git/source-control/status-line-stats-renames-real-git.test.ts +++ b/src/main/git/source-control/status/status-line-stats-renames-real-git.test.ts @@ -4,7 +4,7 @@ import { join } from 'node:path' import { runProcessSync } from '@orca/process-host' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { getStatus } from './status-read' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' let root: string let repo: string diff --git a/src/main/git/source-control/status-line-stats.ts b/src/main/git/source-control/status/status-line-stats.ts similarity index 86% rename from src/main/git/source-control/status-line-stats.ts rename to src/main/git/source-control/status/status-line-stats.ts index dbb9f365e25..23cb1d6edb5 100644 --- a/src/main/git/source-control/status-line-stats.ts +++ b/src/main/git/source-control/status/status-line-stats.ts @@ -1,23 +1,23 @@ -import type { GitStatusEntry } from '../../../shared/git/status/git-status-types' -import { collectGitStatusLineStatInputs } from '../../../shared/git/status/git-status-line-stat-inputs' +import type { GitStatusEntry } from '../../../../shared/git/status/git-status-types' +import { collectGitStatusLineStatInputs } from '../../../../shared/git/status/git-status-line-stat-inputs' import { applyLineStats, collectUntrackedAdditions, parseNumstat, type GitLineStats -} from '../../../shared/git/status/git-uncommitted-line-stats' +} from '../../../../shared/git/status/git-uncommitted-line-stats' import { type GitRuntimeOptions, gitReadOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' -import { gitOptionalLocksDisabledEnv } from '../command-runner/git-environment/git-process-env' -import { resolveWorktreeFilesystemPath } from './worktree-filesystem-path' -import { settledNumstatCache } from './git-read-cache-invalidation' -import { readStampedGitConfig, type StampedGitConfig } from './stamped-git-config' -import { addGitattributesStampPaths } from './gitattributes-stamp-paths' -import { readWorktreePathsDiffStamp } from './worktree-diff-stamp' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' +import { gitOptionalLocksDisabledEnv } from '../../command-runner/git-environment/git-process-env' +import { resolveWorktreeFilesystemPath } from '../worktree-filesystem-path' +import { settledNumstatCache } from '../git-read-cache-invalidation' +import { readStampedGitConfig, type StampedGitConfig } from '../input-stamps/stamped-git-config' +import { addGitattributesStampPaths } from '../input-stamps/gitattributes-stamp-paths' +import { readWorktreePathsDiffStamp } from '../input-stamps/worktree-diff-stamp' async function runNumstat( worktreePath: string, diff --git a/src/main/git/source-control/status-porcelain-stream.ts b/src/main/git/source-control/status/status-porcelain-stream.ts similarity index 93% rename from src/main/git/source-control/status-porcelain-stream.ts rename to src/main/git/source-control/status/status-porcelain-stream.ts index 0ad274bcb35..5b9821a299c 100644 --- a/src/main/git/source-control/status-porcelain-stream.ts +++ b/src/main/git/source-control/status/status-porcelain-stream.ts @@ -1,7 +1,7 @@ -import { BoundedMap } from '../../../shared/bounded-map' -import { StatusPorcelainParser } from '../../../shared/git/status/git-status-porcelain-parser' -import { gitOptionalLocksDisabledEnv } from '../command-runner/git-environment/git-process-env' -import { gitStreamStdout } from '../command-runner/git-stream-stdout' +import { BoundedMap } from '../../../../shared/bounded-map' +import { StatusPorcelainParser } from '../../../../shared/git/status/git-status-porcelain-parser' +import { gitOptionalLocksDisabledEnv } from '../../command-runner/git-environment/git-process-env' +import { gitStreamStdout } from '../../command-runner/git-stream-stdout' import type { GetStatusOptions } from './get-status-options' import { listUntrackedDirectoryFiles, readUntrackedScan } from './status-untracked-directories' diff --git a/src/main/git/source-control/status-read-failure-real-git.test.ts b/src/main/git/source-control/status/status-read-failure-real-git.test.ts similarity index 96% rename from src/main/git/source-control/status-read-failure-real-git.test.ts rename to src/main/git/source-control/status/status-read-failure-real-git.test.ts index f313c354d6e..7420477cbc2 100644 --- a/src/main/git/source-control/status-read-failure-real-git.test.ts +++ b/src/main/git/source-control/status/status-read-failure-real-git.test.ts @@ -4,7 +4,7 @@ import * as path from 'node:path' import { runProcessSync } from '@orca/process-host' import { afterEach, expect, it } from 'vitest' import { getStatus } from './status-read' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' let root: string | undefined diff --git a/src/main/git/source-control/status-read.ts b/src/main/git/source-control/status/status-read.ts similarity index 87% rename from src/main/git/source-control/status-read.ts rename to src/main/git/source-control/status/status-read.ts index 97b660ff577..243e158d761 100644 --- a/src/main/git/source-control/status-read.ts +++ b/src/main/git/source-control/status/status-read.ts @@ -3,40 +3,40 @@ import type { GitStatusEntry, GitStatusResult, GitUpstreamStatus -} from '../../../shared/git/status/git-status-types' -import { resolveGitStatusLimit } from '../../../shared/git/status/git-status-limit' -import { stableInFlightKey } from '../../../shared/in-flight-promise-dedupe' -import { beginGitStatusLineStatsCacheWrite } from '../../../shared/git/status/git-status-line-stats-write-token' +} from '../../../../shared/git/status/git-status-types' +import { resolveGitStatusLimit } from '../../../../shared/git/status/git-status-limit' +import { stableInFlightKey } from '../../../../shared/in-flight-promise-dedupe' +import { beginGitStatusLineStatsCacheWrite } from '../../../../shared/git/status/git-status-line-stats-write-token' import { clearGitStatusLineStatsCacheKey, reuseOrRecomputeGitStatusLineStats -} from '../../../shared/git/status/git-status-line-stats-cache' -import { resolveWorktreeHostPath } from '../../../shared/git/metadata/git-metadata-path' -import { readGitCommandFailureStderr } from '../../../shared/git/commands/git-command-failure-text' +} from '../../../../shared/git/status/git-status-line-stats-cache' +import { resolveWorktreeHostPath } from '../../../../shared/git/metadata/git-metadata-path' +import { readGitCommandFailureStderr } from '../../../../shared/git/commands/git-command-failure-text' import { readStatusPorcelain } from './status-porcelain-stream' import { waitForIndexLockRelease } from './status-index-lock-wait' import { findExistingWorktreeSymlinkPaths, getSafeRelativePath -} from '../worktrees/content-paths/worktree-symlink-detection' +} from '../../worktrees/content-paths/worktree-symlink-detection' import type { GetStatusOptions } from './get-status-options' -import { statusReadLeaseOwner } from './git-read-cache-invalidation' -import { detectConflictOperation } from './git-conflict-operation' -import { parseUnmergedEntry } from '../../../shared/git/status/git-status-conflict-entries' -import { getEffectiveUpstreamStatusCacheKey } from './effective-upstream-status-cache' +import { statusReadLeaseOwner } from '../git-read-cache-invalidation' +import { detectConflictOperation } from '../mutations/git-conflict-operation' +import { parseUnmergedEntry } from '../../../../shared/git/status/git-status-conflict-entries' +import { getEffectiveUpstreamStatusCacheKey } from '../upstream/effective-upstream-status-cache' import { getShortBranchName, readOrProbeEffectiveUpstreamStatus, shouldProbeEffectiveUpstreamStatus -} from './effective-upstream-status-probe' +} from '../upstream/effective-upstream-status-probe' import { attachLineStats } from './status-line-stats' import { createBranchLineTotalInput, getStatusLineStatsCacheKey } from './status-branch-line-total-input' -import { reconcileDriveCheckoutSuspects } from './drive-checkout-suspect-status' -import { markRecursiveSubmoduleRestores } from './submodule-restore-recurse' -import { withRepositoryGitCause } from '../repository-git-cause' +import { reconcileDriveCheckoutSuspects } from '../drive-checkout/drive-checkout-suspect-status' +import { markRecursiveSubmoduleRestores } from '../submodules/submodule-restore-recurse' +import { withRepositoryGitCause } from '../../repository-git-cause' export async function getStatus( worktreePath: string, diff --git a/src/main/git/source-control/status-shared-symlinks.test.ts b/src/main/git/source-control/status/status-shared-symlinks.test.ts similarity index 100% rename from src/main/git/source-control/status-shared-symlinks.test.ts rename to src/main/git/source-control/status/status-shared-symlinks.test.ts diff --git a/src/main/git/source-control/status-untracked-directories.ts b/src/main/git/source-control/status/status-untracked-directories.ts similarity index 94% rename from src/main/git/source-control/status-untracked-directories.ts rename to src/main/git/source-control/status/status-untracked-directories.ts index 18681b5a25b..bbaf6dc706d 100644 --- a/src/main/git/source-control/status-untracked-directories.ts +++ b/src/main/git/source-control/status/status-untracked-directories.ts @@ -1,8 +1,8 @@ -import { gitOptionalLocksDisabledEnv } from '../command-runner/git-environment/git-process-env' -import { gitStreamStdout } from '../command-runner/git-stream-stdout' -import { readStampedGitConfig } from './stamped-git-config' +import { gitOptionalLocksDisabledEnv } from '../../command-runner/git-environment/git-process-env' +import { gitStreamStdout } from '../../command-runner/git-stream-stdout' +import { readStampedGitConfig } from '../input-stamps/stamped-git-config' import type { GetStatusOptions } from './get-status-options' -import { bulkPathspecCommands } from './git-pathspec' +import { bulkPathspecCommands } from '../git-pathspec' /** * How to get every untracked file: `expand` lists configured-mode `? dir/` rows, `configured-all` needs nothing, diff --git a/src/main/git/source-control/status-submodule.test.ts b/src/main/git/source-control/submodules/status-submodule.test.ts similarity index 83% rename from src/main/git/source-control/status-submodule.test.ts rename to src/main/git/source-control/submodules/status-submodule.test.ts index fa2049f73c0..8dd28c57de6 100644 --- a/src/main/git/source-control/status-submodule.test.ts +++ b/src/main/git/source-control/submodules/status-submodule.test.ts @@ -1,8 +1,8 @@ -import type * as GitExecFileModule from '../command-runner/git-exec-file' -import type * as GitStreamStdoutModule from '../command-runner/git-stream-stdout' -import type * as GitProcessEnvModule from '../command-runner/git-environment/git-process-env' +import type * as GitExecFileModule from '../../command-runner/git-exec-file' +import type * as GitStreamStdoutModule from '../../command-runner/git-stream-stdout' +import type * as GitProcessEnvModule from '../../command-runner/git-environment/git-process-env' import { beforeEach, describe, expect, it, vi } from 'vitest' -import type * as BoundedFileReader from '../../../shared/file-content/node-bounded-file-reader' +import type * as BoundedFileReader from '../../../../shared/file-content/node-bounded-file-reader' import path from 'node:path' import { createBoundedFileReaderModuleMock, @@ -10,7 +10,7 @@ import { createGitExecFileModuleMock, createGitStreamStdoutModuleMock, createGitProcessEnvModuleMock -} from './status-test-harness' +} from '../diffs/status-test-harness' const { gitExecFileAsyncMock, @@ -34,17 +34,17 @@ const { existsSyncMock: vi.fn() })) -vi.mock('../command-runner/git-exec-file', async (importOriginal) => ({ +vi.mock('../../command-runner/git-exec-file', async (importOriginal) => ({ ...(await importOriginal()), ...createGitExecFileModuleMock({ gitExecFileAsyncMock, gitExecFileAsyncBufferMock }) })) -vi.mock('../command-runner/git-stream-stdout', async (importOriginal) => ({ +vi.mock('../../command-runner/git-stream-stdout', async (importOriginal) => ({ ...(await importOriginal()), ...createGitStreamStdoutModuleMock({ gitExecFileAsyncMock, gitStreamOptionsMock }) })) -vi.mock('../command-runner/git-environment/git-process-env', async (importOriginal) => ({ +vi.mock('../../command-runner/git-environment/git-process-env', async (importOriginal) => ({ ...(await importOriginal()), ...createGitProcessEnvModuleMock() })) @@ -57,14 +57,14 @@ vi.mock('fs', () => ({ existsSync: existsSyncMock })) -vi.mock('../../../shared/file-content/node-bounded-file-reader', async (importOriginal) => +vi.mock('../../../../shared/file-content/node-bounded-file-reader', async (importOriginal) => createBoundedFileReaderModuleMock(await importOriginal(), { readFileMock, statMock }) ) import { clearSubmodulePathsCacheForTests } from './submodule-paths' -import { getDiff } from './file-diff' +import { getDiff } from '../diffs/file-diff' describe('submodule diff routing', () => { const OLD_OID = 'a'.repeat(40) diff --git a/src/main/git/source-control/submodule-diff.ts b/src/main/git/source-control/submodules/submodule-diff.ts similarity index 91% rename from src/main/git/source-control/submodule-diff.ts rename to src/main/git/source-control/submodules/submodule-diff.ts index c3986eef75f..3e2ab128dac 100644 --- a/src/main/git/source-control/submodule-diff.ts +++ b/src/main/git/source-control/submodules/submodule-diff.ts @@ -1,7 +1,7 @@ -import type { GitDiffResult } from '../../../shared/git/diff/git-diff-compare-types' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' -import { buildDiffResult } from './diff-result' -import { type BlobConversion, blobConversion, readGitBlobAtOidPath } from './git-blob-read' +import type { GitDiffResult } from '../../../../shared/git/diff/git-diff-compare-types' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' +import { buildDiffResult } from '../diffs/diff-result' +import { type BlobConversion, blobConversion, readGitBlobAtOidPath } from '../diffs/git-blob-read' import { resolveSubmoduleWorktreePath } from './submodule-paths' import { readGitlinkOidFromIndex, diff --git a/src/main/git/source-control/submodule-gitlink-oid.ts b/src/main/git/source-control/submodules/submodule-gitlink-oid.ts similarity index 84% rename from src/main/git/source-control/submodule-gitlink-oid.ts rename to src/main/git/source-control/submodules/submodule-gitlink-oid.ts index b9272c04bba..25a70710ad2 100644 --- a/src/main/git/source-control/submodule-gitlink-oid.ts +++ b/src/main/git/source-control/submodules/submodule-gitlink-oid.ts @@ -1,10 +1,10 @@ import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' -import { gitOptionalLocksDisabledEnv } from '../command-runner/git-environment/git-process-env' -import { literalPathspec } from './git-pathspec' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' +import { gitOptionalLocksDisabledEnv } from '../../command-runner/git-environment/git-process-env' +import { literalPathspec } from '../git-pathspec' export async function readGitlinkOidFromTree( worktreePath: string, diff --git a/src/main/git/source-control/submodule-gitlink-pathspec-real-git.test.ts b/src/main/git/source-control/submodules/submodule-gitlink-pathspec-real-git.test.ts similarity index 98% rename from src/main/git/source-control/submodule-gitlink-pathspec-real-git.test.ts rename to src/main/git/source-control/submodules/submodule-gitlink-pathspec-real-git.test.ts index edbd5460520..0ecf2950474 100644 --- a/src/main/git/source-control/submodule-gitlink-pathspec-real-git.test.ts +++ b/src/main/git/source-control/submodules/submodule-gitlink-pathspec-real-git.test.ts @@ -3,7 +3,7 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { runProcess } from '@orca/process-host' import { afterEach, describe, expect, it } from 'vitest' -import { getDiff } from './file-diff' +import { getDiff } from '../diffs/file-diff' import { getSubmoduleStatus } from './submodule-status' const directories: string[] = [] diff --git a/src/main/git/source-control/submodule-paths.ts b/src/main/git/source-control/submodules/submodule-paths.ts similarity index 92% rename from src/main/git/source-control/submodule-paths.ts rename to src/main/git/source-control/submodules/submodule-paths.ts index 7a9fc27c153..b4a39cfdb99 100644 --- a/src/main/git/source-control/submodule-paths.ts +++ b/src/main/git/source-control/submodules/submodule-paths.ts @@ -3,11 +3,11 @@ import * as path from 'node:path' import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' -import { gitOptionalLocksDisabledEnv } from '../command-runner/git-environment/git-process-env' -import { gitRuntimeOptionsKey } from './git-runtime-options-cache-key' -import { iterateNulDelimitedFields } from '../../../shared/nul-delimited-fields' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' +import { gitOptionalLocksDisabledEnv } from '../../command-runner/git-environment/git-process-env' +import { gitRuntimeOptionsKey } from '../git-runtime-options-cache-key' +import { iterateNulDelimitedFields } from '../../../../shared/nul-delimited-fields' const SUBMODULE_PATHS_CACHE_TTL_MS = 5_000 export const MAX_SUBMODULE_PATHS_CACHE_ENTRIES = 512 diff --git a/src/main/git/source-control/submodule-restore-recurse-real-git.test.ts b/src/main/git/source-control/submodules/submodule-restore-recurse-real-git.test.ts similarity index 91% rename from src/main/git/source-control/submodule-restore-recurse-real-git.test.ts rename to src/main/git/source-control/submodules/submodule-restore-recurse-real-git.test.ts index 582c8b0fcdd..19534f1803b 100644 --- a/src/main/git/source-control/submodule-restore-recurse-real-git.test.ts +++ b/src/main/git/source-control/submodules/submodule-restore-recurse-real-git.test.ts @@ -3,9 +3,9 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { runProcess } from '@orca/process-host' import { afterEach, describe, expect, it, vi } from 'vitest' -import { canDiscardStatusEntry } from '../../../shared/source-control/listing/source-control-entry-actions' -import { discardChanges } from './discard-changes' -import { getStatus } from './status-read' +import { canDiscardStatusEntry } from '../../../../shared/source-control/listing/source-control-entry-actions' +import { discardChanges } from '../mutations/discard-changes' +import { getStatus } from '../status/status-read' const directories: string[] = [] diff --git a/src/main/git/source-control/submodule-restore-recurse.ts b/src/main/git/source-control/submodules/submodule-restore-recurse.ts similarity index 76% rename from src/main/git/source-control/submodule-restore-recurse.ts rename to src/main/git/source-control/submodules/submodule-restore-recurse.ts index 8b449ad0378..767163be0b0 100644 --- a/src/main/git/source-control/submodule-restore-recurse.ts +++ b/src/main/git/source-control/submodules/submodule-restore-recurse.ts @@ -1,11 +1,11 @@ -import type { GitStatusEntry } from '../../../shared/git/status/git-status-types' -import { isWorktreeUndiscardableGitlink } from '../../../shared/source-control/listing/source-control-discard-all-sequence' +import type { GitStatusEntry } from '../../../../shared/git/status/git-status-types' +import { isWorktreeUndiscardableGitlink } from '../../../../shared/source-control/listing/source-control-discard-all-sequence' import { type GitRuntimeOptions, gitReadOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' /** Plain `git restore` recurses into submodules when submodule.recurse is on, so those gitlinks stay discardable. */ export async function markRecursiveSubmoduleRestores( diff --git a/src/main/git/source-control/submodule-status.ts b/src/main/git/source-control/submodules/submodule-status.ts similarity index 84% rename from src/main/git/source-control/submodule-status.ts rename to src/main/git/source-control/submodules/submodule-status.ts index fb6466764b3..9c3809672e1 100644 --- a/src/main/git/source-control/submodule-status.ts +++ b/src/main/git/source-control/submodules/submodule-status.ts @@ -1,18 +1,21 @@ -import type { GitStatusEntry, GitStatusResult } from '../../../shared/git/status/git-status-types' +import type { + GitStatusEntry, + GitStatusResult +} from '../../../../shared/git/status/git-status-types' import { capGitStatusEntries, resolveGitStatusLimit -} from '../../../shared/git/status/git-status-limit' -import { gitChangeListArgs, parseGitChangeList } from '../../../shared/git/diff/git-change-list' +} from '../../../../shared/git/status/git-status-limit' +import { gitChangeListArgs, parseGitChangeList } from '../../../../shared/git/diff/git-change-list' import { type GitRuntimeOptions, gitOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' -import { gitOptionalLocksDisabledEnv } from '../command-runner/git-environment/git-process-env' -import type { GetStatusOptions } from './get-status-options' -import { getStatus } from './status-read' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' +import { gitOptionalLocksDisabledEnv } from '../../command-runner/git-environment/git-process-env' +import type { GetStatusOptions } from '../status/get-status-options' +import { getStatus } from '../status/status-read' import { resolveSubmoduleWorktreePath } from './submodule-paths' import { readGitlinkOidFromIndex, diff --git a/src/main/git/source-control/effective-upstream-status-cache.ts b/src/main/git/source-control/upstream/effective-upstream-status-cache.ts similarity index 95% rename from src/main/git/source-control/effective-upstream-status-cache.ts rename to src/main/git/source-control/upstream/effective-upstream-status-cache.ts index cd71a3bcd3f..5c8fee54c5d 100644 --- a/src/main/git/source-control/effective-upstream-status-cache.ts +++ b/src/main/git/source-control/upstream/effective-upstream-status-cache.ts @@ -1,6 +1,6 @@ -import type { GitUpstreamStatus } from '../../../shared/git/status/git-status-types' -import type { GitRuntimeOptions } from '../command-runner/git-runtime-options' -import { invalidateGitReadCaches } from './git-read-cache-invalidation' +import type { GitUpstreamStatus } from '../../../../shared/git/status/git-status-types' +import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' +import { invalidateGitReadCaches } from '../git-read-cache-invalidation' import { resolvedUpstreamNameCache } from './resolved-upstream-name-cache' const EFFECTIVE_UPSTREAM_NEGATIVE_CACHE_TTL_MS = 5 * 60_000 diff --git a/src/main/git/source-control/effective-upstream-status-probe.ts b/src/main/git/source-control/upstream/effective-upstream-status-probe.ts similarity index 92% rename from src/main/git/source-control/effective-upstream-status-probe.ts rename to src/main/git/source-control/upstream/effective-upstream-status-probe.ts index f40fe5d527f..e0122e2f08b 100644 --- a/src/main/git/source-control/effective-upstream-status-probe.ts +++ b/src/main/git/source-control/upstream/effective-upstream-status-probe.ts @@ -1,15 +1,15 @@ -import type { GitUpstreamStatus } from '../../../shared/git/status/git-status-types' +import type { GitUpstreamStatus } from '../../../../shared/git/status/git-status-types' import { getEffectiveGitUpstreamStatus, getGitUpstreamStatusForUpstreamName -} from '../../../shared/git/remotes/git-effective-upstream' -import { splitRemoteBranchName } from '../../../shared/git/remotes/git-remote-branch-name' +} from '../../../../shared/git/remotes/git-effective-upstream' +import { splitRemoteBranchName } from '../../../../shared/git/remotes/git-remote-branch-name' import { type GitRuntimeOptions, gitReadOptionsForWorktree -} from '../command-runner/git-runtime-options' -import { gitExecFileAsync } from '../command-runner/git-exec-file' +} from '../../command-runner/git-runtime-options' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' import { MAX_EFFECTIVE_UPSTREAM_NEGATIVE_CACHE_ENTRIES, effectiveUpstreamStatusInFlight, diff --git a/src/main/git/source-control/git-upstream-status-read-owner.ts b/src/main/git/source-control/upstream/git-upstream-status-read-owner.ts similarity index 82% rename from src/main/git/source-control/git-upstream-status-read-owner.ts rename to src/main/git/source-control/upstream/git-upstream-status-read-owner.ts index d1712cbd6be..a1a0f8affaf 100644 --- a/src/main/git/source-control/git-upstream-status-read-owner.ts +++ b/src/main/git/source-control/upstream/git-upstream-status-read-owner.ts @@ -1,6 +1,9 @@ -import type { GitUpstreamStatus } from '../../../shared/git/status/git-status-types' -import type { GitPushTarget } from '../../../shared/worktree/types' -import { InFlightPromiseDedupe, stableInFlightKey } from '../../../shared/in-flight-promise-dedupe' +import type { GitUpstreamStatus } from '../../../../shared/git/status/git-status-types' +import type { GitPushTarget } from '../../../../shared/worktree/types' +import { + InFlightPromiseDedupe, + stableInFlightKey +} from '../../../../shared/in-flight-promise-dedupe' /** Fix key order for JSON.stringify and make new push-target fields fail the exhaustive type check. */ function pushTargetKeyParts(pushTarget: GitPushTarget): readonly unknown[] { diff --git a/src/main/git/source-control/resolved-upstream-name-cache.ts b/src/main/git/source-control/upstream/resolved-upstream-name-cache.ts similarity index 100% rename from src/main/git/source-control/resolved-upstream-name-cache.ts rename to src/main/git/source-control/upstream/resolved-upstream-name-cache.ts diff --git a/src/main/git/source-control/status-upstream-ref.test.ts b/src/main/git/source-control/upstream/status-upstream-ref.test.ts similarity index 100% rename from src/main/git/source-control/status-upstream-ref.test.ts rename to src/main/git/source-control/upstream/status-upstream-ref.test.ts diff --git a/src/main/git/source-control/status-upstream-ref.ts b/src/main/git/source-control/upstream/status-upstream-ref.ts similarity index 93% rename from src/main/git/source-control/status-upstream-ref.ts rename to src/main/git/source-control/upstream/status-upstream-ref.ts index 304c14686e2..d5caf98f8d0 100644 --- a/src/main/git/source-control/status-upstream-ref.ts +++ b/src/main/git/source-control/upstream/status-upstream-ref.ts @@ -1,4 +1,4 @@ -import { isSafeGitRefName } from '../../../shared/git/status/git-status-upstream-ref' +import { isSafeGitRefName } from '../../../../shared/git/status/git-status-upstream-ref' type GitStatusUpstreamRefExec = ( args: string[], diff --git a/src/main/git/source-control/upstream-deferred-fork-remote-real.test.ts b/src/main/git/source-control/upstream/upstream-deferred-fork-remote-real.test.ts similarity index 96% rename from src/main/git/source-control/upstream-deferred-fork-remote-real.test.ts rename to src/main/git/source-control/upstream/upstream-deferred-fork-remote-real.test.ts index 6f59d317328..09ccaeb33e7 100644 --- a/src/main/git/source-control/upstream-deferred-fork-remote-real.test.ts +++ b/src/main/git/source-control/upstream/upstream-deferred-fork-remote-real.test.ts @@ -3,7 +3,7 @@ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' -import type { GitPushTarget } from '../../../shared/worktree/types' +import type { GitPushTarget } from '../../../../shared/worktree/types' import { getUpstreamStatus } from './upstream' // Real Git verifies missing-ref exit/stderr handling before a fork remote is materialized. diff --git a/src/main/git/source-control/upstream.test.ts b/src/main/git/source-control/upstream/upstream.test.ts similarity index 92% rename from src/main/git/source-control/upstream.test.ts rename to src/main/git/source-control/upstream/upstream.test.ts index 25dce884ec9..2f7ec01d798 100644 --- a/src/main/git/source-control/upstream.test.ts +++ b/src/main/git/source-control/upstream/upstream.test.ts @@ -4,7 +4,7 @@ const { gitExecFileAsyncMock } = vi.hoisted(() => ({ gitExecFileAsyncMock: vi.fn() })) -vi.mock('../command-runner/git-exec-file', async (importOriginal) => ({ +vi.mock('../../command-runner/git-exec-file', async (importOriginal) => ({ ...(await importOriginal>()), gitExecFileAsync: gitExecFileAsyncMock })) diff --git a/src/main/git/source-control/upstream.ts b/src/main/git/source-control/upstream/upstream.ts similarity index 81% rename from src/main/git/source-control/upstream.ts rename to src/main/git/source-control/upstream/upstream.ts index de6a1f7383a..12b67161489 100644 --- a/src/main/git/source-control/upstream.ts +++ b/src/main/git/source-control/upstream/upstream.ts @@ -1,16 +1,16 @@ -import type { GitUpstreamStatus } from '../../../shared/git/status/git-status-types' -import type { GitPushTarget } from '../../../shared/worktree/types' -import { upstreamOnlyCommitsArePatchEquivalent } from '../../../shared/git/remotes/git-upstream-status' +import type { GitUpstreamStatus } from '../../../../shared/git/status/git-status-types' +import type { GitPushTarget } from '../../../../shared/worktree/types' +import { upstreamOnlyCommitsArePatchEquivalent } from '../../../../shared/git/remotes/git-upstream-status' import { isNoUpstreamError, normalizeGitErrorMessage -} from '../../../shared/git/remotes/git-remote-error' -import { getEffectiveGitUpstreamStatus } from '../../../shared/git/remotes/git-effective-upstream' -import { getPublishTargetStatus } from '../../../shared/git/remotes/git-publish-target-status' -import { gitExecFileAsync } from '../command-runner/git-exec-file' -import { validateGitPushTarget } from '../remotes/push-target-validation' +} from '../../../../shared/git/remotes/git-remote-error' +import { getEffectiveGitUpstreamStatus } from '../../../../shared/git/remotes/git-effective-upstream' +import { getPublishTargetStatus } from '../../../../shared/git/remotes/git-publish-target-status' +import { gitExecFileAsync } from '../../command-runner/git-exec-file' +import { validateGitPushTarget } from '../../remotes/push-target-validation' import { nativeAndWslGitUpstreamStatusReadOwner } from './git-upstream-status-read-owner' -import type { GitAdmissionTier } from '../../../shared/rpc-contract/git-admission-tier-params' +import type { GitAdmissionTier } from '../../../../shared/rpc-contract/git-admission-tier-params' type GitExecOptions = { wslDistro?: string diff --git a/src/main/git/worktrees/checkout/branch-switch-real-git.test.ts b/src/main/git/worktrees/checkout/branch-switch-real-git.test.ts index 76766ae2343..e9fc493d439 100644 --- a/src/main/git/worktrees/checkout/branch-switch-real-git.test.ts +++ b/src/main/git/worktrees/checkout/branch-switch-real-git.test.ts @@ -6,7 +6,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { installRealWorkerThreadChildProcesses } from '../../../worker-thread-child-process/worker-thread-child-process-real.test-fixtures' import { switchBranchGuarded, type BranchSwitchDeps } from './branch-switch' import { checkoutBranch } from './checkout' -import { getStatus } from '../../source-control/status-read' +import { getStatus } from '../../source-control/status/status-read' let directory: string let initialHead: string diff --git a/src/main/git/worktrees/checkout/branch-switch-target-real-git.test.ts b/src/main/git/worktrees/checkout/branch-switch-target-real-git.test.ts index 47b7d4301da..3e28878ada8 100644 --- a/src/main/git/worktrees/checkout/branch-switch-target-real-git.test.ts +++ b/src/main/git/worktrees/checkout/branch-switch-target-real-git.test.ts @@ -8,7 +8,7 @@ import { installRealWorkerThreadChildProcesses } from '../../../worker-thread-ch import { switchBranchGuarded } from './branch-switch' import { BranchSwitchCollisionError } from './branch-switch-target' import { checkoutBranch, listLocalBranches } from './checkout' -import { getStatus } from '../../source-control/status-read' +import { getStatus } from '../../source-control/status/status-read' import { searchRemoteRefPage } from '../../refs/resolution/repo-remote-ref-search' let directory: string diff --git a/src/main/git/worktrees/checkout/commit-actions-real-git.test.ts b/src/main/git/worktrees/checkout/commit-actions-real-git.test.ts index c04e582fa38..868e0e0ec84 100644 --- a/src/main/git/worktrees/checkout/commit-actions-real-git.test.ts +++ b/src/main/git/worktrees/checkout/commit-actions-real-git.test.ts @@ -1,5 +1,5 @@ import './create-branch' -import { detectConflictOperation } from '../../source-control/git-conflict-operation' +import { detectConflictOperation } from '../../source-control/mutations/git-conflict-operation' import { chmod, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' diff --git a/src/main/git/worktrees/checkout/commit-actions.ts b/src/main/git/worktrees/checkout/commit-actions.ts index d964d0118c4..f62e8a7a274 100644 --- a/src/main/git/worktrees/checkout/commit-actions.ts +++ b/src/main/git/worktrees/checkout/commit-actions.ts @@ -1,6 +1,6 @@ import { readFile } from 'node:fs/promises' import { join } from 'node:path' -import { detectConflictOperation } from '../../source-control/git-conflict-operation' +import { detectConflictOperation } from '../../source-control/mutations/git-conflict-operation' import type { GitCommitAction, GitCommitActionResult diff --git a/src/main/git/worktrees/content-paths/worktree-include-file.ts b/src/main/git/worktrees/content-paths/worktree-include-file.ts index 3152fbae53c..7ed999e00ec 100644 --- a/src/main/git/worktrees/content-paths/worktree-include-file.ts +++ b/src/main/git/worktrees/content-paths/worktree-include-file.ts @@ -1,6 +1,6 @@ import { lstat, readFile } from 'node:fs/promises' import { isAbsolute, join } from 'node:path' -import { checkIgnoredPaths } from '../../source-control/check-ignored-paths' +import { checkIgnoredPaths } from '../../source-control/ignores/check-ignored-paths' import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' import { mapWithConcurrency } from '../../../../shared/map-with-concurrency' diff --git a/src/main/git/worktrees/content-paths/worktree-shared-directories.ts b/src/main/git/worktrees/content-paths/worktree-shared-directories.ts index 793e854f9f8..19414431f3e 100644 --- a/src/main/git/worktrees/content-paths/worktree-shared-directories.ts +++ b/src/main/git/worktrees/content-paths/worktree-shared-directories.ts @@ -1,6 +1,6 @@ import { stat } from 'node:fs/promises' import { join } from 'node:path' -import { checkIgnoredPaths } from '../../source-control/check-ignored-paths' +import { checkIgnoredPaths } from '../../source-control/ignores/check-ignored-paths' import type { GitRuntimeOptions } from '../../command-runner/git-runtime-options' import { loadHooks } from '../../../workspace-hooks/hooks' import type { Repo } from '../../../../shared/repo-types' diff --git a/src/main/ipc/filesystem/filesystem-git-commit-generation-handlers.ts b/src/main/ipc/filesystem/filesystem-git-commit-generation-handlers.ts index d11ac6319d3..f327e81233b 100644 --- a/src/main/ipc/filesystem/filesystem-git-commit-generation-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-commit-generation-handlers.ts @@ -12,7 +12,7 @@ import { type GenerateCommitMessageResult } from '../../text-generation/commit-message-text-generation' import { getCommitMessageModelDiscoveryHostKey } from '../../../shared/source-control/ai/generation/commit-message-host-key' -import { getStagedCommitContext } from '../../git/source-control/staged-commit-context' +import { getStagedCommitContext } from '../../git/source-control/mutations/staged-commit-context' import { SSH_HOST_NOT_SERVED_MESSAGE } from '../../providers/ssh-host-not-served-error' import { resolveRegisteredWorktreePath } from '../worktrees/removal/registered-worktree-roots-cache' import { getLocalGitOptionsForRegisteredWorktree } from '../local-worktree-runtime-options' diff --git a/src/main/ipc/filesystem/filesystem-git-commit-handlers.ts b/src/main/ipc/filesystem/filesystem-git-commit-handlers.ts index b2c270d8d7b..bb608f9f274 100644 --- a/src/main/ipc/filesystem/filesystem-git-commit-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-commit-handlers.ts @@ -2,7 +2,7 @@ import type { GitCommitAction } from '../../../shared/git/commands/git-commit-ac import { applyCommit, recoverSequencer } from '../../git/worktrees/checkout/commit-actions' import { createBranch } from '../../git/worktrees/checkout/create-branch' import { ipcMain } from 'electron' -import { commitChanges, completeMerge } from '../../git/source-control/commit-changes' +import { commitChanges, completeMerge } from '../../git/source-control/mutations/commit-changes' import { resolveRegisteredWorktreePath } from '../worktrees/removal/registered-worktree-roots-cache' import { getLocalGitOptionsForRegisteredWorktree } from '../local-worktree-runtime-options' import type { FilesystemHandlerContext } from './filesystem-handler-context' diff --git a/src/main/ipc/filesystem/filesystem-git-diff-handlers.ts b/src/main/ipc/filesystem/filesystem-git-diff-handlers.ts index e0b4f243bae..fc7a610e49c 100644 --- a/src/main/ipc/filesystem/filesystem-git-diff-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-diff-handlers.ts @@ -1,7 +1,7 @@ import { ipcMain } from 'electron' import type { GitDiffResult } from '../../../shared/git/diff/git-diff-compare-types' -import { getBranchDiff } from '../../git/source-control/branch-diff' -import { getCommitDiff } from '../../git/source-control/commit-diff' +import { getBranchDiff } from '../../git/source-control/comparisons/branch-diff' +import { getCommitDiff } from '../../git/source-control/comparisons/commit-diff' import { validateFullGitObjectId, validateGitRelativeFilePath diff --git a/src/main/ipc/filesystem/filesystem-git-index-handlers.ts b/src/main/ipc/filesystem/filesystem-git-index-handlers.ts index a86b2c674db..764faf455ef 100644 --- a/src/main/ipc/filesystem/filesystem-git-index-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-index-handlers.ts @@ -6,8 +6,11 @@ import { bulkStageFiles, bulkUnstageFiles, stageWorktreeChanges -} from '../../git/source-control/staging' -import { discardChanges, bulkDiscardChanges } from '../../git/source-control/discard-changes' +} from '../../git/source-control/mutations/staging' +import { + discardChanges, + bulkDiscardChanges +} from '../../git/source-control/mutations/discard-changes' import { resolveRegisteredWorktreePath } from '../worktrees/removal/registered-worktree-roots-cache' import { getLocalGitOptionsForRegisteredWorktree, diff --git a/src/main/ipc/filesystem/filesystem-git-status-handlers.ts b/src/main/ipc/filesystem/filesystem-git-status-handlers.ts index dc2199f2d70..57c484c0ac7 100644 --- a/src/main/ipc/filesystem/filesystem-git-status-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-status-handlers.ts @@ -7,24 +7,24 @@ import type { import type { GitDiffResult } from '../../../shared/git/diff/git-diff-compare-types' import type { GitStatusRefBindingRequest as GitStatusUpstreamRefWatchRequest } from '../worktrees/git/worktree-git-status-ref-watch' import type { GitAdmissionTier } from '../../../shared/rpc-contract/git-admission-tier-params' -import { getStatus } from '../../git/source-control/status-read' -import { getSubmoduleStatus } from '../../git/source-control/submodule-status' +import { getStatus } from '../../git/source-control/status/status-read' +import { getSubmoduleStatus } from '../../git/source-control/submodules/submodule-status' import { abortMerge, abortRebase, detectConflictOperation -} from '../../git/source-control/git-conflict-operation' -import { getDiff } from '../../git/source-control/file-diff' +} from '../../git/source-control/mutations/git-conflict-operation' +import { getDiff } from '../../git/source-control/diffs/file-diff' import { registerFilesystemGitHistoryHandlers } from './filesystem-git-history-handlers' import { isGitWorktreeMutationInFlight } from '../../git/command-runner/git-worktree-mutation-state' import type { GitMutationInFlight } from '../../../shared/git/commands/git-mutation-in-flight' -import { checkIgnoredPaths } from '../../git/source-control/check-ignored-paths' +import { checkIgnoredPaths } from '../../git/source-control/ignores/check-ignored-paths' import { readWorktreeAtRiskLocalFiles } from '../../git/worktrees/removal/worktree-at-risk-local-files' import type { WorktreeAtRiskLocalFiles } from '../../../shared/worktree/at-risk-local-files' import { appendFolderToGitignore, findKnownHugeFolderPathsToIgnore -} from '../../git/source-control/huge-folder-ignore' +} from '../../git/source-control/ignores/huge-folder-ignore' import { resolveRegisteredWorktreePath } from '../worktrees/removal/registered-worktree-roots-cache' import { validateGitRelativeFilePath } from './access/filesystem-path-containment' import { diff --git a/src/main/ipc/filesystem/filesystem-git-status-staging.test.ts b/src/main/ipc/filesystem/filesystem-git-status-staging.test.ts index 0c36980e9e5..33fb1a301a5 100644 --- a/src/main/ipc/filesystem/filesystem-git-status-staging.test.ts +++ b/src/main/ipc/filesystem/filesystem-git-status-staging.test.ts @@ -40,44 +40,44 @@ vi.mock( '../../crash-reporting/breadcrumbs/crash-breadcrumb-store', async () => (await import('./filesystem-test-harness')).crashBreadcrumbMock ) -vi.mock('../../git/source-control/commit-changes', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/commit-changes', async (importOriginal) => ({ ...(await importOriginal>()), commitChanges: (await import('./filesystem-test-harness')).commitChangesMock })) -vi.mock('../../git/source-control/status-read', async (importOriginal) => ({ +vi.mock('../../git/source-control/status/status-read', async (importOriginal) => ({ ...(await importOriginal>()), getStatus: (await import('./filesystem-test-harness')).getStatusMock })) -vi.mock('../../git/source-control/git-conflict-operation', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/git-conflict-operation', async (importOriginal) => ({ ...(await importOriginal>()), detectConflictOperation: (await import('./filesystem-test-harness')).detectConflictOperationMock, abortMerge: (await import('./filesystem-test-harness')).abortMergeMock, abortRebase: (await import('./filesystem-test-harness')).abortRebaseMock })) -vi.mock('../../git/source-control/file-diff', async (importOriginal) => ({ +vi.mock('../../git/source-control/diffs/file-diff', async (importOriginal) => ({ ...(await importOriginal>()), getDiff: (await import('./filesystem-test-harness')).getDiffMock })) -vi.mock('../../git/source-control/branch-compare', async (importOriginal) => ({ +vi.mock('../../git/source-control/comparisons/branch-compare', async (importOriginal) => ({ ...(await importOriginal>()), getBranchCompare: (await import('./filesystem-test-harness')).getBranchCompareMock })) -vi.mock('../../git/source-control/branch-diff', async (importOriginal) => ({ +vi.mock('../../git/source-control/comparisons/branch-diff', async (importOriginal) => ({ ...(await importOriginal>()), getBranchDiff: (await import('./filesystem-test-harness')).getBranchDiffMock })) -vi.mock('../../git/source-control/staged-commit-context', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/staged-commit-context', async (importOriginal) => ({ ...(await importOriginal>()), getStagedCommitContext: (await import('./filesystem-test-harness')).getStagedCommitContextMock })) -vi.mock('../../git/source-control/staging', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/staging', async (importOriginal) => ({ ...(await importOriginal>()), stageFile: (await import('./filesystem-test-harness')).stageFileMock, bulkStageFiles: (await import('./filesystem-test-harness')).bulkStageFilesMock, @@ -86,13 +86,13 @@ vi.mock('../../git/source-control/staging', async (importOriginal) => ({ bulkUnstageFiles: (await import('./filesystem-test-harness')).bulkUnstageFilesMock })) -vi.mock('../../git/source-control/discard-changes', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/discard-changes', async (importOriginal) => ({ ...(await importOriginal>()), bulkDiscardChanges: (await import('./filesystem-test-harness')).bulkDiscardChangesMock, discardChanges: (await import('./filesystem-test-harness')).discardChangesMock })) vi.mock( - '../../git/source-control/check-ignored-paths', + '../../git/source-control/ignores/check-ignored-paths', async () => (await import('./filesystem-test-harness')).gitIgnoredPathsMock ) diff --git a/src/main/ipc/filesystem/filesystem-pull-request-linked-issue-lifetime.test.ts b/src/main/ipc/filesystem/filesystem-pull-request-linked-issue-lifetime.test.ts index 7af180afb3a..86c73a66ca9 100644 --- a/src/main/ipc/filesystem/filesystem-pull-request-linked-issue-lifetime.test.ts +++ b/src/main/ipc/filesystem/filesystem-pull-request-linked-issue-lifetime.test.ts @@ -42,44 +42,44 @@ vi.mock( '../../crash-reporting/breadcrumbs/crash-breadcrumb-store', async () => (await import('./filesystem-test-harness')).crashBreadcrumbMock ) -vi.mock('../../git/source-control/commit-changes', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/commit-changes', async (importOriginal) => ({ ...(await importOriginal>()), commitChanges: (await import('./filesystem-test-harness')).commitChangesMock })) -vi.mock('../../git/source-control/status-read', async (importOriginal) => ({ +vi.mock('../../git/source-control/status/status-read', async (importOriginal) => ({ ...(await importOriginal>()), getStatus: (await import('./filesystem-test-harness')).getStatusMock })) -vi.mock('../../git/source-control/git-conflict-operation', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/git-conflict-operation', async (importOriginal) => ({ ...(await importOriginal>()), detectConflictOperation: (await import('./filesystem-test-harness')).detectConflictOperationMock, abortMerge: (await import('./filesystem-test-harness')).abortMergeMock, abortRebase: (await import('./filesystem-test-harness')).abortRebaseMock })) -vi.mock('../../git/source-control/file-diff', async (importOriginal) => ({ +vi.mock('../../git/source-control/diffs/file-diff', async (importOriginal) => ({ ...(await importOriginal>()), getDiff: (await import('./filesystem-test-harness')).getDiffMock })) -vi.mock('../../git/source-control/branch-compare', async (importOriginal) => ({ +vi.mock('../../git/source-control/comparisons/branch-compare', async (importOriginal) => ({ ...(await importOriginal>()), getBranchCompare: (await import('./filesystem-test-harness')).getBranchCompareMock })) -vi.mock('../../git/source-control/branch-diff', async (importOriginal) => ({ +vi.mock('../../git/source-control/comparisons/branch-diff', async (importOriginal) => ({ ...(await importOriginal>()), getBranchDiff: (await import('./filesystem-test-harness')).getBranchDiffMock })) -vi.mock('../../git/source-control/staged-commit-context', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/staged-commit-context', async (importOriginal) => ({ ...(await importOriginal>()), getStagedCommitContext: (await import('./filesystem-test-harness')).getStagedCommitContextMock })) -vi.mock('../../git/source-control/staging', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/staging', async (importOriginal) => ({ ...(await importOriginal>()), stageFile: (await import('./filesystem-test-harness')).stageFileMock, bulkStageFiles: (await import('./filesystem-test-harness')).bulkStageFilesMock, @@ -88,13 +88,13 @@ vi.mock('../../git/source-control/staging', async (importOriginal) => ({ bulkUnstageFiles: (await import('./filesystem-test-harness')).bulkUnstageFilesMock })) -vi.mock('../../git/source-control/discard-changes', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/discard-changes', async (importOriginal) => ({ ...(await importOriginal>()), bulkDiscardChanges: (await import('./filesystem-test-harness')).bulkDiscardChangesMock, discardChanges: (await import('./filesystem-test-harness')).discardChangesMock })) vi.mock( - '../../git/source-control/check-ignored-paths', + '../../git/source-control/ignores/check-ignored-paths', async () => (await import('./filesystem-test-harness')).gitIgnoredPathsMock ) diff --git a/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-git.test.ts b/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-git.test.ts index a4e9e31742a..d1116168f01 100644 --- a/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-git.test.ts +++ b/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-git.test.ts @@ -52,44 +52,44 @@ vi.mock( '../../crash-reporting/breadcrumbs/crash-breadcrumb-store', async () => (await import('./filesystem-test-harness')).crashBreadcrumbMock ) -vi.mock('../../git/source-control/commit-changes', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/commit-changes', async (importOriginal) => ({ ...(await importOriginal>()), commitChanges: (await import('./filesystem-test-harness')).commitChangesMock })) -vi.mock('../../git/source-control/status-read', async (importOriginal) => ({ +vi.mock('../../git/source-control/status/status-read', async (importOriginal) => ({ ...(await importOriginal>()), getStatus: (await import('./filesystem-test-harness')).getStatusMock })) -vi.mock('../../git/source-control/git-conflict-operation', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/git-conflict-operation', async (importOriginal) => ({ ...(await importOriginal>()), detectConflictOperation: (await import('./filesystem-test-harness')).detectConflictOperationMock, abortMerge: (await import('./filesystem-test-harness')).abortMergeMock, abortRebase: (await import('./filesystem-test-harness')).abortRebaseMock })) -vi.mock('../../git/source-control/file-diff', async (importOriginal) => ({ +vi.mock('../../git/source-control/diffs/file-diff', async (importOriginal) => ({ ...(await importOriginal>()), getDiff: (await import('./filesystem-test-harness')).getDiffMock })) -vi.mock('../../git/source-control/branch-compare', async (importOriginal) => ({ +vi.mock('../../git/source-control/comparisons/branch-compare', async (importOriginal) => ({ ...(await importOriginal>()), getBranchCompare: (await import('./filesystem-test-harness')).getBranchCompareMock })) -vi.mock('../../git/source-control/branch-diff', async (importOriginal) => ({ +vi.mock('../../git/source-control/comparisons/branch-diff', async (importOriginal) => ({ ...(await importOriginal>()), getBranchDiff: (await import('./filesystem-test-harness')).getBranchDiffMock })) -vi.mock('../../git/source-control/staged-commit-context', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/staged-commit-context', async (importOriginal) => ({ ...(await importOriginal>()), getStagedCommitContext: (await import('./filesystem-test-harness')).getStagedCommitContextMock })) -vi.mock('../../git/source-control/staging', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/staging', async (importOriginal) => ({ ...(await importOriginal>()), stageFile: (await import('./filesystem-test-harness')).stageFileMock, bulkStageFiles: (await import('./filesystem-test-harness')).bulkStageFilesMock, @@ -98,13 +98,13 @@ vi.mock('../../git/source-control/staging', async (importOriginal) => ({ bulkUnstageFiles: (await import('./filesystem-test-harness')).bulkUnstageFilesMock })) -vi.mock('../../git/source-control/discard-changes', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/discard-changes', async (importOriginal) => ({ ...(await importOriginal>()), bulkDiscardChanges: (await import('./filesystem-test-harness')).bulkDiscardChangesMock, discardChanges: (await import('./filesystem-test-harness')).discardChangesMock })) vi.mock( - '../../git/source-control/check-ignored-paths', + '../../git/source-control/ignores/check-ignored-paths', async () => (await import('./filesystem-test-harness')).gitIgnoredPathsMock ) diff --git a/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-reads.test.ts b/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-reads.test.ts index e37577bb026..bb72b4352bb 100644 --- a/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-reads.test.ts +++ b/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-reads.test.ts @@ -48,44 +48,44 @@ vi.mock( '../../crash-reporting/breadcrumbs/crash-breadcrumb-store', async () => (await import('./filesystem-test-harness')).crashBreadcrumbMock ) -vi.mock('../../git/source-control/commit-changes', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/commit-changes', async (importOriginal) => ({ ...(await importOriginal>()), commitChanges: (await import('./filesystem-test-harness')).commitChangesMock })) -vi.mock('../../git/source-control/status-read', async (importOriginal) => ({ +vi.mock('../../git/source-control/status/status-read', async (importOriginal) => ({ ...(await importOriginal>()), getStatus: (await import('./filesystem-test-harness')).getStatusMock })) -vi.mock('../../git/source-control/git-conflict-operation', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/git-conflict-operation', async (importOriginal) => ({ ...(await importOriginal>()), detectConflictOperation: (await import('./filesystem-test-harness')).detectConflictOperationMock, abortMerge: (await import('./filesystem-test-harness')).abortMergeMock, abortRebase: (await import('./filesystem-test-harness')).abortRebaseMock })) -vi.mock('../../git/source-control/file-diff', async (importOriginal) => ({ +vi.mock('../../git/source-control/diffs/file-diff', async (importOriginal) => ({ ...(await importOriginal>()), getDiff: (await import('./filesystem-test-harness')).getDiffMock })) -vi.mock('../../git/source-control/branch-compare', async (importOriginal) => ({ +vi.mock('../../git/source-control/comparisons/branch-compare', async (importOriginal) => ({ ...(await importOriginal>()), getBranchCompare: (await import('./filesystem-test-harness')).getBranchCompareMock })) -vi.mock('../../git/source-control/branch-diff', async (importOriginal) => ({ +vi.mock('../../git/source-control/comparisons/branch-diff', async (importOriginal) => ({ ...(await importOriginal>()), getBranchDiff: (await import('./filesystem-test-harness')).getBranchDiffMock })) -vi.mock('../../git/source-control/staged-commit-context', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/staged-commit-context', async (importOriginal) => ({ ...(await importOriginal>()), getStagedCommitContext: (await import('./filesystem-test-harness')).getStagedCommitContextMock })) -vi.mock('../../git/source-control/staging', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/staging', async (importOriginal) => ({ ...(await importOriginal>()), stageFile: (await import('./filesystem-test-harness')).stageFileMock, bulkStageFiles: (await import('./filesystem-test-harness')).bulkStageFilesMock, @@ -94,13 +94,13 @@ vi.mock('../../git/source-control/staging', async (importOriginal) => ({ bulkUnstageFiles: (await import('./filesystem-test-harness')).bulkUnstageFilesMock })) -vi.mock('../../git/source-control/discard-changes', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/discard-changes', async (importOriginal) => ({ ...(await importOriginal>()), bulkDiscardChanges: (await import('./filesystem-test-harness')).bulkDiscardChangesMock, discardChanges: (await import('./filesystem-test-harness')).discardChangesMock })) vi.mock( - '../../git/source-control/check-ignored-paths', + '../../git/source-control/ignores/check-ignored-paths', async () => (await import('./filesystem-test-harness')).gitIgnoredPathsMock ) diff --git a/src/main/ipc/filesystem/filesystem.test.ts b/src/main/ipc/filesystem/filesystem.test.ts index a8bbce81e38..a39a0764766 100644 --- a/src/main/ipc/filesystem/filesystem.test.ts +++ b/src/main/ipc/filesystem/filesystem.test.ts @@ -46,44 +46,44 @@ vi.mock( '../../crash-reporting/breadcrumbs/crash-breadcrumb-store', async () => (await import('./filesystem-test-harness')).crashBreadcrumbMock ) -vi.mock('../../git/source-control/commit-changes', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/commit-changes', async (importOriginal) => ({ ...(await importOriginal>()), commitChanges: (await import('./filesystem-test-harness')).commitChangesMock })) -vi.mock('../../git/source-control/status-read', async (importOriginal) => ({ +vi.mock('../../git/source-control/status/status-read', async (importOriginal) => ({ ...(await importOriginal>()), getStatus: (await import('./filesystem-test-harness')).getStatusMock })) -vi.mock('../../git/source-control/git-conflict-operation', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/git-conflict-operation', async (importOriginal) => ({ ...(await importOriginal>()), detectConflictOperation: (await import('./filesystem-test-harness')).detectConflictOperationMock, abortMerge: (await import('./filesystem-test-harness')).abortMergeMock, abortRebase: (await import('./filesystem-test-harness')).abortRebaseMock })) -vi.mock('../../git/source-control/file-diff', async (importOriginal) => ({ +vi.mock('../../git/source-control/diffs/file-diff', async (importOriginal) => ({ ...(await importOriginal>()), getDiff: (await import('./filesystem-test-harness')).getDiffMock })) -vi.mock('../../git/source-control/branch-compare', async (importOriginal) => ({ +vi.mock('../../git/source-control/comparisons/branch-compare', async (importOriginal) => ({ ...(await importOriginal>()), getBranchCompare: (await import('./filesystem-test-harness')).getBranchCompareMock })) -vi.mock('../../git/source-control/branch-diff', async (importOriginal) => ({ +vi.mock('../../git/source-control/comparisons/branch-diff', async (importOriginal) => ({ ...(await importOriginal>()), getBranchDiff: (await import('./filesystem-test-harness')).getBranchDiffMock })) -vi.mock('../../git/source-control/staged-commit-context', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/staged-commit-context', async (importOriginal) => ({ ...(await importOriginal>()), getStagedCommitContext: (await import('./filesystem-test-harness')).getStagedCommitContextMock })) -vi.mock('../../git/source-control/staging', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/staging', async (importOriginal) => ({ ...(await importOriginal>()), stageFile: (await import('./filesystem-test-harness')).stageFileMock, bulkStageFiles: (await import('./filesystem-test-harness')).bulkStageFilesMock, @@ -92,13 +92,13 @@ vi.mock('../../git/source-control/staging', async (importOriginal) => ({ bulkUnstageFiles: (await import('./filesystem-test-harness')).bulkUnstageFilesMock })) -vi.mock('../../git/source-control/discard-changes', async (importOriginal) => ({ +vi.mock('../../git/source-control/mutations/discard-changes', async (importOriginal) => ({ ...(await importOriginal>()), bulkDiscardChanges: (await import('./filesystem-test-harness')).bulkDiscardChangesMock, discardChanges: (await import('./filesystem-test-harness')).discardChangesMock })) vi.mock( - '../../git/source-control/check-ignored-paths', + '../../git/source-control/ignores/check-ignored-paths', async () => (await import('./filesystem-test-harness')).gitIgnoredPathsMock ) diff --git a/src/main/ipc/filesystem/git-remote/compare-handlers.ts b/src/main/ipc/filesystem/git-remote/compare-handlers.ts index 818c5970a99..186ac0de9d0 100644 --- a/src/main/ipc/filesystem/git-remote/compare-handlers.ts +++ b/src/main/ipc/filesystem/git-remote/compare-handlers.ts @@ -3,8 +3,8 @@ import type { GitBranchCompareResult, GitCommitCompareResult } from '../../../../shared/git/diff/git-diff-compare-types' -import { getBranchCompare } from '../../../git/source-control/branch-compare' -import { getCommitCompare } from '../../../git/source-control/commit-compare' +import { getBranchCompare } from '../../../git/source-control/comparisons/branch-compare' +import { getCommitCompare } from '../../../git/source-control/comparisons/commit-compare' import { resolveRegisteredWorktreePath } from '../../worktrees/removal/registered-worktree-roots-cache' import { getLocalGitOptionsForRegisteredWorktree } from '../../local-worktree-runtime-options' import { validateFullGitObjectId } from '../access/filesystem-path-containment' diff --git a/src/main/ipc/filesystem/git-remote/sync-handlers.ts b/src/main/ipc/filesystem/git-remote/sync-handlers.ts index 14f89b72e65..cb0328590eb 100644 --- a/src/main/ipc/filesystem/git-remote/sync-handlers.ts +++ b/src/main/ipc/filesystem/git-remote/sync-handlers.ts @@ -4,7 +4,7 @@ import type { GitPushTarget } from '../../../../shared/worktree/types' import type { GitUpstreamStatus } from '../../../../shared/git/status/git-status-types' import { gitFetch } from '../../../git/remotes/remote' import { gitSyncForkDefaultBranch } from '../../../git/remotes/fork-sync' -import { getUpstreamStatus } from '../../../git/source-control/upstream' +import { getUpstreamStatus } from '../../../git/source-control/upstream/upstream' import { validateGitPushTarget } from '../../../git/remotes/push-target-validation' import { resolveRegisteredWorktreePath } from '../../worktrees/removal/registered-worktree-roots-cache' import { getLocalGitOptionsForRegisteredWorktree } from '../../local-worktree-runtime-options' diff --git a/src/main/ipc/workspace-cleanup/workspace-cleanup-document-lifetime.test.ts b/src/main/ipc/workspace-cleanup/workspace-cleanup-document-lifetime.test.ts index be985c90e77..7fa3793d420 100644 --- a/src/main/ipc/workspace-cleanup/workspace-cleanup-document-lifetime.test.ts +++ b/src/main/ipc/workspace-cleanup/workspace-cleanup-document-lifetime.test.ts @@ -27,7 +27,7 @@ vi.mock('../../workspaces/catalog/repo-worktrees', async (importOriginal) => ({ ...(await importOriginal()), listRepoWorktrees: boundary.list })) -vi.mock('../../git/source-control/status-read', () => ({ getStatus: boundary.status })) +vi.mock('../../git/source-control/status/status-read', () => ({ getStatus: boundary.status })) vi.mock('../../git/command-runner/git-exec-file', () => ({ gitExecFileAsync: vi.fn(async () => ({ stdout: '0\n', stderr: '' })) })) diff --git a/src/main/ipc/workspace-cleanup/workspace-cleanup-execution-host-routing.test.ts b/src/main/ipc/workspace-cleanup/workspace-cleanup-execution-host-routing.test.ts index 701b75b0cbd..55f1993a49c 100644 --- a/src/main/ipc/workspace-cleanup/workspace-cleanup-execution-host-routing.test.ts +++ b/src/main/ipc/workspace-cleanup/workspace-cleanup-execution-host-routing.test.ts @@ -33,7 +33,7 @@ vi.mock('../../workspaces/catalog/repo-worktrees', () => ({ createFolderWorktree: vi.fn() })) -vi.mock('../../git/source-control/status-read', () => ({ getStatus: getStatusMock })) +vi.mock('../../git/source-control/status/status-read', () => ({ getStatus: getStatusMock })) vi.mock('../../git/command-runner/git-exec-file', () => ({ gitExecFileAsync: gitExecFileAsyncMock })) @@ -117,28 +117,6 @@ describe('workspace cleanup execution-host routing', () => { vi.useRealTimers() }) - it('never lists or reads an executionHostId-only SSH repo on this machine', async () => { - const store = makeStore([makeRepo({ executionHostId: 'ssh:alpha' })], { - [WORKTREE_ID]: makeMeta() - }) - - await scanWorkspaceCleanup(store) - - expect(listRepoWorktreesMock).not.toHaveBeenCalled() - expect(getStatusMock).not.toHaveBeenCalled() - expect(gitExecFileAsyncMock).not.toHaveBeenCalled() - }) - - it('never stats a remote workspace path on this client', async () => { - const store = makeStore([makeRepo({ executionHostId: 'ssh:alpha' })], { - [WORKTREE_ID]: makeMeta({ lastActivityAt: INACTIVE_AT }) - }) - - await scanWorkspaceCleanup(store, { refreshActivity: true }) - - expect(lstatMock).not.toHaveBeenCalled() - }) - it('refuses a runtime host that carries no nested SSH target instead of scanning locally', async () => { const store = makeStore([makeRepo({ executionHostId: 'runtime:env-a' })], { [WORKTREE_ID]: makeMeta() diff --git a/src/main/ipc/workspace-cleanup/workspace-cleanup-git-evidence.ts b/src/main/ipc/workspace-cleanup/workspace-cleanup-git-evidence.ts index b48c352f00d..295fc38264d 100644 --- a/src/main/ipc/workspace-cleanup/workspace-cleanup-git-evidence.ts +++ b/src/main/ipc/workspace-cleanup/workspace-cleanup-git-evidence.ts @@ -1,4 +1,4 @@ -import { getStatus } from '../../git/source-control/status-read' +import { getStatus } from '../../git/source-control/status/status-read' import { gitExecFileAsync } from '../../git/command-runner/git-exec-file' import type { GitStatusResult } from '../../../shared/git/status/git-status-types' import type { Repo } from '../../../shared/repo-types' diff --git a/src/main/ipc/worktrees/creation/worktree-base-directory-git-status-events.test.ts b/src/main/ipc/worktrees/creation/worktree-base-directory-git-status-events.test.ts index 28f56baa0fe..b56f922c8cf 100644 --- a/src/main/ipc/worktrees/creation/worktree-base-directory-git-status-events.test.ts +++ b/src/main/ipc/worktrees/creation/worktree-base-directory-git-status-events.test.ts @@ -4,7 +4,7 @@ import { BrowserWindow } from 'electron' import { afterEach, expect, it, vi } from 'vitest' import { listWorktrees } from '../../../git/worktrees/catalog/worktree-scan-cache' import { listWorktreesStrict } from '../../../git/worktrees/catalog/worktree-listing' -import { getStatusLineStatsCacheKey } from '../../../git/source-control/status-branch-line-total-input' +import { getStatusLineStatsCacheKey } from '../../../git/source-control/status/status-branch-line-total-input' import { OrcaRuntimeService } from '../../../runtime/orca-runtime' import { partialRuntimeStore } from '../../../runtime/orca-runtime-tests/runtime-store-partial-fixture' import { makeStore } from '../../../runtime/client/navigation/paired-client-navigation-test-harness' diff --git a/src/main/ipc/worktrees/git/git-status-upstream-ref-watch-request.ts b/src/main/ipc/worktrees/git/git-status-upstream-ref-watch-request.ts index 54009d7de39..889733188fe 100644 --- a/src/main/ipc/worktrees/git/git-status-upstream-ref-watch-request.ts +++ b/src/main/ipc/worktrees/git/git-status-upstream-ref-watch-request.ts @@ -1,5 +1,5 @@ import type { Store } from '../../../persistence/loading-store/store' -import { resolveGitStatusUpstreamRef } from '../../../git/source-control/status-upstream-ref' +import { resolveGitStatusUpstreamRef } from '../../../git/source-control/upstream/status-upstream-ref' import { gitExecFileAsync } from '../../../git/command-runner/git-exec-file' import { abortSignalWithTimeout } from '../../../lib/abort-signal-with-timeout' import { resolveRegisteredWorktreePath } from '../removal/registered-worktree-roots-cache' diff --git a/src/main/providers/git/local-provider.ts b/src/main/providers/git/local-provider.ts index 8ae52f3109a..f8d5715b714 100644 --- a/src/main/providers/git/local-provider.ts +++ b/src/main/providers/git/local-provider.ts @@ -2,7 +2,7 @@ import { createBranch } from '../../git/worktrees/checkout/create-branch' import { applyCommit, recoverSequencer } from '../../git/worktrees/checkout/commit-actions' import { gitSyncForkDefaultBranch } from '../../git/remotes/fork-sync' import type { GitRuntimeOptions } from '../../git/command-runner/git-runtime-options' -import { checkIgnoredPaths } from '../../git/source-control/check-ignored-paths' +import { checkIgnoredPaths } from '../../git/source-control/ignores/check-ignored-paths' import { checkoutBranch, listLocalBranches } from '../../git/worktrees/checkout/checkout' import { getHistory, getHistoryRefs } from '../../git/source-control/history' import { gitFastForward, gitFetch, gitPull, gitPush } from '../../git/remotes/remote' @@ -15,8 +15,11 @@ import { abortMerge, abortRebase, detectConflictOperation -} from '../../git/source-control/git-conflict-operation' -import { bulkDiscardChanges, discardChanges } from '../../git/source-control/discard-changes' +} from '../../git/source-control/mutations/git-conflict-operation' +import { + bulkDiscardChanges, + discardChanges +} from '../../git/source-control/mutations/discard-changes' import { bulkStageFiles, bulkUnstageFiles, @@ -24,16 +27,16 @@ import { stageFile, stageWorktreeChanges, unstageFile -} from '../../git/source-control/staging' -import { commitChanges, completeMerge } from '../../git/source-control/commit-changes' -import { getBranchCompare } from '../../git/source-control/branch-compare' -import { getCommitCompare } from '../../git/source-control/commit-compare' -import { getCommitDiff } from '../../git/source-control/commit-diff' -import { getDiff } from '../../git/source-control/file-diff' -import { getStagedCommitContext } from '../../git/source-control/staged-commit-context' -import { getStatus } from '../../git/source-control/status-read' -import { getSubmoduleStatus } from '../../git/source-control/submodule-status' -import { getUpstreamStatus } from '../../git/source-control/upstream' +} from '../../git/source-control/mutations/staging' +import { commitChanges, completeMerge } from '../../git/source-control/mutations/commit-changes' +import { getBranchCompare } from '../../git/source-control/comparisons/branch-compare' +import { getCommitCompare } from '../../git/source-control/comparisons/commit-compare' +import { getCommitDiff } from '../../git/source-control/comparisons/commit-diff' +import { getDiff } from '../../git/source-control/diffs/file-diff' +import { getStagedCommitContext } from '../../git/source-control/mutations/staged-commit-context' +import { getStatus } from '../../git/source-control/status/status-read' +import { getSubmoduleStatus } from '../../git/source-control/submodules/submodule-status' +import { getUpstreamStatus } from '../../git/source-control/upstream/upstream' import { listWorktrees } from '../../git/worktrees/catalog/worktree-scan-cache' import type { IGitProvider } from './provider-contract' diff --git a/src/main/runtime/integrations/git/runtime-git-diff-commands.ts b/src/main/runtime/integrations/git/runtime-git-diff-commands.ts index 4da4a859976..ef39a39ace3 100644 --- a/src/main/runtime/integrations/git/runtime-git-diff-commands.ts +++ b/src/main/runtime/integrations/git/runtime-git-diff-commands.ts @@ -4,7 +4,7 @@ import type { GitDiffResult } from '../../../../shared/git/diff/git-diff-compare-types' import { assertGitDiffWithinTransportBudget } from '../../../../shared/git/diff/git-diff-transport-budget' -import { getBranchDiff } from '../../../git/source-control/branch-diff' +import { getBranchDiff } from '../../../git/source-control/comparisons/branch-diff' import type { GitAdmissionTier } from '../../../../shared/rpc-contract/git-admission-tier-params' import { normalizeRuntimeRelativePath } from '../../files/paths/runtime-relative-paths' import { diff --git a/src/main/runtime/integrations/git/runtime-git-generation-admission.test.ts b/src/main/runtime/integrations/git/runtime-git-generation-admission.test.ts index 2b4b0e22138..a04459eb4b2 100644 --- a/src/main/runtime/integrations/git/runtime-git-generation-admission.test.ts +++ b/src/main/runtime/integrations/git/runtime-git-generation-admission.test.ts @@ -16,7 +16,7 @@ const mocks = vi.hoisted(() => ({ const linkedLookup = vi.hoisted(() => ({ run: (): Promise => Promise.resolve(null) })) -vi.mock('../../../git/source-control/staged-commit-context', () => ({ +vi.mock('../../../git/source-control/mutations/staged-commit-context', () => ({ getStagedCommitContext: mocks.getStagedCommitContext })) vi.mock('../../../git/command-runner/git-exec-file', async (importOriginal) => ({ diff --git a/src/main/runtime/integrations/git/runtime-git-generation-commands.ts b/src/main/runtime/integrations/git/runtime-git-generation-commands.ts index bfc0007a147..a26dc0e0a35 100644 --- a/src/main/runtime/integrations/git/runtime-git-generation-commands.ts +++ b/src/main/runtime/integrations/git/runtime-git-generation-commands.ts @@ -7,7 +7,7 @@ import { getCommitMessageModelDiscoveryHostKey } from '../../../../shared/source import type { HostedReviewProvider } from '../../../../shared/source-control/hosted-review/hosted-review-contracts' import { withLinkedIssueDraftContext } from '../../../../shared/source-control/ai/recipes/source-control-ai-action-variables' import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent' -import { getStagedCommitContext } from '../../../git/source-control/staged-commit-context' +import { getStagedCommitContext } from '../../../git/source-control/mutations/staged-commit-context' import { SSH_HOST_NOT_SERVED_MESSAGE } from '../../../providers/ssh-host-not-served-error' import { loadPullRequestLinkedIssue } from '../../../source-control/pull-request-linked-issue' import { resolveHostedReviewBodyForGeneration } from '../../../source-control/pull-request-template' diff --git a/src/main/runtime/orca-runtime-tests/repository-project-operations-part-02.spec.ts b/src/main/runtime/orca-runtime-tests/repository-project-operations-part-02.spec.ts index f07e72eaca1..bd2ad8b3888 100644 --- a/src/main/runtime/orca-runtime-tests/repository-project-operations-part-02.spec.ts +++ b/src/main/runtime/orca-runtime-tests/repository-project-operations-part-02.spec.ts @@ -8,7 +8,7 @@ import { OrcaRuntimeService } from '../orca-runtime' import { clearSubmodulePathsCacheForTests, listSubmodulePaths -} from '../../git/source-control/submodule-paths' +} from '../../git/source-control/submodules/submodule-paths' import { execFileSync } from 'node:child_process' import '../orca-runtime-test-mocks/setup.spec' import { join } from 'node:path' diff --git a/src/main/runtime/orca-runtime-tests/runtime-test-module-registration.spec.ts b/src/main/runtime/orca-runtime-tests/runtime-test-module-registration.spec.ts index c72c4b786da..bfd9fcd3fd7 100644 --- a/src/main/runtime/orca-runtime-tests/runtime-test-module-registration.spec.ts +++ b/src/main/runtime/orca-runtime-tests/runtime-test-module-registration.spec.ts @@ -27,7 +27,7 @@ import '../../git/worktrees/catalog/worktree-scan-cache' import '../../git/worktrees/removal/worktree-removal' import '../workspaces/removal/worktree-teardown' import '../workspaces/removal/worktree-teardown-deadline' -import '../../git/source-control/submodule-paths' +import '../../git/source-control/submodules/submodule-paths' import '../../workspace-hooks/hooks' import '../../../shared/orca-yaml' import '../../workspace-hooks/worktree-runner-script' diff --git a/src/main/runtime/repositories/runtime-repository-issue-command.test.ts b/src/main/runtime/repositories/runtime-repository-issue-command.test.ts index 7c963a2ce80..44562d512d4 100644 --- a/src/main/runtime/repositories/runtime-repository-issue-command.test.ts +++ b/src/main/runtime/repositories/runtime-repository-issue-command.test.ts @@ -6,7 +6,7 @@ import { RuntimeRepositoryIssueCommand } from './runtime-repository-issue-comman const mocks = vi.hoisted(() => ({ localCheck: vi.fn() })) -vi.mock('../../git/source-control/check-ignored-paths', () => ({ +vi.mock('../../git/source-control/ignores/check-ignored-paths', () => ({ checkIgnoredPaths: mocks.localCheck })) diff --git a/src/main/source-control/hosted-review-creation-eligibility.test.ts b/src/main/source-control/hosted-review-creation-eligibility.test.ts index ce8bdcb15fe..9de1fc11e94 100644 --- a/src/main/source-control/hosted-review-creation-eligibility.test.ts +++ b/src/main/source-control/hosted-review-creation-eligibility.test.ts @@ -120,7 +120,9 @@ vi.mock('../gitlab/projects/gitlab-project-ref-resolution', () => ({ connectionId ? {} : { cwd: repoPath } })) -vi.mock('../git/source-control/upstream', () => ({ getUpstreamStatus: getUpstreamStatusMock })) +vi.mock('../git/source-control/upstream/upstream', () => ({ + getUpstreamStatus: getUpstreamStatusMock +})) vi.mock('./hosted-review', () => ({ getHostedReviewForBranch: getHostedReviewForBranchMock diff --git a/src/main/source-control/hosted-review-creation-git-state.ts b/src/main/source-control/hosted-review-creation-git-state.ts index c00cddeba72..7f3b420786e 100644 --- a/src/main/source-control/hosted-review-creation-git-state.ts +++ b/src/main/source-control/hosted-review-creation-git-state.ts @@ -12,9 +12,9 @@ import { gitOptionalLocksDisabledEnv } from '../git/command-runner/git-environme import { parsePorcelainV1Records, type PorcelainV1Record -} from '../git/source-control/porcelain-v1-records' +} from '../git/source-control/status/porcelain-v1-records' import { resolveDefaultBaseRefViaExec } from '../../shared/git/refs/git-default-base-ref' -import { getUpstreamStatus } from '../git/source-control/upstream' +import { getUpstreamStatus } from '../git/source-control/upstream/upstream' import { findExistingWorktreeSymlinkPaths } from '../git/worktrees/content-paths/worktree-symlink-detection' import { requireReachableGitRoute } from '../providers/execution-host-provider-dispatch' import type { ExecutionHostId } from '../../shared/execution-host' diff --git a/src/main/source-control/hosted-review-creation.test.ts b/src/main/source-control/hosted-review-creation.test.ts index 441add3fa9d..5c3bbee61bb 100644 --- a/src/main/source-control/hosted-review-creation.test.ts +++ b/src/main/source-control/hosted-review-creation.test.ts @@ -120,7 +120,7 @@ vi.mock('../gitlab/projects/gitlab-project-ref-resolution', () => ({ connectionId ? {} : { cwd: repoPath } })) -vi.mock('../git/source-control/upstream', () => ({ +vi.mock('../git/source-control/upstream/upstream', () => ({ getUpstreamStatus: getUpstreamStatusMock })) diff --git a/src/main/source-control/hosted-review-execution-host-routing.test.ts b/src/main/source-control/hosted-review-execution-host-routing.test.ts index 0d6b3596358..389ee702e6b 100644 --- a/src/main/source-control/hosted-review-execution-host-routing.test.ts +++ b/src/main/source-control/hosted-review-execution-host-routing.test.ts @@ -85,7 +85,9 @@ vi.mock('../git/command-runner/git-environment/git-process-env', async (importOr gitOptionalLocksDisabledEnv: vi.fn(() => ({})) })) -vi.mock('../git/source-control/upstream', () => ({ getUpstreamStatus: getUpstreamStatusMock })) +vi.mock('../git/source-control/upstream/upstream', () => ({ + getUpstreamStatus: getUpstreamStatusMock +})) vi.mock('../../shared/git/refs/git-default-base-ref', async (importOriginal) => ({ ...(await importOriginal>()), diff --git a/src/main/workspace-hooks/issue-command-file.ts b/src/main/workspace-hooks/issue-command-file.ts index 3825f8d96a9..59d633cfa80 100644 --- a/src/main/workspace-hooks/issue-command-file.ts +++ b/src/main/workspace-hooks/issue-command-file.ts @@ -3,8 +3,8 @@ import { readFileSync, existsSync, mkdirSync, writeFileSync, rmSync } from 'node import { join } from 'node:path' import { loadHooks } from './hooks' import type { GitRuntimeOptions } from '../git/command-runner/git-runtime-options' -import { checkIgnoredPaths } from '../git/source-control/check-ignored-paths' -import { listTrackedPathSpecs } from '../git/source-control/discard-changes' +import { checkIgnoredPaths } from '../git/source-control/ignores/check-ignored-paths' +import { listTrackedPathSpecs } from '../git/source-control/mutations/discard-changes' import { guardWslUncPathAccess, WslDistroStoppedError, diff --git a/src/main/workspace-hooks/issue-command-wsl-stopped-distro.test.ts b/src/main/workspace-hooks/issue-command-wsl-stopped-distro.test.ts index 7cfc2f3b40b..25732ca452c 100644 --- a/src/main/workspace-hooks/issue-command-wsl-stopped-distro.test.ts +++ b/src/main/workspace-hooks/issue-command-wsl-stopped-distro.test.ts @@ -23,7 +23,7 @@ vi.mock('node:fs', async (importOriginal) => ({ mkdirSync: mocks.mkdirSync, rmSync: mocks.rmSync })) -vi.mock('../git/source-control/check-ignored-paths', () => ({ +vi.mock('../git/source-control/ignores/check-ignored-paths', () => ({ checkIgnoredPaths: vi.fn(async () => new Set([''])) })) vi.mock('./hooks', () => ({ loadHooks: mocks.loadHooks })) @@ -109,12 +109,4 @@ describe('issue command reads for a stopped WSL distro (E9)', () => { ).rejects.toThrow("WSL distro 'Ubuntu' is not running") expect(mocks.writeFileSync).not.toHaveBeenCalled() }) - - it('still saves a host-path repo whose runtime awaits repair (no Git distro yet)', async () => { - const repairing = (): never => { - throw new Error('Project runtime requires repair') - } - await writeIssueCommandUnlessWslStopped(HOST_REPO, 'mine', repairing) - expect(mocks.writeFileSync).toHaveBeenCalled() - }) }) diff --git a/tests/tools/branch-line-total-renderer.test.mjs b/tests/tools/branch-line-total-renderer.test.mjs deleted file mode 100644 index f39bd372928..00000000000 --- a/tests/tools/branch-line-total-renderer.test.mjs +++ /dev/null @@ -1,72 +0,0 @@ -import { runProcess } from '@orca/process-host' -import { mkdtemp, rm, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { expect, it } from 'vitest' -import { getBranchCompare } from '../../src/main/git/source-control/branch-compare' -import { getStatus } from '../../src/main/git/source-control/status-read' -import { invalidateGitReadCaches } from '../../src/main/git/source-control/git-read-cache-invalidation' -import { createEditorStore } from '../../src/renderer/src/store/slices/editor/editor-slice-test-harness' - -async function git(repo, ...args) { - const result = await runProcess({ program: 'git', args, cwd: repo }) - if (result.code !== 0) { - throw new Error(result.stderr) - } - return result.stdout.trim() -} - -it('removes a published deletion total when the host finds an untracked recreation', async () => { - const repo = await mkdtemp(join(tmpdir(), 'orca-renderer-branch-total-')) - invalidateGitReadCaches() - try { - await git(repo, 'init', '-q') - await git(repo, 'config', 'user.email', 'test@example.com') - await git(repo, 'config', 'user.name', 'Test') - await git(repo, 'config', 'commit.gpgSign', 'false') - await writeFile(join(repo, 'gone.txt'), 'one\ntwo\nthree\n') - await git(repo, 'add', 'gone.txt') - await git(repo, 'commit', '-qm', 'base file') - await git(repo, 'branch', 'base') - await git(repo, 'checkout', '-qb', 'feature') - await git(repo, 'rm', '-q', 'gone.txt') - await git(repo, 'commit', '-qm', 'delete file') - const { summary } = await getBranchCompare(repo, 'base') - expect(summary.status).toBe('ready') - const mergeBase = summary.mergeBase - if (!mergeBase) { - throw new Error('Missing merge base') - } - const options = { branchLineTotalMergeBase: mergeBase } - const store = createEditorStore() - const refresh = async (reuseLineStats = false) => { - store.getState().setGitStatus('wt-1', await getStatus(repo, { ...options, reuseLineStats })) - } - - await refresh() - expect(store.getState().gitBranchLineTotalByWorktree['wt-1']).toMatchObject({ - added: 0, - removed: 3, - mergeBase - }) - - await writeFile(join(repo, 'gone.txt'), 'replacement\nfile\n') - await refresh() - expect(store.getState().gitBranchLineTotalByWorktree['wt-1']).toBeUndefined() - await refresh(true) - expect(store.getState().gitBranchLineTotalByWorktree['wt-1']).toBeUndefined() - expect((await getBranchCompare(repo, 'base')).summary.mergeBase).toBe(mergeBase) - - await git(repo, 'add', 'gone.txt') - invalidateGitReadCaches() - await refresh() - expect(store.getState().gitBranchLineTotalByWorktree['wt-1']).toMatchObject({ - added: 2, - removed: 3, - mergeBase - }) - } finally { - invalidateGitReadCaches() - await rm(repo, { recursive: true, force: true }) - } -}) From aa736b6650fd13a0256a7ba0b3b1c7741fb933ee Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 20:49:14 -0800 Subject: [PATCH 012/178] test: remove narrow mobile and settings navigation samples (#41818) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 1 - .../mobile-file-tap-open.test.ts | 54 -------- .../native-stack-return.test.ts | 105 +-------------- .../general/GeneralPane.navigation.test.tsx | 121 ------------------ ...nding-terminal-conflict-diagnostic.spec.ts | 50 -------- .../settings-search-shortcuts-pane.spec.ts | 42 ------ 6 files changed, 1 insertion(+), 372 deletions(-) delete mode 100644 src/renderer/src/components/settings/general/GeneralPane.navigation.test.tsx delete mode 100644 tests/e2e/keybinding-terminal-conflict-diagnostic.spec.ts delete mode 100644 tests/e2e/settings-search-shortcuts-pane.spec.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 592b553b375..ab37b939b62 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -7283,7 +7283,6 @@ "tests/e2e/settings-agent-awake.spec.ts": 33400, "tests/e2e/settings-display-name-ime.spec.ts": 18100, "tests/e2e/settings-search-responsiveness.spec.ts": 15600, - "tests/e2e/settings-search-shortcuts-pane.spec.ts": 15800, "tests/e2e/settings-skill-detection.spec.ts": 21800, "tests/e2e/settled-worker-tab-survives-restart.spec.ts": 62400, "tests/e2e/setup-guide-sidebar.spec.ts": 6800, diff --git a/mobile/src/files/session-documents/mobile-file-tap-open.test.ts b/mobile/src/files/session-documents/mobile-file-tap-open.test.ts index 94ab9f2357d..778676c0d8e 100644 --- a/mobile/src/files/session-documents/mobile-file-tap-open.test.ts +++ b/mobile/src/files/session-documents/mobile-file-tap-open.test.ts @@ -85,58 +85,4 @@ describe('openMobileFileTap', () => { ) expect(opened).toEqual([{ worktree: 'id:wt-1', relativePath: 'src/file19.ts' }]) }) - - function openLinkedWorkspaceRoot(sourceAuthorityCurrent: boolean) { - const calls = { openWorkspace: vi.fn(), pushPreviewRoute: vi.fn(), onOpenFailed: vi.fn() } - openMobileFileTap({ - client: { - sendRequest: vi - .fn(async (method) => { - throw new Error(`no scripted response for ${method}`) - }) - .mockResolvedValueOnce( - ok({ - worktree: 'wt-2', - relativePath: '', - absolutePath: '/workspaces/wt-2', - exists: true, - isDirectory: true - }) - ) - }, - hostId: 'host-1', - worktreeId: 'wt-1', - candidates: [{ pathText: '/workspaces/wt-2', line: null, column: null }], - openBrowser: vi.fn(), - triggerOpenFeedback: vi.fn(), - fetchSessionTabs: vi.fn(), - getSessionTabs: () => [], - getActiveSessionTabId: () => null, - getActivationState: (activated) => ({ - ...activeTerminalState(activated), - sourceAuthorityCurrent - }), - switchSessionTab: vi.fn(), - scheduleDelayedAction: vi.fn(), - ...calls - }) - return calls - } - - it("opens the workspace when the link is another workspace's root", async () => { - const calls = openLinkedWorkspaceRoot(true) - await vi.advanceTimersByTimeAsync(0) - - expect(calls.openWorkspace.mock.calls).toEqual([['wt-2']]) - expect(calls.onOpenFailed).not.toHaveBeenCalled() - }) - - it('drops a workspace-root reply that outlived its host authority', async () => { - const calls = openLinkedWorkspaceRoot(false) - await vi.advanceTimersByTimeAsync(0) - - expect(calls.openWorkspace).not.toHaveBeenCalled() - expect(calls.pushPreviewRoute).not.toHaveBeenCalled() - expect(calls.onOpenFailed).not.toHaveBeenCalled() - }) }) diff --git a/mobile/src/mobile-web-shell/native-stack-return.test.ts b/mobile/src/mobile-web-shell/native-stack-return.test.ts index cb3943cebe5..18747543b42 100644 --- a/mobile/src/mobile-web-shell/native-stack-return.test.ts +++ b/mobile/src/mobile-web-shell/native-stack-return.test.ts @@ -1,9 +1,5 @@ import { describe, expect, it } from 'vitest' -import { - nativeSessionReturn, - nativeStackReturn, - type NativeStackState -} from './native-stack-return' +import { nativeSessionReturn, type NativeStackState } from './native-stack-return' type Route = NativeStackState['routes'][number] @@ -30,102 +26,8 @@ const SESSION = { params: { hostId: 'mac/1', worktreeId: 'wt-a' } } -describe('nativeStackReturn', () => { - it('pops back to Home under the host instead of stacking a second Home', () => { - expect(nativeStackReturn(rootStack(HOME, hostStack(HOST)), '/')).toEqual({ - target: 'root-stack', - source: 'root-stack/1', - count: 1 - }) - }) - - it('finds nothing when the host is the root, as after a cold deep link', () => { - expect(nativeStackReturn(rootStack(hostStack(HOST)), '/')).toBeNull() - }) - - it('returns to an exact path and query, however the href spells it', () => { - const files = { name: '[hostId]/files/[worktreeId]', params: SESSION.params } - const tabbed = { ...SESSION, params: { ...SESSION.params, tab: 'b', pane: '1' } } - const state = rootStack(HOME, hostStack(HOST, tabbed, files)) - expect(nativeStackReturn(state, '/h/mac%2F1/session/wt-a?pane=1&tab=b')).toEqual({ - target: 'host-stack', - source: 'host-stack/2', - count: 1 - }) - expect(nativeStackReturn(state, '/h/mac%2F1')).toMatchObject({ target: 'host-stack', count: 2 }) - expect(nativeStackReturn(state, '/h/mac%2F1/session/wt-a?pane=1&tab=c')).toBeNull() - expect(nativeStackReturn(state, '/h/mac%2F1/session/wt-b?pane=1&tab=b')).toBeNull() - }) - - it('treats a param cleared to empty as absent', () => { - const cleared = { ...SESSION, params: { ...SESSION.params, paneKey: '' } } - const state = rootStack( - HOME, - hostStack(HOST, cleared, { name: '[hostId]/files/[worktreeId]', params: SESSION.params }) - ) - expect(nativeStackReturn(state, '/h/mac%2F1/session/wt-a')).toMatchObject({ count: 1 }) - }) - - it('names the focused screen as current rather than a pop', () => { - const state = rootStack(HOME, hostStack(HOST, SESSION)) - expect(nativeStackReturn(state, '/h/mac%2F1/session/wt-a')).toBe('current') - }) -}) - describe('nativeSessionReturn', () => { const SOURCE_CONTROL = { name: '[hostId]/source-control/[worktreeId]', params: SESSION.params } - const spent = { ...SESSION, params: { ...SESSION.params, paneKey: '', name: 'Workspace A' } } - const href = '/h/mac%2F1/session/wt-a?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - - it('finds the session beneath source control whatever pane or name its entry carries', () => { - const state = rootStack(HOME, hostStack(HOST, spent, SOURCE_CONTROL)) - expect(nativeSessionReturn(state, href)).toEqual({ - target: 'host-stack', - source: 'host-stack/2', - count: 1, - routeKey: 'host-stack/1' - }) - }) - - it('names the focused session with nothing to pop', () => { - const state = rootStack(HOME, hostStack(HOST, spent)) - expect(nativeSessionReturn(state, href)).toEqual({ - target: 'host-stack', - source: 'host-stack/1', - count: 0, - routeKey: 'host-stack/1' - }) - }) - - it('finds nothing when only another workspace or execution host is on the stack', () => { - const other = { ...SESSION, params: { hostId: 'mac/1', worktreeId: 'wt-b' } } - expect( - nativeSessionReturn(rootStack(HOME, hostStack(HOST, other, SOURCE_CONTROL)), href) - ).toBeNull() - const remote = { ...SESSION, params: { ...SESSION.params, executionHost: 'ssh:box' } } - expect( - nativeSessionReturn(rootStack(HOME, hostStack(HOST, remote, SOURCE_CONTROL)), href) - ).toBeNull() - expect( - nativeSessionReturn( - rootStack(HOME, hostStack(HOST, remote, SOURCE_CONTROL)), - `${href}&executionHost=ssh%3Abox` - ) - ).toMatchObject({ count: 1, routeKey: 'host-stack/1' }) - }) - - it('keeps worktrees apart when they differ only after an encoded question mark', () => { - const onStack = { ...SESSION, params: { hostId: 'mac/1', worktreeId: 'repo::/w/a?x' } } - const state = rootStack(HOME, hostStack(HOST, onStack, SOURCE_CONTROL)) - const to = (worktreeId: string) => - `/h/mac%2F1/session/${encodeURIComponent(worktreeId)}?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111` - expect(nativeSessionReturn(state, to('repo::/w/a?y'))).toBeNull() - expect(nativeSessionReturn(state, to('repo::/w/a'))).toBeNull() - expect(nativeSessionReturn(state, to('repo::/w/a?x'))).toMatchObject({ - count: 1, - routeKey: 'host-stack/1' - }) - }) it('keeps execution hosts apart for a worktree holding an encoded question mark', () => { const params = { hostId: 'mac/1', worktreeId: 'repo::/w/a?x' } @@ -139,9 +41,4 @@ describe('nativeSessionReturn', () => { routeKey: 'host-stack/1' }) }) - - it('answers only session hrefs', () => { - const state = rootStack(HOME, hostStack(HOST, spent, SOURCE_CONTROL)) - expect(nativeSessionReturn(state, '/h/mac%2F1/source-control/wt-a?paneKey=x')).toBeNull() - }) }) diff --git a/src/renderer/src/components/settings/general/GeneralPane.navigation.test.tsx b/src/renderer/src/components/settings/general/GeneralPane.navigation.test.tsx deleted file mode 100644 index fa3a1544e71..00000000000 --- a/src/renderer/src/components/settings/general/GeneralPane.navigation.test.tsx +++ /dev/null @@ -1,121 +0,0 @@ -// @vitest-environment happy-dom -import { useRef, useState } from 'react' -import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { getDefaultSettings } from '../../../../../shared/constants' -import { useAppStore } from '@/store' -import { GeneralPane } from './GeneralPane' -import type { GeneralSubPage } from './general-sub-pages' -import { useSettingsRepoScrollEffects } from '../navigation/use-settings-repo-scroll-effects' -import type { SettingsStoreModel } from '../page/use-settings-store-model' -import type { SettingsInteractionController } from '../page/use-settings-interaction-controller' -import type { SettingsNavigationModel } from '../navigation/use-settings-navigation-model' -import type { SettingsTerminalModel } from '../page/use-settings-terminal-model' -import { TooltipProvider } from '../../ui/tooltip' - -beforeEach(() => useAppStore.setState(useAppStore.getInitialState(), true)) -afterEach(() => { - cleanup() - vi.restoreAllMocks() -}) - -function NavigationHarness({ - initialPage = 'editor', - onWrite = vi.fn() -}: { - initialPage?: GeneralSubPage - onWrite?: (updates: Partial>) => void -}) { - const [settings, setSettings] = useState(() => ({ - ...getDefaultSettings('/tmp'), - editorMinimapEnabled: true - })) - const [generalSubPage, setGeneralSubPage] = useState(initialPage) - const [activeSectionId, setActiveSectionId] = useState('general') - const [pendingNavRequestTick, setPendingNavRequestTick] = useState(0) - const query = useAppStore((state) => state.settingsSearchQuery) - const setQuery = useAppStore((state) => state.setSettingsSearchQuery) - const pendingNavSectionRef = useRef(null) - const pendingScrollTargetRef = useRef(null) - const contentScrollRef = useRef(null) - const pendingScrollTargetWatchRef = useRef(null) - const pendingSubsectionScrollFrameRef = useRef(null) - const repoHooksRequestSeqRef = useRef(0) - const requestTarget = (sectionId: string) => { - pendingNavSectionRef.current = 'general' - pendingScrollTargetRef.current = sectionId - setPendingNavRequestTick((tick) => tick + 1) - } - const model = { - activeSectionId, - setActiveSectionId, - generalSubPage, - setGeneralSubPage, - setHighlightedSettingsTarget: vi.fn(), - pendingNavRequestTick, - setPendingNavRequestTick, - repos: [], - setRepoHooksMap: vi.fn(), - settingsSearchQuery: query, - setSettingsSearchQuery: setQuery - } - useSettingsRepoScrollEffects( - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Every model field this navigation hook reads is supplied; repository loading is inactive. - model as unknown as SettingsStoreModel, - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Supplies the production hook's complete navigation-ref contract. - { - contentScrollRef, - pendingNavSectionRef, - pendingScrollTargetRef, - pendingScrollTargetWatchRef, - pendingSubsectionScrollFrameRef, - repoHooksRequestSeqRef - } as SettingsInteractionController, - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: General is the only visible section in this focused renderer harness. - { - visibleNavSections: [{ id: 'general' }], - visibleSectionIds: new Set(['general']) - } as SettingsNavigationModel, - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: No repository capability requests are needed for General navigation. - { neededRepos: [] } as unknown as SettingsTerminalModel - ) - return ( - - - { - onWrite(updates) - setSettings((current) => ({ ...current, ...updates })) - }} - onSelectSubPage={(page, sectionId) => { - if (sectionId) { - requestTarget(sectionId) - } else { - setGeneralSubPage(page) - setQuery('') - } - }} - /> - - ) -} - -function renderNavigationHarness(props: Parameters[0] = {}) { - return render() -} - -it('opens an old subsection link from another child, including repeated selection', async () => { - const scroll = vi.spyOn(HTMLElement.prototype, 'scrollIntoView') - const { container } = renderNavigationHarness() - fireEvent.click(screen.getByRole('button', { name: 'Open app settings link' })) - const add = await screen.findByRole('button', { name: 'Add app' }) - const target = container.querySelector('#general-open-in-apps') - expect(target?.contains(add)).toBe(true) - await waitFor(() => expect(scroll.mock.contexts).toContain(target)) - scroll.mockClear() - fireEvent.click(screen.getByRole('button', { name: 'Open app settings link' })) - await waitFor(() => expect(scroll.mock.contexts).toContain(target)) -}) diff --git a/tests/e2e/keybinding-terminal-conflict-diagnostic.spec.ts b/tests/e2e/keybinding-terminal-conflict-diagnostic.spec.ts deleted file mode 100644 index 89f484e403d..00000000000 --- a/tests/e2e/keybinding-terminal-conflict-diagnostic.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { mkdirSync, writeFileSync } from 'node:fs' -import path from 'node:path' -import { test, expect } from './helpers/orca-app' -import { waitForSessionReady } from './helpers/store' - -const CONFLICTING_FILE = `${JSON.stringify( - { version: 1, keybindings: { 'terminal.splitRight': ['Mod+Alt+ArrowRight'] } }, - null, - 2 -)}\n` - -test.describe('Terminal shortcut conflicting with a chord main intercepts', () => { - test('keeps the saved override and reports the conflict in Settings', async ({ - orcaPage, - electronApp - }) => { - await waitForSessionReady(orcaPage) - - const isolatedHome = await electronApp.evaluate(({ app }) => app.getPath('home')) - mkdirSync(path.join(isolatedHome, '.orca'), { recursive: true }) - writeFileSync(path.join(isolatedHome, '.orca', 'keybindings.json'), CONFLICTING_FILE) - - const savedOverride = await orcaPage.evaluate(async () => { - const store = window.__store - if (!store) { - throw new Error('window.__store is not available') - } - await store.getState().updateSettings({ uiLanguage: 'en' }) - await store.getState().reloadKeybindings() - store.getState().openSettingsPage() - return store.getState().keybindings['terminal.splitRight'] - }) - expect(savedOverride).toEqual(['Mod+Alt+ArrowRight']) - - const searchInput = orcaPage.getByPlaceholder('Search settings') - await expect(searchInput).toBeVisible() - await searchInput.fill('shortcuts') - // Search lists results; the page moves on an explicit pick. - await searchInput.press('ArrowDown') - await searchInput.press('Enter') - await expect(orcaPage.getByRole('heading', { name: 'Shortcuts', exact: true })).toBeVisible() - - await orcaPage.getByRole('button', { name: /Conflicts/ }).click() - await expect(orcaPage.getByText('Split terminal right', { exact: true })).toBeVisible() - await expect(orcaPage.getByText('Worktree history forward', { exact: true })).toBeVisible() - await expect( - orcaPage.getByText(/conflicts with .*Worktree history forward/).first() - ).toBeVisible() - }) -}) diff --git a/tests/e2e/settings-search-shortcuts-pane.spec.ts b/tests/e2e/settings-search-shortcuts-pane.spec.ts deleted file mode 100644 index 7b684a2aeb7..00000000000 --- a/tests/e2e/settings-search-shortcuts-pane.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { test, expect } from './helpers/orca-app' -import { waitForSessionReady } from './helpers/store' - -test.describe('Settings sidebar search on the Shortcuts pane', () => { - test('picking a result opens the Shortcuts pane unfiltered with local search usable', async ({ - orcaPage - }) => { - await waitForSessionReady(orcaPage) - - await orcaPage.evaluate(async () => { - const store = window.__store - if (!store) { - throw new Error('window.__store is not available') - } - // Pin English instead of following the host locale. - await store.getState().updateSettings({ uiLanguage: 'en' }) - store.getState().openSettingsPage() - }) - - const searchInput = orcaPage.getByPlaceholder('Search settings') - await expect(searchInput).toBeVisible() - await expect(searchInput).toBeFocused() - await searchInput.fill('shortcuts') - - // Typing only lists results; the page moves on an explicit pick. - await expect(orcaPage.getByRole('option').first()).toBeVisible() - await expect( - orcaPage.getByRole('heading', { name: 'Shortcuts', exact: true }) - ).not.toBeVisible() - await searchInput.press('ArrowDown') - await searchInput.press('Enter') - await expect(orcaPage.getByRole('heading', { name: 'Shortcuts', exact: true })).toBeVisible() - - // The sidebar query no longer filters the pane, so every row and the pane's own search stay usable. - await expect(orcaPage.getByText('Go to file', { exact: true })).toBeVisible() - await expect(searchInput).toHaveValue('shortcuts') - const localSearch = orcaPage.getByPlaceholder('Search command or keys') - await localSearch.fill('go to') - await expect(orcaPage.getByText('Go to file', { exact: true })).toBeVisible() - await expect(orcaPage.getByText('Force reload', { exact: true })).not.toBeVisible() - }) -}) From f5c9075f0e0828d1d612d20676153d5e282f22fb Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 21:02:30 -0800 Subject: [PATCH 013/178] test: remove low-value provider and branch cleanup coverage (#41825) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 2 - config/reliability-gates.jsonc | 6 +- .../scripts/ci/sharding/ci-shard-timings.json | 8 - ...-foreground-process-git-bash.win32.test.ts | 93 +------ ...ath-existence-handlers.integration.test.ts | 44 ---- .../filesystem/local-provider.test.ts | 67 ----- .../foreground/agent-process.test.ts | 241 ------------------ .../pty/process-list-admission.test.ts | 102 -------- .../macos/tcc-login-shell.test.ts | 96 ------- .../windows/powershell-executable.test.ts | 91 ------- .../shell-preflight-runtime.windows.test.ts | 35 +-- .../validation-retention.test.ts | 167 ------------ .../working-directory/validation.test.ts | 33 +-- .../git-branch-cleanup-real-git.test.ts | 129 ---------- .../git/branches/git-branch-cleanup.test.ts | 106 -------- 15 files changed, 9 insertions(+), 1211 deletions(-) delete mode 100644 src/main/ipc/shell-path-existence-handlers.integration.test.ts delete mode 100644 src/main/providers/filesystem/local-provider.test.ts delete mode 100644 src/main/providers/foreground/agent-process.test.ts delete mode 100644 src/main/providers/pty/process-list-admission.test.ts delete mode 100644 src/main/providers/shell-launch/macos/tcc-login-shell.test.ts delete mode 100644 src/main/providers/shell-launch/windows/powershell-executable.test.ts delete mode 100644 src/main/providers/working-directory/validation-retention.test.ts delete mode 100644 src/shared/git/branches/git-branch-cleanup.test.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index 7661ea2fa67..f2c0bb53ddc 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -174,7 +174,6 @@ 1 test-no-real-time-wait:src/main/native-chat/agent-session-wire/recovery/testing/structured-agent-session-target-reconcile.test.ts 1 test-no-real-time-wait:src/main/provider-process/provider-group-orphan-guard.integration.test.ts 2 test-no-real-time-wait:src/main/provider-process/provider-process-supervisor.integration.test.ts -1 test-no-real-time-wait:src/main/daemon/pty-subprocess/testing/agent-foreground-process-git-bash.win32.test.ts 2 test-no-real-time-wait:src/main/pty/history/terminal-history-fish-session.node-pty.test.ts 1 test-no-real-time-wait:src/main/pty/process-retirement/node-pty-master-fd-retirement.test.ts 1 test-no-real-time-wait:src/main/pty/process-retirement/posix-pty-process-groups.integration.test.ts @@ -607,7 +606,6 @@ 1 test-timeout-needs-reason:src/main/orcad/host-lifecycle/managed-stop/orcad-stop-request-shutdown.integration.test.ts 1 test-timeout-needs-reason:src/main/orcad/native-runtime/orcad-windows-conpty-breakaway.integration.test.ts 1 test-timeout-needs-reason:src/main/persistence/applying-settings/persistence-source-control-ai-migration.test.ts -2 test-timeout-needs-reason:src/main/daemon/pty-subprocess/testing/agent-foreground-process-git-bash.win32.test.ts 1 test-timeout-needs-reason:src/main/providers/local-pty/local-pty-child-process-verdict.test.ts 5 test-timeout-needs-reason:src/main/pty/process-retirement/node-pty-master-fd-retirement.test.ts 1 test-timeout-needs-reason:src/main/runtime/mobile-cloud-relay/testing/mobile-relay-e2ee.integration.test.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 17e2a702bed..462e7691f8d 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -5777,12 +5777,12 @@ "providers": ["local", "daemon", "ssh", "remote-runtime", "wsl"], "coveredPlatforms": ["macos"], "coveredProviders": ["local", "daemon", "remote-runtime"], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3984 retires all nine mobile-presence and imported subscribe callbacks plus their orphan store fixture. Accepted losses include desktop/phone restore geometry, echo filtering, held reconnect/reclaim notifications, and the real headless snapshot sequence race. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial mobile-presence suite retains only five authored callbacks: admitted preview-paste completion across phone takeover, desktop take-back superseding mobile input, desktop viewport write/restore geometry, and held-fit reclaim convergence. Other supplied floor/rollback/selection/viewport/grace/disconnect/reconnect/orphan-state alarms are lost. No per-source assertion object exists to reassign. The one affected dated mobile-view-authority receipt is retired whole into historical notes with its full original/currentCommand/raw data preserved; it is not active corroboration for reduced candidate bytes. Imported mobile-subscribe registrations and retained callbacks remain bounded to their actual source obligations, without equivalent replacement or physical-phone/native-PTY claims. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1709 removes low-value test definitions and retires affected assertion or execution-receipt claims without equivalent replacement coverage. Per-source reviews bound retained behavior and injected/native/remote limits. Unchanged assertions and receipts keep their original scope; gates lacking current active evidence are downgraded. Historical scope before this audit: Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally deletes src/renderer/src/components/terminal-pane/pty-connection-hidden-tui-snapshot-replay.spec.ts: Loses rich hidden snapshot sequence replay/grid resize and forced repaint after hidden snapshot alarms, including live-trigger deduplication. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1320 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Deterministic host/parser, process-classifier, snapshot-publication, mixed-version, renderer, and macOS Electron coverage proves the local and shared paired-runtime contracts. Direct SSH intentionally publishes no shell proof and immediately preserves incumbent behavior. WSL, Windows ConPTY, Linux desktop, and paired headed/headless journeys remain gaps. The audit removed the isolated terminal-multiplex pending-escape-tail forwarding test. Its direct RPC snapshot field-copy alarm is lost; retained parser, snapshot and lifecycle suites do not establish equivalent RPC field coverage. Audit1320: src/renderer/src/components/terminal-pane/remote-runtime-pty-snapshot-escape-tail.test.ts removed. Snapshot pending escape/alternate-screen/shell metadata forwarding and negative app-store-read alarm disappears. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1391: src/renderer/src/components/terminal-pane/pty-connection-remote-pause-drop-notice.spec.ts reduced. Lose repeated quiet switches avoiding snapshots and legacy host without drop-report capability requesting a snapshot. Retained imported registrar uses fake transport/xterm writes, exercising real gap/restore lifecycle rather than actual terminal rendering. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1402: src/renderer/src/components/terminal-pane/pty-connection-park-reveal-model-snapshot.spec.ts reduced. Loses basic paired serialize/initial replay forwarding check; hidden alt-frame grid/precedence policy remains. No real terminal renderer claim. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1412: src/main/daemon/terminal-host-concurrent-create.test.ts reduced. Loses race deduplication, attach during unsettled ownership proof, retry after failed spawn, distinct-session parallelism, disposal waiting and shutdown retry-fencing alarms bundled with removed internal-call counts or real-time waits. No replacement-equivalence claim. src/main/daemon/terminal-checkpoint-serializer.test.ts reduced. Loses bounded native conversion/no-whole-candidate materialization and snapshot getter single-read alarms. Retained byte-size outcomes do not establish CPU/allocation bounds. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4345 deletes the foreground agent-process suite (8 authored cases): nested/stopped POSIX shells, recycled/query-denied Windows anchors and console-attachment filtering/refusal alarms are deliberately lost without equivalent replacement. Reduced selectors have no fresh gate execution; unrelated historical receipts retain their own commands. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3984 retires all nine mobile-presence and imported subscribe callbacks plus their orphan store fixture. Accepted losses include desktop/phone restore geometry, echo filtering, held reconnect/reclaim notifications, and the real headless snapshot sequence race. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial mobile-presence suite retains only five authored callbacks: admitted preview-paste completion across phone takeover, desktop take-back superseding mobile input, desktop viewport write/restore geometry, and held-fit reclaim convergence. Other supplied floor/rollback/selection/viewport/grace/disconnect/reconnect/orphan-state alarms are lost. No per-source assertion object exists to reassign. The one affected dated mobile-view-authority receipt is retired whole into historical notes with its full original/currentCommand/raw data preserved; it is not active corroboration for reduced candidate bytes. Imported mobile-subscribe registrations and retained callbacks remain bounded to their actual source obligations, without equivalent replacement or physical-phone/native-PTY claims. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1709 removes low-value test definitions and retires affected assertion or execution-receipt claims without equivalent replacement coverage. Per-source reviews bound retained behavior and injected/native/remote limits. Unchanged assertions and receipts keep their original scope; gates lacking current active evidence are downgraded. Historical scope before this audit: Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally deletes src/renderer/src/components/terminal-pane/pty-connection-hidden-tui-snapshot-replay.spec.ts: Loses rich hidden snapshot sequence replay/grid resize and forced repaint after hidden snapshot alarms, including live-trigger deduplication. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1320 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Deterministic host/parser, process-classifier, snapshot-publication, mixed-version, renderer, and macOS Electron coverage proves the local and shared paired-runtime contracts. Direct SSH intentionally publishes no shell proof and immediately preserves incumbent behavior. WSL, Windows ConPTY, Linux desktop, and paired headed/headless journeys remain gaps. The audit removed the isolated terminal-multiplex pending-escape-tail forwarding test. Its direct RPC snapshot field-copy alarm is lost; retained parser, snapshot and lifecycle suites do not establish equivalent RPC field coverage. Audit1320: src/renderer/src/components/terminal-pane/remote-runtime-pty-snapshot-escape-tail.test.ts removed. Snapshot pending escape/alternate-screen/shell metadata forwarding and negative app-store-read alarm disappears. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1391: src/renderer/src/components/terminal-pane/pty-connection-remote-pause-drop-notice.spec.ts reduced. Lose repeated quiet switches avoiding snapshots and legacy host without drop-report capability requesting a snapshot. Retained imported registrar uses fake transport/xterm writes, exercising real gap/restore lifecycle rather than actual terminal rendering. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1402: src/renderer/src/components/terminal-pane/pty-connection-park-reveal-model-snapshot.spec.ts reduced. Loses basic paired serialize/initial replay forwarding check; hidden alt-frame grid/precedence policy remains. No real terminal renderer claim. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1412: src/main/daemon/terminal-host-concurrent-create.test.ts reduced. Loses race deduplication, attach during unsettled ownership proof, retry after failed spawn, distinct-session parallelism, disposal waiting and shutdown retry-fencing alarms bundled with removed internal-call counts or real-time waits. No replacement-equivalence claim. src/main/daemon/terminal-checkpoint-serializer.test.ts reduced. Loses bounded native conversion/no-whole-candidate materialization and snapshot getter single-read alarms. Retained byte-size outcomes do not establish CPU/allocation bounds. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": ["https://github.com/stablyai/orca/pull/16379"], "invariant": "After a TUI exits or is killed, reveal, reattach, snapshot replay, or renderer remount must not deliver terminal-owned mouse or alternate-screen protocol bytes to the surviving shell. Recovery is an ordered output barrier in the daemon session data path: an OSC 133;D completing while the alternate screen is still active pauses the stream at that exact byte boundary, a fresh execution-host process inspection proves shell ownership, and on proof a mode reset is injected as in-stream output so every consumer converges by parsing the same bytes and the queued post-boundary shell output (the prompt) lands on the normal buffer. Snapshots are pure reads. Any failure — refuted proof, timeout, queue overflow, session death, disposal — flushes the queue unmodified, preserving incumbent behavior; later command or mode bytes revoke proof. Clean alternate-screen exits prove ownership asynchronously without pausing.", "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial mobile-presence suite retains only five authored callbacks: admitted preview-paste completion across phone takeover, desktop take-back superseding mobile input, desktop viewport write/restore geometry, and held-fit reclaim convergence. Other supplied floor/rollback/selection/viewport/grace/disconnect/reconnect/orphan-state alarms are lost. No per-source assertion object exists to reassign. The one affected dated mobile-view-authority receipt is retired whole into historical notes with its full original/currentCommand/raw data preserved; it is not active corroboration for reduced candidate bytes. Imported mobile-subscribe registrations and retained callbacks remain bounded to their actual source obligations, without equivalent replacement or physical-phone/native-PTY claims. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1709 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Historical pre-audit oracle account for changed sources: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): Run one fixed child-TUI journey for normal exit and cleanup-free SIGKILL. Assert renderer and host normal-buffer/non-mouse state, host snapshot terminalOwner metadata, exact PTY writes with no post-exit mouse report, unrelated-pane survival, post-boundary prompt output preserved (normal exit), ordered proof invalidation, bounded settlement and bail-out flush, one inspection per unclean episode with zero scans for ordinary output, split-escape safety at every chunk boundary, and old/new client-host fallback parity.", "commands": [ - "pnpm exec vitest run --config config/vitest.config.ts src/main/daemon/shell-readiness/testing/terminal-shell-recovery-barrier.test.ts src/main/daemon/sessions/testing/session-shell-recovery.test.ts src/main/daemon/terminal-host/testing/terminal-host-concurrent-create.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/history/restoration/testing/daemon-restore-scrollback-depth.test.ts src/main/daemon/history/restoration/testing/terminal-checkpoint-serializer.test.ts src/main/providers/foreground/agent-process.test.ts src/main/runtime/construction/orca-runtime.test.ts src/shared/terminal/protocol/escape/terminal-partial-escape-tail.test.ts src/shared/terminal/protocol/escape/terminal-partial-escape-tail.fuzz.test.ts --reporter=dot", + "pnpm exec vitest run --config config/vitest.config.ts src/main/daemon/shell-readiness/testing/terminal-shell-recovery-barrier.test.ts src/main/daemon/sessions/testing/session-shell-recovery.test.ts src/main/daemon/terminal-host/testing/terminal-host-concurrent-create.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/history/restoration/testing/daemon-restore-scrollback-depth.test.ts src/main/daemon/history/restoration/testing/terminal-checkpoint-serializer.test.ts src/main/runtime/construction/orca-runtime.test.ts src/shared/terminal/protocol/escape/terminal-partial-escape-tail.test.ts src/shared/terminal/protocol/escape/terminal-partial-escape-tail.fuzz.test.ts --reporter=dot", "pnpm exec vitest run --config config/vitest.config.ts tests/e2e/cross-version-wire/cross-version-terminal-wire.unit.test.ts --reporter=dot", "pnpm exec electron-vite build --mode e2e", "SKIP_BUILD=1 pnpm exec playwright test tests/e2e/terminal-hidden-child-tui-kill-mode-reset.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1" @@ -5794,7 +5794,6 @@ "src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts", "src/main/daemon/history/restoration/testing/daemon-restore-scrollback-depth.test.ts", "src/main/daemon/history/restoration/testing/terminal-checkpoint-serializer.test.ts", - "src/main/providers/foreground/agent-process.test.ts", "src/main/runtime/construction/orca-runtime.test.ts", "src/shared/terminal/protocol/escape/terminal-partial-escape-tail.test.ts", "src/shared/terminal/protocol/escape/terminal-partial-escape-tail.fuzz.test.ts", @@ -5860,6 +5859,7 @@ "Keep missing-proof mixed-version behavior identical to the incumbent client path." ], "knownGaps": [ + "Audit4345 deletes the foreground agent-process suite (8 authored cases): nested/stopped POSIX shells, recycled/query-denied Windows anchors and console-attachment filtering/refusal alarms are deliberately lost without equivalent replacement. Reduced selectors have no fresh gate execution; unrelated historical receipts retain their own commands.", "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed.", "Audit3984 retires all nine mobile-presence and imported subscribe callbacks plus their orphan store fixture. Accepted losses include desktop/phone restore geometry, echo filtering, held reconnect/reclaim notifications, and the real headless snapshot sequence race. No equivalent replacement or fresh gate execution is claimed.", "Audit3814 per-case accepted runtime losses:\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: resolves tui-idle from a Codex ready prompt even when stale startup lines remain :: Stale MCP startup lines must not block a later Codex ready header.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: resolves tui-idle when a stale Codex prompt is followed by the ready header :: A Codex ready header following a stale resume prompt must satisfy tui-idle.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: resolves tui-idle for an idle Cursor lane past its dismissed trust dialog (#8210) :: Later Cursor idle banner overrides retained trust dialog.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: does not block a mid-run Cursor lane on its dismissed trust dialog (#8210) :: Dismissed trust dialog must not report blocked for later busy Cursor; wait times out.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: does not classify unrelated press-enter prompts as Codex blocked prompts :: Unidentified press-enter text must time out rather than be classified Codex blocked.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: resolves tui-idle for quiet background PTY agents without OSC titles :: Quiet Codex background terminal without OSC title eventually resolves tui-idle.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: sends agent prompts as bracketed paste before submit :: Driving prompt escapes embedded paste-end ESC, writes one framed paste then CR, and counts bytes.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts :: settles a foreground Codex prompt when launch metadata has not arrived :: Codex inferred from foreground without launch metadata waits for composer marker/quiescence before one CR.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts :: gives a late Codex render marker a fresh quiescence window :: Marker arriving at7.9s re-arms fresh quiet window instead of submitting at initial cap.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts :: writes large agent prompt paste frames atomically before delayed submit :: A prompt larger than input chunk budget is a single paste write before delayed CR with exact byte count.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts :: rejects oversized terminal.send text before provider writes :: Oversized ordinary terminal.send rejects input-too-large before any provider write.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts :: rejects focusing an exited background terminal session :: focusTerminal refuses terminal_exited before invoking background reveal.\nsrc/main/runtime/orca-runtime-tests/agent-launch-prompt-submit.spec.ts :: pastes the same bytes the desktop draft paste sent for the prompt :: Launch-ready prompt normalizes CR/LF and renders embedded ESC visibly identically to desktop bracketed paste.\nsrc/main/runtime/orca-runtime-tests/agent-launch-prompt-submit.spec.ts :: keeps a prompt into a running agent behind the render settle, as before :: Driving prompt remains held through7s repaint activity then submits once by9s.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: reports a queued PTY focus as not navigated when its notifier disappears :: Queued PTY focus and completed active reveal report not navigated after notifier removal; no second reveal.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: does not invoke a stale graph-leaf focus notifier after queued PTY work :: Queued graph-leaf focus must not invoke old notifier after preceding PTY reveal and notifier removal.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: paginates retained terminal output with explicit limits and truncation metadata :: Preview120lines, fractional limits,50/200 pages, retained2000line truncation and future-cursor metadata stay consistent.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: falls back to renderer visible screen when uncursored TUI tail is blank :: Substantial blank uncursored tail uses mounted renderer visible screen with zero scrollback.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: reads and shows the runtime-owned alternate-screen grid without serialization :: Actual runtime alternate-screen grid provides read/show content without renderer/provider serialization.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: classifies older provider alternate-screen snapshots from ANSI :: Older provider without alternateScreen metadata classifies ANSI alternate entry and returns Remote Vim screen.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: honors the last ANSI screen transition when older provider metadata is absent :: Last ANSI alternate exit overrides earlier entry when explicit provider mode metadata is absent.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: rejects a full provider screen frame after live output advances :: Live output arriving during second full-screen capture causes screen-unavailable rather than stale full frame.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: shares one provider snapshot between concurrent read and show calls :: Concurrent read/show share one successful provider snapshot and both receive Shared TUI content.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: keeps cursor reads provider-free on restored alternate-screen PTYs :: Restored alternate-screen cursor transcript reads never acquire provider snapshot.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: backs off after a provider snapshot failure instead of retrying for show :: Failed provider capture is cached/backed off so later show does not retry.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: falls back once to a mounted renderer when a known alternate provider fails :: A known-alternate failed provider capture falls back exactly once to newly mounted renderer.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: bounds provider visible frames for read and show responses :: Provider visible read/show use six bounded lines and300character preview.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: keeps renderer-fallback composer drafts separate from terminal output :: Renderer fallback strips dim composer draft from terminal output and exposes draft separately.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: does not use renderer visible-screen fallback for a short blank shell tail :: Two blank shell lines must not trigger renderer screen fallback.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: trims terminal tail character budget without per-line array shifts :: Tail builder trims to256KiB with correct retained suffix and no Array.shift calls.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: retains ANSI-only status redraws instead of appending every frame :: Repeated erase/redraw replaces preview until newline then publishes one completed cursor row.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: retains split multi-line ANSI footer redraw state across chunks :: CUU state across chunks retains corrected footer rows and publishes new cursor entries.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: does not let stale lower rows hide earlier corrected footer rows from cursor reads :: Earlier corrected footer row must not be hidden by stale lower rows in cursor reads.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: bounds completed transcript entries from a single multi-line redraw chunk :: Single redraw chunk bounds completed transcript to2000retained lines and correct oldest/next/latest cursors.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: retains multi-line ANSI redraws when the last footer row stays partial :: Partial final redrawn row appears in preview but cursor sees it only after newline.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: does not retain split ANSI controls as visible terminal preview text :: Split color/mode escapes never leak preview fragments; long split OSC title truncates at MAX_OSC_TITLE_CHARS.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: retains same-line redraw cursor position across split full-line erase chunks :: Full-line erase preserves column across chunks so XY follows three spaces rather than old ABC.\nsrc/main/runtime/orca-runtime-tests/conpty-stream-read.spec.ts :: fills the requested line count even when blank rows sit below the prompt :: Blank rows below a ConPTY prompt must not consume the requested nonempty-line count.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-12.spec.ts :: does not retain split ST-terminated string controls as preview text :: Split ST-terminated Kitty/string controls must remain excluded from preview text.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: detects split OSC titles before retaining terminal previews :: Split BEL OSC title must update working title/status without leaking into preview.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: detects ST-terminated OSC titles split before the final backslash :: ST OSC split before final backslash must update title without leaking framing.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: preserves a trailing escape after a completed OSC title :: Completed OSC followed by pending ESC must preserve the next idle-title transition.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: seeds newly synced leaves from PTY pending ANSI state :: A newly synced graph leaf inherits pending ANSI so a split color fragment cannot leak.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: bounds retained partial terminal output before preview reads :: Partial terminal state stays capped at 4000, preserves retained array identity, and preview truncation/cursor output remains correct.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: submits the mail pointer to Cursor Agent only after the paste-burst delay :: Cursor mailbox pointer Enter must wait through the paste-burst delay; this is a real input-timing alarm intentionally retired.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-02.spec.ts :: adopts a v1.4.150-shaped agent, setup, and shell orphan as one topology transaction :: Stale incarnation refuses without topology change; three agent/setup/shell lanes adopt atomically with layout/group/revision and preserved read/input/resize; exact replay is idempotent and competing owner refuses.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-03.spec.ts :: retires the exact worker when it exits after renderer reveal :: Exact worker exit after reveal retires that worker and its surface.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-03.spec.ts :: re-reveals a recovered worker after the renderer graph epoch changes :: Changed renderer graph epoch invalidates reveal receipt and forces another reveal.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-03.spec.ts :: defers recovery when the renderer omits the exact reveal identity :: Missing exact reveal identity defers recovery after two attempts.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-03.spec.ts :: reconciles a same-id process replacement before headless adoption :: A process replacement before headless adoption must fence off stale ownership.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-04.spec.ts :: retires provider resume only after authoritative inventory proves the legacy PTY exited :: Authoritative owning-host inventory overrides false local hasPty, avoids SSH scanning, retires exited provider resume/surface, adopts live workers without headless reveal.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-04.spec.ts :: retries inventory and unknown liveness without revealing a ghost worker :: Inventory failure and unknown liveness must retry without ghost reveal, eventually adopt, and stop retrying.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-12.spec.ts :: refuses a live reserved pane before issuing a handle or revealing it :: A requireFreshPane launch receiving a live reserved owner must reject before handle/reveal/kill, and release its claim. No equivalent replacement claimed.", diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index ab37b939b62..4c66f4ecdf9 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -2762,8 +2762,6 @@ "src/main/provider-process/provider-process-supervisor.integration.test.ts": 7485, "src/main/provider-process/provider-process-teardown.test.ts": 146, "src/main/daemon/pty-subprocess/testing/agent-foreground-process-git-bash.win32.test.ts": 361, - "src/main/providers/foreground/agent-process.test.ts": 227, - "src/main/providers/filesystem/local-provider.test.ts": 344, "src/main/providers/local-pty/local-pty-child-process-verdict.test.ts": 676, "src/main/providers/local-pty/local-pty-default-shell.test.ts": 272, "src/main/providers/local-pty/local-pty-executable-spawn.test.ts": 96, @@ -2776,12 +2774,7 @@ "src/main/providers/local-pty/local-pty-shell-ready-startup-command.test.ts": 54, "src/main/providers/local-pty/local-pty-shell-ready-wrapper-generation.test.ts": 558, "src/main/providers/local-pty/local-pty-shell-startup-command.node-pty.test.ts": 315, - "src/main/providers/shell-launch/macos/tcc-login-shell.test.ts": 182, - "src/main/providers/pty/process-list-admission.test.ts": 138, - "src/main/ipc/shell-path-existence-handlers.integration.test.ts": 337, - "src/main/providers/shell-launch/windows/powershell-executable.test.ts": 65, "src/main/providers/shell-launch/windows/shell-preflight-runtime.windows.test.ts": 126, - "src/main/providers/working-directory/validation-retention.test.ts": 132, "src/main/providers/working-directory/validation.test.ts": 163, "src/main/workspaces/catalog/provisioned-root-checkout.test.ts": 115, "src/main/pty/process-retirement/pty-descendant-exit-verification.test.ts": 155, @@ -6290,7 +6283,6 @@ "src/shared/git/compatibility/git-binary-compatibility.test.ts": 377, "src/shared/git/diff/git-blob-absence.test.ts": 54, "src/shared/git/branches/git-branch-cleanup-real-git.test.ts": 883, - "src/shared/git/branches/git-branch-cleanup.test.ts": 79, "src/shared/git/status/git-branch-line-total-soft-deadline.test.ts": 74, "src/shared/git/status/git-branch-line-total.test.ts": 135, "src/shared/git/diff/git-change-list.test.ts": 133, diff --git a/src/main/daemon/pty-subprocess/testing/agent-foreground-process-git-bash.win32.test.ts b/src/main/daemon/pty-subprocess/testing/agent-foreground-process-git-bash.win32.test.ts index fc205a15f1d..35691e9686f 100644 --- a/src/main/daemon/pty-subprocess/testing/agent-foreground-process-git-bash.win32.test.ts +++ b/src/main/daemon/pty-subprocess/testing/agent-foreground-process-git-bash.win32.test.ts @@ -1,6 +1,6 @@ -import { mkdtempSync, rmdirSync, symlinkSync } from 'node:fs' +import { mkdtempSync } from 'node:fs' import { tmpdir } from 'node:os' -import { dirname, join } from 'node:path' +import { join } from 'node:path' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' import { removeTreeSync } from '../../../../shared/windows-transient-lock-removal' import { WINDOWS_GIT_BASH_SHELL } from '../../../../shared/windows-terminal-shell' @@ -8,13 +8,9 @@ import { confirmPtyShellForeground } from '../pty-shell-foreground-confirmation' import { createPtyShellLaunchPlan, type PtyShellLaunchPlan } from '../shell-launch-plan' import { spawnNativeDaemonPty } from '../native-pty-spawn' import { createDaemonPtyEnvironment } from '../spawn-environment' -import { inspectSpawnFileWindowsChildProcesses } from '../spawn-file-child-processes' import type { PtySubprocessOptions } from '../pty-subprocess' -import { - isGitForWindowsBashLauncherPath, - resolveGitBashPath -} from '../../../pty/shell-startup/git-bash' +import { isGitForWindowsBashLauncherPath } from '../../../pty/shell-startup/git-bash' import { readWindowsPtyJobProcessIds } from '../../../providers/foreground/windows/pty-job-membership' const describeOnWindows = process.platform === 'win32' ? describe : describe.skip @@ -100,11 +96,7 @@ describeOnWindows("Git Bash launcher shell proof with Orca's real launch", () => removeTreeSync(userData) }) - it.each([ - ['login shell', {}], - // Why: with the Codex preflight set, the exec'd shell runs Orca's --rcfile wrapper. - ['rcfile wrapper', { ORCA_CODEX_LAUNCH_PREFLIGHT: 'C:\\orca-missing-preflight.exe' }] - ])( + it.each([['login shell', {}]])( 'confirms an idle %s prompt, refutes a running command, and confirms again', async (_label, extraEnv) => { const opts: PtySubprocessOptions = { @@ -124,81 +116,4 @@ describeOnWindows("Git Bash launcher shell proof with Orca's real launch", () => }, 60_000 ) - - it('confirms an idle prompt for an install folder named neither Git nor PortableGit', async () => { - const discovered = resolveGitBashPath() - if (!discovered) { - throw new Error('this host has no Git for Windows install to stage') - } - expect(isGitForWindowsBashLauncherPath(discovered)).toBe(true) - // A junction, not a copy: the operator's install is neither modified nor duplicated. - const stagingRoot = mkdtempSync(join(tmpdir(), 'orca-git-renamed-')) - const stagedInstall = join(stagingRoot, 'Git-2.55') - symlinkSync(dirname(dirname(discovered)), stagedInstall, 'junction') - const opts: PtySubprocessOptions = { - sessionId: 'git-bash-renamed-shell-proof', - cols: 120, - rows: 30, - cwd: tmpdir(), - shellOverride: join(stagedInstall, 'bin', 'bash.exe') - } - try { - const env = createDaemonPtyEnvironment(opts) - const plan = createPtyShellLaunchPlan(opts, env) - expect(plan.shellPath).toBe(opts.shellOverride) - expect(isGitForWindowsBashLauncherPath(plan.shellPath)).toBe(true) - // No Git Bash startup args for an unrecognized folder, so no exec stub: launcher -> bash. - await proveIdlePromptCycle(await spawnPlannedPane(plan, env, opts), 2) - } finally { - // rmdir, never a recursive remove: it detaches the junction and cannot reach the target. - rmdirSync(stagedInstall) - removeTreeSync(stagingRoot) - } - }, 60_000) - - it('sets aside a detached ssh-agent for adoption, but not a background job', async () => { - const opts: PtySubprocessOptions = { - sessionId: 'git-bash-detached-daemon-proof', - cols: 120, - rows: 30, - cwd: tmpdir(), - shellOverride: WINDOWS_GIT_BASH_SHELL - } - const env = createDaemonPtyEnvironment(opts) - const spawned = await spawnPlannedPane(createPtyShellLaunchPlan(opts, env), env, opts) - const proc = spawned.process - let output = spawned.claimStartupEvents?.().data.join('') ?? '' - proc.onData((chunk) => { - output += chunk - }) - let agentPid = 0 - const inspect = (ignoreDetached: boolean) => - inspectSpawnFileWindowsChildProcesses(proc, spawned.shellPath, true, ignoreDetached) - try { - await vi.waitFor(() => expect(output).toContain('$'), { timeout: 20_000 }) - // ssh-agent forks, setsids off the console, and its Win32 parent exits. - proc.write('eval "$(ssh-agent -s)"; echo ORCA_AGENT_$SSH_AGENT_PID\r') - // Usually ~1s, but it exceeded 10s on a loaded runner; the bound only catches a hang. - await vi.waitFor(() => expect(output).toMatch(/ORCA_AGENT_\d+/), { timeout: 30_000 }) - agentPid = Number(/ORCA_AGENT_(\d+)/.exec(output)?.[1]) - await vi.waitFor(async () => expect(await inspect(true)).toBe('no-children'), { - timeout: 10_000 - }) - // Close checks keep the job census: the daemon still counts there. - expect(await inspect(false)).not.toBe('no-children') - - // MSYS leaves the background sleep under an exited Win32 parent too, but on the console. - proc.write('sleep 60 &\r') - await vi.waitFor(async () => expect(await inspect(true)).toBe('children'), { - timeout: 10_000 - }) - } finally { - // From inside the pane: SSH_AGENT_PID is an MSYS pid, and it outlives the pane otherwise. - if (agentPid) { - proc.write(`kill %1; kill ${agentPid}\r`) - await new Promise((resolve) => setTimeout(resolve, 500)) - } - proc.kill() - } - }, 90_000) }) diff --git a/src/main/ipc/shell-path-existence-handlers.integration.test.ts b/src/main/ipc/shell-path-existence-handlers.integration.test.ts deleted file mode 100644 index 72d710d66f8..00000000000 --- a/src/main/ipc/shell-path-existence-handlers.integration.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { afterEach, expect, it, vi } from 'vitest' -import { mkdtemp, writeFile, rm } from 'node:fs/promises' -import { join } from 'node:path' -import { tmpdir } from 'node:os' -const handlers = vi.hoisted(() => new Map Promise>()) -vi.mock('electron', () => ({ - ipcMain: { - handle: (name: string, fn: (...args: unknown[]) => Promise) => handlers.set(name, fn) - }, - shell: {}, - dialog: {} -})) -import { registerShellHandlers } from './shell' -let root: string | undefined -afterEach(async () => { - if (root) { - await rm(root, { recursive: true, force: true }) - } - root = undefined - handlers.clear() -}) -async function fixture() { - root = await mkdtemp(join(tmpdir(), 'orca-link-batch-')) - const paths = Array.from({ length: 8 }, (_, i) => join(root!, `file-${i}.ts`)) - await Promise.all(paths.map((path) => writeFile(path, 'fixture'))) - return paths -} -it('one actual shell IPC handler probes eight distinct temporary files and retains scalar answers', async () => { - const paths = await fixture() - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: This registration fixture never invokes unrelated store operations. - registerShellHandlers({} as never) - const all = [...paths, join(root!, 'missing'), root!] - expect(await handlers.get('shell:pathsExist')!(null, all)).toEqual( - await Promise.all(all.map((path) => handlers.get('shell:pathExists')!(null, path))) - ) - expect(await handlers.get('shell:pathsExist')!(null, all)).toEqual([ - ...paths.map(() => true), - false, - true - ]) - await expect(handlers.get('shell:pathsExist')!(null, Array(129).fill('x'))).rejects.toThrow( - 'Invalid' - ) -}) diff --git a/src/main/providers/filesystem/local-provider.test.ts b/src/main/providers/filesystem/local-provider.test.ts deleted file mode 100644 index 08557b66e5e..00000000000 --- a/src/main/providers/filesystem/local-provider.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { mkdir, mkdtemp, readFile, readlink, rm, symlink, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { RuntimeStore } from '../../runtime/construction/runtime-store-contract' -import { createLocalFilesystemProvider } from './local-provider' - -let root: string -// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the provider reads only settings from the store; authorization is the injected stub. -const store = { - getSettings: () => ({ followSymlinkedDirectories: true }) -} as unknown as RuntimeStore -const authorize = vi.fn(async (path: string, _store: RuntimeStore, _options?: unknown) => path) -const provider = () => - createLocalFilesystemProvider({ requireStore: () => store, resolveAuthorizedPath: authorize }) - -beforeEach(async () => { - root = await mkdtemp(join(tmpdir(), 'orca-local-fs-provider-')) - authorize.mockClear() -}) -afterEach(() => rm(root, { recursive: true, force: true })) - -describe('createLocalFilesystemProvider', () => { - it('authorizes every path against the injected store before touching it', async () => { - const file = join(root, 'a', 'b.txt') - await provider().writeFileBase64(file, Buffer.from('hi').toString('base64')) - await provider().writeFileBase64Chunk(file, Buffer.from('!').toString('base64'), true) - expect(await readFile(file, 'utf8')).toBe('hi!') - expect(authorize).toHaveBeenCalledWith(file, store) - await expect(provider().writeFileBase64(file, '')).rejects.toMatchObject({ code: 'EEXIST' }) - }) - - // Why: creating symlinks needs elevated rights on Windows runners. - it.skipIf(process.platform === 'win32')( - 'renames, copies and deletes a symlink itself rather than its target', - async () => { - const target = join(root, 'target.txt') - const link = join(root, 'link.txt') - await writeFile(target, 'body') - await symlink(target, link) - await provider().renameNoClobber(link, join(root, 'moved.txt')) - expect(await readlink(join(root, 'moved.txt'))).toBe(target) - await provider().copy(target, join(root, 'nested', 'copy.txt')) - await expect(provider().copy(target, join(root, 'nested', 'copy.txt'))).rejects.toMatchObject( - { code: 'EEXIST' } - ) - await provider().deletePath(join(root, 'moved.txt')) - expect(await readFile(target, 'utf8')).toBe('body') - for (const call of authorize.mock.calls.filter(([path]) => path !== target)) { - expect(call[2]).toEqual({ preserveSymlink: true }) - } - } - ) - - it.skipIf(process.platform === 'win32')( - 'follows symlinked directories by the store setting unless the caller overrides it', - async () => { - await mkdir(join(root, 'real')) - await symlink(join(root, 'real'), join(root, 'alias')) - const isAliasDir = async (followSymlinks?: boolean) => - (await provider().readDir(root, { followSymlinks })).find((entry) => entry.name === 'alias') - ?.isDirectory - expect(await isAliasDir()).toBe(true) - expect(await isAliasDir(false)).toBe(false) - } - ) -}) diff --git a/src/main/providers/foreground/agent-process.test.ts b/src/main/providers/foreground/agent-process.test.ts deleted file mode 100644 index 5e27a97f83b..00000000000 --- a/src/main/providers/foreground/agent-process.test.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' - -const { execFileMock } = vi.hoisted(() => ({ - execFileMock: vi.fn() -})) - -vi.mock('child_process', () => ({ - execFile: execFileMock -})) - -const getAllProcessesMock = vi.fn() - -import { resetProcessTableSnapshotForTests } from '../../../shared/process-table-snapshot-reader' -import { __setWindowsProcessTreeLoaderForTests } from '../../windows/windows-process-table' -import { - confirmShellForegroundProcess, - resolveAgentForegroundProcessWithAvailability -} from './agent-process' -// Toolhelp denial may return an empty table; require the querying process to distinguish a readable snapshot. -const SELF_PROCESS_ROW = { pid: process.pid, ppid: 0, name: 'vitest.exe', commandLine: 'vitest' } -const withSelf = (rows: readonly T[]): (T | typeof SELF_PROCESS_ROW)[] => [ - SELF_PROCESS_ROW, - ...rows -] - -// Why: the POSIX reader wraps execFile with promisify, so the mock must honor -// the Node callback contract — invoke the last arg with (err, { stdout, stderr }). -function mockPs(stdout: string): void { - execFileMock.mockImplementation((_cmd: string, _args: string[], _opts: unknown, cb: unknown) => { - const callback = cb as (err: unknown, result: { stdout: string; stderr: string }) => void - callback(null, { stdout, stderr: '' }) - }) -} - -type NativeProcessRow = { - pid: number - ppid: number - name: string - commandLine?: string -} - -const DEFAULT_WINDOWS_ROWS: NativeProcessRow[] = [ - { - pid: 100, - ppid: 99, - name: 'powershell.exe', - commandLine: 'powershell.exe' - }, - { - pid: 101, - ppid: 100, - name: 'node.exe', - commandLine: 'node C:\\Users\\dev\\AppData\\Roaming\\npm\\codex.cmd' - } -] - -function mockWindowsRows(rows: NativeProcessRow[] = DEFAULT_WINDOWS_ROWS): void { - getAllProcessesMock.mockImplementation((cb: (snapshot: NativeProcessRow[]) => void) => { - cb(withSelf(rows)) - }) -} - -describe('resolveAgentForegroundProcess', () => { - let platform: PropertyDescriptor | undefined - - beforeEach(() => { - execFileMock.mockReset() - getAllProcessesMock.mockReset() - resetProcessTableSnapshotForTests() - // Reset the 500ms Windows snapshot cache so each case reads its own rows. - __setWindowsProcessTreeLoaderForTests(() => ({ - ProcessDataFlag: { None: 0, Memory: 1, CommandLine: 2, CreationTime: 4 }, - getAllProcesses: getAllProcessesMock - })) - platform = Object.getOwnPropertyDescriptor(process, 'platform') - Object.defineProperty(process, 'platform', { configurable: true, value: 'darwin' }) - }) - - afterEach(() => { - __setWindowsProcessTreeLoaderForTests() - if (platform) { - Object.defineProperty(process, 'platform', platform) - } - }) - - // Why: OMP embeds Pi, but the outer process is the user-visible identity (#6364). - - it('rejects a foreground nested shell while the spawned shell remains suspended', async () => { - mockPs( - [ - '100 99 100 102 Ss /usr/bin/login -pfl developer /bin/zsh', - '101 100 101 102 S -zsh', - '102 101 102 102 S+ agent-tui', - '103 102 102 102 S+ /bin/zsh -i' - ].join('\n') - ) - - await expect(confirmShellForegroundProcess(100, '/bin/zsh')).resolves.toBe(false) - }) - - it('rejects shell ownership while a stopped TUI remains resumable', async () => { - mockPs(['100 99 100 100 Ss+ /bin/zsh -l', '101 100 101 100 T agent-tui'].join('\n')) - - await expect(confirmShellForegroundProcess(100, 'zsh')).resolves.toBe(false) - }) - - it('detects a foreign anchor even when the squatter is orphaned out of the descendant walk', async () => { - // PID reuse can leave the squatter outside the shell ancestry while retaining job membership. - Object.defineProperty(process, 'platform', { value: 'win32' }) - mockWindowsRows([ - { pid: 100, ppid: 99, name: 'powershell.exe', commandLine: 'powershell.exe' }, - { pid: 999, ppid: 500, name: 'node.exe', commandLine: 'node server.js' } - ]) - - await expect( - resolveAgentForegroundProcessWithAvailability(100, 'powershell.exe', { - anchorProcessId: 999 - }) - ).resolves.toEqual({ - available: true, - processName: 'powershell.exe', - anchorPidForeign: true - }) - }) - - it('flags an anchor recycled by a DIFFERENT agent as foreign', async () => { - Object.defineProperty(process, 'platform', { value: 'win32' }) - mockWindowsRows([ - { pid: 100, ppid: 99, name: 'powershell.exe', commandLine: 'powershell.exe' }, - { pid: 999, ppid: 500, name: 'node.exe', commandLine: 'node /usr/bin/codex' } - ]) - - await expect( - resolveAgentForegroundProcessWithAvailability(100, 'powershell.exe', { - anchorProcessId: 999, - anchorProcessName: 'claude' - }) - ).resolves.toEqual({ - available: true, - processName: 'powershell.exe', - anchorPidForeign: true - }) - }) - - it('treats a query-denied anchor row as inconclusive, never foreign', async () => { - // A denied query yields command === name; the agent may just be unreadable. - Object.defineProperty(process, 'platform', { value: 'win32' }) - mockWindowsRows([ - { pid: 100, ppid: 99, name: 'powershell.exe', commandLine: 'powershell.exe' }, - { pid: 999, ppid: 100, name: 'node.exe', commandLine: '' } - ]) - - await expect( - resolveAgentForegroundProcessWithAvailability(100, 'powershell.exe', { - anchorProcessId: 999 - }) - ).resolves.toEqual({ available: true, processName: 'powershell.exe' }) - }) - - it('filters detached agents before resolving an otherwise ambiguous ConPTY tree', async () => { - Object.defineProperty(process, 'platform', { value: 'win32' }) - mockWindowsRows([ - { - pid: 100, - ppid: 99, - name: 'powershell.exe', - commandLine: 'powershell.exe' - }, - { - pid: 101, - ppid: 100, - name: 'droid.exe', - commandLine: 'droid' - }, - { - pid: 102, - ppid: 100, - name: 'agy.exe', - commandLine: 'agy' - } - ]) - - await expect( - resolveAgentForegroundProcessWithAvailability(100, 'powershell.exe', { - fresh: true, - readWindowsConsoleAttachedProcessIds: async () => new Set([100, 101]) - }) - ).resolves.toEqual({ - available: true, - processName: 'droid', - processId: 101 - }) - }) - - it('fails closed when console attachment cannot be read', async () => { - // Unreadable console attachment cannot authorize a detached descendant as the pane agent (#16419). - Object.defineProperty(process, 'platform', { value: 'win32' }) - mockWindowsRows([ - { pid: 200, ppid: 199, name: 'powershell.exe', commandLine: 'powershell.exe' }, - { pid: 201, ppid: 200, name: 'droid.exe', commandLine: 'droid' }, - { pid: 202, ppid: 200, name: 'agy.exe', commandLine: 'agy' } - ]) - - const reader = vi.fn(async () => null) - - const resolution = await resolveAgentForegroundProcessWithAvailability(200, 'powershell.exe', { - fresh: true, - readWindowsConsoleAttachedProcessIds: reader - }) - - expect(reader).toHaveBeenCalledTimes(1) - // Unavailable evidence must yield the shell fallback, never an unverified agent identity. - expect(resolution.available).toBe(false) - expect(resolution.processName).toBe('powershell.exe') - }) - - it('excludes a detached Windows Droid descendant from byte authority', async () => { - Object.defineProperty(process, 'platform', { value: 'win32' }) - mockWindowsRows([ - { - pid: 100, - ppid: 99, - name: 'powershell.exe', - commandLine: 'powershell.exe' - }, - { - pid: 101, - ppid: 100, - name: 'droid.exe', - commandLine: 'droid' - } - ]) - - await expect( - resolveAgentForegroundProcessWithAvailability(100, 'powershell.exe', { - fresh: true, - readWindowsConsoleAttachedProcessIds: async () => new Set([100, 999]) - }) - ).resolves.toEqual({ available: true, processName: 'powershell.exe' }) - }) -}) diff --git a/src/main/providers/pty/process-list-admission.test.ts b/src/main/providers/pty/process-list-admission.test.ts deleted file mode 100644 index d83b2a1383e..00000000000 --- a/src/main/providers/pty/process-list-admission.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - MAX_AGGREGATED_PTY_PROCESS_LIST_BYTES, - MAX_AGGREGATED_PTY_PROCESS_LIST_ENTRIES, - PtyProcessListAdmission -} from './process-list-admission' -import { MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES as MAX_AGGREGATED_PTY_PROCESS_LIST_OWNERS } from '../../../shared/claimed-agent-pty-owner' - -describe('PtyProcessListAdmission', () => { - const evidence = { - verdict: 'live' as const, - processName: 'codex', - authorityGeneration: 'relay-generation', - observationEpoch: 4, - capturedAgeMs: 12 - } - - it('rejects malformed foreground evidence instead of stripping it', () => { - expect(() => - new PtyProcessListAdmission().admit({ - id: 'pty-1', - cwd: '/repo', - title: 'shell', - foregroundProcessEvidence: { ...evidence, verdict: 'wat' } - } as never) - ).toThrow('invalid_pty_process_list') - }) - - it('carries a WSL guest root and rejects a malformed one instead of stripping it', () => { - const session = { - id: 'pty', - cwd: '/', - title: 'shell', - wslDistro: 'Ubuntu', - wslGuestRoot: { pid: 4242, startTime: '0b1d2c3e-4f50-4617-8293-a4b5c6d7e8f9:98765' } - } - expect(new PtyProcessListAdmission().admit(session)).toEqual(session) - expect(() => - new PtyProcessListAdmission().admit({ ...session, wslGuestRoot: { pid: 1, startTime: 'x' } }) - ).toThrow('invalid_pty_process_list') - }) - - it('admits diagnostic provenance and rejects malformed routing evidence', () => { - const session = { - id: 'pty', - cwd: '/', - title: 'shell', - inventorySourceId: 'source', - routed: false - } - expect(new PtyProcessListAdmission().admit(session)).toEqual(session) - Reflect.set(session, 'routed', 'yes') - expect(() => new PtyProcessListAdmission().admit(session)).toThrow('invalid_pty_process_list') - Reflect.set(session, 'routed', false) - Reflect.set(session, 'inventorySourceId', 1) - expect(() => new PtyProcessListAdmission().admit(session)).toThrow('invalid_pty_process_list') - }) - - it('strips unknown provider payloads from admitted process metadata', () => { - const admission = new PtyProcessListAdmission() - - expect( - admission.admit({ - id: 'pty-1', - cwd: '/repo', - title: 'shell', - unknownPayload: 'x'.repeat(1024 * 1024) - } as never) - ).toEqual({ id: 'pty-1', cwd: '/repo', title: 'shell' }) - }) - - it('rejects aggregate entry and byte amplification', () => { - const entryAdmission = new PtyProcessListAdmission() - for (let index = 0; index < MAX_AGGREGATED_PTY_PROCESS_LIST_ENTRIES; index += 1) { - entryAdmission.admit({ id: `pty-${index}`, cwd: '', title: 'shell' }) - } - expect(() => entryAdmission.admit({ id: 'one-more', cwd: '', title: 'shell' })).toThrow( - 'pty_process_list_capacity' - ) - - const byteAdmission = new PtyProcessListAdmission() - expect(() => - byteAdmission.admit({ - id: 'pty-large', - cwd: 'x'.repeat(MAX_AGGREGATED_PTY_PROCESS_LIST_BYTES), - title: 'shell' - }) - ).toThrow('pty_process_list_capacity') - - expect(() => - new PtyProcessListAdmission().admit({ - id: 'pty-owner-flood', - cwd: '', - title: 'shell', - agentSessionOwners: Array.from( - { length: MAX_AGGREGATED_PTY_PROCESS_LIST_OWNERS + 1 }, - () => ({}) - ) - } as never) - ).toThrow('pty_process_list_capacity') - }) -}) diff --git a/src/main/providers/shell-launch/macos/tcc-login-shell.test.ts b/src/main/providers/shell-launch/macos/tcc-login-shell.test.ts deleted file mode 100644 index a11ba979e20..00000000000 --- a/src/main/providers/shell-launch/macos/tcc-login-shell.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type * as LoginSessionPtyProbe from './login-session-pty-probe' - -const { existsSyncMock, userInfoMock, runProcessMock, ptyProbeMock } = vi.hoisted(() => ({ - existsSyncMock: vi.fn(), - userInfoMock: vi.fn(), - runProcessMock: vi.fn(), - ptyProbeMock: vi.fn() -})) - -vi.mock('node:fs', () => ({ existsSync: existsSyncMock })) -vi.mock('node:os', () => ({ userInfo: userInfoMock })) -// Process-host owns encoding/signals/caps; this suite checks PAM argv and verdicts. -vi.mock('@orca/process-host', () => ({ - runProcess: (...args: unknown[]) => Promise.resolve(runProcessMock(...args)) -})) -vi.mock('./login-session-pty-probe', async (importOriginal) => ({ - ...(await importOriginal()), - runMacosLoginSessionPtyProbe: ptyProbeMock -})) - -import { - prepareMacosTccLoginShell, - probeMacosLoginSessionAlive, - resetMacosLoginShellPreflightForTests, - wrapShellSpawnForMacosTccAttribution -} from './tcc-login-shell' - -const ACCEPTED_RESULT = { - code: 0, - signal: null, - stdout: 'ORCA_LOGIN_PREFLIGHT_OK', - stderr: '', - timedOut: false -} -const REJECTED_RESULT = { code: 1, signal: null, stdout: '', stderr: '', timedOut: false } -const ACCEPTED_OUTCOME = { ok: true, conclusive: true, reason: 'accepted' } as const -const REJECTED_OUTCOME = { ok: false, conclusive: true, reason: 'rejected' } as const -void (process.platform === 'win32' ? it.skip : it) - -describe('probeMacosLoginSessionAlive', () => { - let origPlatform: PropertyDescriptor | undefined - let origDisable: string | undefined - - function setPlatform(value: string): void { - Object.defineProperty(process, 'platform', { configurable: true, value }) - } - - beforeEach(() => { - origPlatform = Object.getOwnPropertyDescriptor(process, 'platform') - origDisable = process.env.ORCA_DISABLE_MACOS_LOGIN_SHELL - delete process.env.ORCA_DISABLE_MACOS_LOGIN_SHELL - existsSyncMock.mockReturnValue(true) - userInfoMock.mockReturnValue({ username: 'ada', homedir: '/Users/ada' }) - runProcessMock.mockResolvedValue({ - code: 0, - signal: null, - stdout: 'ORCA_LOGIN_PREFLIGHT_OK', - stderr: '', - timedOut: false - }) - ptyProbeMock.mockResolvedValue({ ok: true, conclusive: true, reason: 'accepted' }) - resetMacosLoginShellPreflightForTests() - }) - - afterEach(() => { - if (origPlatform) { - Object.defineProperty(process, 'platform', origPlatform) - } - if (origDisable === undefined) { - delete process.env.ORCA_DISABLE_MACOS_LOGIN_SHELL - } else { - process.env.ORCA_DISABLE_MACOS_LOGIN_SHELL = origDisable - } - vi.restoreAllMocks() - vi.clearAllMocks() - }) - - it('does not let a spawn-path probe overwrite a newer death verdict', async () => { - setPlatform('darwin') - ptyProbeMock.mockResolvedValue(REJECTED_OUTCOME) - const finishers: ((result: typeof ACCEPTED_RESULT) => void)[] = [] - runProcessMock.mockImplementation(() => new Promise((resolve) => finishers.push(resolve))) - - const freshProbe = probeMacosLoginSessionAlive() - const spawnProbe = prepareMacosTccLoginShell() - expect(runProcessMock).toHaveBeenCalledTimes(2) - - finishers[0](REJECTED_RESULT) - await expect(freshProbe).resolves.toEqual(REJECTED_OUTCOME) - finishers[1](ACCEPTED_RESULT) - await expect(spawnProbe).resolves.toEqual(ACCEPTED_OUTCOME) - - expect(wrapShellSpawnForMacosTccAttribution('/bin/zsh', ['-l']).file).toBe('/bin/zsh') - }) -}) diff --git a/src/main/providers/shell-launch/windows/powershell-executable.test.ts b/src/main/providers/shell-launch/windows/powershell-executable.test.ts deleted file mode 100644 index 9a514dcc5ff..00000000000 --- a/src/main/providers/shell-launch/windows/powershell-executable.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - resolveWindowsPowerShellExecutablePath, - resolveWindowsPowerShellSpawnChain -} from './powershell-executable' - -const WIN_ENV: NodeJS.ProcessEnv = { - ProgramW6432: 'C:\\Program Files', - 'ProgramFiles(x86)': 'C:\\Program Files (x86)', - LOCALAPPDATA: 'C:\\Users\\dev\\AppData\\Local', - SystemRoot: 'C:\\Windows', - ComSpec: 'C:\\Windows\\System32\\cmd.exe' -} - -const PATH_PWSH7 = 'D:\\Tools\\PowerShell\\7\\pwsh.exe' -const WINDOWS_POWERSHELL = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' -// Store alias reparse points fail ConPTY CreateProcessW with access-denied (5). -const PWSH_STORE_ALIAS = 'C:\\Users\\dev\\AppData\\Local\\Microsoft\\WindowsApps\\pwsh.exe' -const PWSH_STORE_EXE = - 'C:\\Program Files\\WindowsApps\\Microsoft.PowerShell_7.6.3.0_x64__8wekyb3d8bbwe\\pwsh.exe' - -describe('resolveWindowsPowerShellExecutablePath', () => { - it('repro: resolves pwsh.exe from a real PATH entry when standard roots miss', () => { - expect( - resolveWindowsPowerShellExecutablePath('pwsh.exe', { - platform: 'win32', - env: { - ...WIN_ENV, - Path: [ - 'relative-tools', - 'C:\\Users\\dev\\AppData\\Local\\Microsoft\\WindowsApps', - 'D:\\Tools\\PowerShell\\7' - ].join(';') - }, - isRealExecutable: (p) => p === PATH_PWSH7 - }) - ).toBe(PATH_PWSH7) - }) - - it('repro: never resolves pwsh.exe to the Store App Execution Alias stub', () => { - // Alias existence cannot prove a ConPTY-launchable executable. - const resolved = resolveWindowsPowerShellExecutablePath('pwsh.exe', { - platform: 'win32', - env: WIN_ENV, - isRealExecutable: (p) => p === PWSH_STORE_ALIAS - }) - expect(resolved).toBeNull() - expect(resolved).not.toBe(PWSH_STORE_ALIAS) - }) - - it('repro: resolves a Store App Execution Alias to its real package executable', () => { - const resolved = resolveWindowsPowerShellExecutablePath('pwsh.exe', { - platform: 'win32', - env: { - ...WIN_ENV, - Path: 'C:\\Users\\dev\\AppData\\Local\\Microsoft\\WindowsApps' - }, - isRealExecutable: (p) => p === PWSH_STORE_EXE, - resolveAppExecutionAlias: (p) => (p === PWSH_STORE_ALIAS ? PWSH_STORE_EXE : null) - }) - expect(resolved).toBe(PWSH_STORE_EXE) - }) - - it.each(['pwsh.exe', PWSH_STORE_ALIAS, 'C:\\Tools\\powershell.exe'])( - 'rejects unsafe Store alias target %s', - (target) => { - const resolved = resolveWindowsPowerShellExecutablePath('pwsh.exe', { - platform: 'win32', - env: { - ...WIN_ENV, - Path: 'C:\\Users\\dev\\AppData\\Local\\Microsoft\\WindowsApps' - }, - isRealExecutable: (p) => p === target, - resolveAppExecutionAlias: (p) => (p === PWSH_STORE_ALIAS ? target : null) - }) - expect(resolved).toBeNull() - } - ) -}) - -describe('resolveWindowsPowerShellSpawnChain', () => { - it('falls back to Windows PowerShell + cmd.exe when pwsh is only a Store alias', () => { - const chain = resolveWindowsPowerShellSpawnChain('pwsh.exe', { - platform: 'win32', - env: WIN_ENV, - isRealExecutable: (p) => p === PWSH_STORE_ALIAS || p === WINDOWS_POWERSHELL - }) - expect(chain).toEqual([WINDOWS_POWERSHELL, WIN_ENV.ComSpec]) - expect(chain).not.toContain(PWSH_STORE_ALIAS) - }) -}) diff --git a/src/main/providers/shell-launch/windows/shell-preflight-runtime.windows.test.ts b/src/main/providers/shell-launch/windows/shell-preflight-runtime.windows.test.ts index a67853d6f4d..e1b9da070a8 100644 --- a/src/main/providers/shell-launch/windows/shell-preflight-runtime.windows.test.ts +++ b/src/main/providers/shell-launch/windows/shell-preflight-runtime.windows.test.ts @@ -14,10 +14,7 @@ import * as pty from 'node-pty' import { afterEach, describe, expect, it } from 'vitest' import { CODEX_COMMAND_IDENTITY_ENV_KEYS } from '../../../../shared/codex-shell-function' import { resolveGitBashPath } from '../../../pty/shell-startup/git-bash' -import { - ORCA_CODEX_LAUNCH_PREFLIGHT_CMD_QUOTE_ENV, - resolveWindowsShellLaunchArgs -} from './shell-args' +import { resolveWindowsShellLaunchArgs } from './shell-args' const describeWindows = process.platform === 'win32' ? describe : describe.skip const tempDirs: string[] = [] @@ -124,36 +121,6 @@ afterEach(() => { }) describeWindows('Windows Codex shell preflight runtime', () => { - it('runs a spaced executable through cmd.exe and continues after failure', async () => { - const root = makeTempDir() - const preflight = writeFailingPreflight(root) - const preflightMarker = join(root, 'cmd-preflight-ran') - const startupMarker = join(root, 'cmd-started') - const resolved = resolveWindowsShellLaunchArgs( - 'cmd.exe', - root, - root, - undefined, - 'echo launched>cmd-started & exit /b 0', - preflight - ) - - await runPty({ - shellPath: 'cmd.exe', - shellArgs: resolved.shellArgs, - cwd: root, - env: { - ...process.env, - ORCA_CODEX_LAUNCH_PREFLIGHT: preflight, - [ORCA_CODEX_LAUNCH_PREFLIGHT_CMD_QUOTE_ENV]: '"', - ORCA_PREFLIGHT_MARKER: preflightMarker - } - }) - - expect(readFileSync(preflightMarker, 'utf8')).toBe('ran') - expect(readFileSync(startupMarker, 'utf8').trim()).toBe('launched') - }) - it('runs the typed-Codex wrapper through Git Bash without MSYS switch rewriting', async () => { const gitBash = resolveGitBashPath() expect(gitBash).not.toBeNull() diff --git a/src/main/providers/working-directory/validation-retention.test.ts b/src/main/providers/working-directory/validation-retention.test.ts deleted file mode 100644 index 42163d2872a..00000000000 --- a/src/main/providers/working-directory/validation-retention.test.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { getEventListeners } from 'node:events' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' - -const { stat } = vi.hoisted(() => ({ - stat: vi.fn<() => Promise<{ isDirectory: () => boolean }>>() -})) -vi.mock('node:fs/promises', () => ({ stat })) -vi.mock('../../wsl/filesystem/unc-path-access', () => ({ - wslUncDirectoryExistsAsync: () => { - throw new Error('Unexpected WSL probe') - } -})) - -import { - _resetWorkingDirectoryValidationStateForTest, - validateWorkingDirectoryAsync as validate, - WorkingDirectoryValidationAbortedError -} from './validation' - -const directory = { isDirectory: () => true } -const cwd = 'synthetic-cwd-wait-retention' - -function pendingStat() { - const gate = Promise.withResolvers() - stat.mockReturnValue(gate.promise) - return gate -} - -async function canceledWait(path = cwd) { - const controller = new AbortController() - const signal = new WeakRef(controller.signal) - const waiting = validate(path, { signal: controller.signal }) - controller.abort() - try { - await waiting - throw new Error('Expected cancellation') - } catch (error) { - if (!(error instanceof WorkingDirectoryValidationAbortedError)) { - throw error - } - expect(getEventListeners(controller.signal, 'abort')).toHaveLength(0) - return { signal, error: new WeakRef(error) } - } -} - -async function collect(): Promise { - if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { - throw new Error('The test runner must enable --expose-gc') - } - for (let round = 0; round < 6; round += 1) { - await new Promise((resolve) => setImmediate(resolve)) - globalThis.gc() - } -} - -beforeEach(() => { - stat.mockReset() - _resetWorkingDirectoryValidationStateForTest() -}) -afterEach(() => vi.restoreAllMocks()) - -describe('working directory validation waiter lifetime', () => { - it('releases the first caller and subsequent canceled callers while their native stat stays owned', async () => { - const gate = pendingStat() - try { - const first = await canceledWait() - const later: Awaited>[] = [] - for (let index = 0; index < 31; index += 1) { - later.push(await canceledWait()) - } - await collect() - expect(first.signal.deref()).toBeUndefined() - expect(first.error.deref()).toBeUndefined() - expect(later.filter((ref) => ref.signal.deref() || ref.error.deref())).toHaveLength(0) - expect(stat).toHaveBeenCalledOnce() - - const staying = validate(cwd) - expect(stat).toHaveBeenCalledOnce() - gate.resolve(directory) - await staying - } finally { - gate.resolve(directory) - } - }) - - it('preserves the raw operation when the first caller is already aborted', async () => { - const gate = pendingStat() - try { - const signal = AbortSignal.abort() - await expect(validate(cwd, { signal })).rejects.toBeInstanceOf( - WorkingDirectoryValidationAbortedError - ) - const first = validate(cwd) - expect(validate(cwd)).toBe(first) - expect(stat).toHaveBeenCalledOnce() - expect(getEventListeners(signal, 'abort')).toHaveLength(0) - gate.resolve(directory) - await first - } finally { - gate.resolve(directory) - } - }) - - it('handles native rejection after every caller has already canceled', async () => { - const unhandled: unknown[] = [] - const recordUnhandled = (error: unknown): void => { - unhandled.push(error) - } - process.on('unhandledRejection', recordUnhandled) - const gate = pendingStat() - try { - await canceledWait() - gate.reject(new Error('Late native failure')) - await new Promise((resolve) => setImmediate(resolve)) - expect(unhandled).toEqual([]) - stat.mockResolvedValue(directory) - await validate(cwd) - expect(stat).toHaveBeenCalledTimes(2) - } finally { - gate.resolve(directory) - process.off('unhandledRejection', recordUnhandled) - } - }) - - it.each( - (['before', 'between', 'after'] as const).flatMap((position) => - [false, true].map((reject) => ({ position, reject })) - ) - )( - 'preserves an external raw observer at $position with reject=$reject', - async ({ position, reject }) => { - const gate = pendingStat() - const raw = validate(cwd) - const controller = new AbortController() - const start = () => - validate(cwd, { signal: controller.signal }).then( - () => 'fulfilled', - (error: unknown) => (error instanceof Error ? error.name : 'unknown') - ) - const waiters: Promise[] = [] - if (position !== 'before') { - waiters.push(start()) - } - const abortObserver = raw.then( - () => controller.abort(), - () => controller.abort() - ) - if (position !== 'after') { - waiters.push(start()) - } - if (reject) { - gate.reject(new Error('Native stat failed')) - } else { - gate.resolve(directory) - } - const rawOutcome = reject ? 'Error' : 'fulfilled' - expect(await Promise.all(waiters)).toEqual( - position === 'before' - ? ['WorkingDirectoryValidationAbortedError'] - : position === 'between' - ? [rawOutcome, 'WorkingDirectoryValidationAbortedError'] - : [rawOutcome] - ) - await abortObserver - } - ) -}) diff --git a/src/main/providers/working-directory/validation.test.ts b/src/main/providers/working-directory/validation.test.ts index 91c12495994..898c4f067fe 100644 --- a/src/main/providers/working-directory/validation.test.ts +++ b/src/main/providers/working-directory/validation.test.ts @@ -1,6 +1,3 @@ -import { mkdtemp, rm } from 'node:fs/promises' -import os from 'node:os' -import path from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' const wslUncDirectoryExistsAsyncMock = vi.hoisted(() => vi.fn()) @@ -15,17 +12,10 @@ import { _resetWorkingDirectoryValidationStateForTest } from './validation' -let tempDir: string - -beforeEach(async () => { +beforeEach(() => { wslUncDirectoryExistsAsyncMock.mockReset() wslUncDirectoryExistsAsyncMock.mockResolvedValue(null) _resetWorkingDirectoryValidationStateForTest() - tempDir = await mkdtemp(path.join(os.tmpdir(), 'orca-cwd-validate-')) -}) - -afterEach(async () => { - await rm(tempDir, { recursive: true, force: true }) }) describe('validateWorkingDirectoryAsync', () => { @@ -61,25 +51,4 @@ describe('validateWorkingDirectoryAsync', () => { expect(wslUncDirectoryExistsAsyncMock).toHaveBeenCalledOnce() }) }) - - describe('UNC route concurrency', () => { - beforeEach(() => { - vi.spyOn(process, 'platform', 'get').mockReturnValue('win32') - }) - - afterEach(() => { - vi.restoreAllMocks() - }) - - it('keeps a healthy local path out of a stalled share queue', async () => { - wslUncDirectoryExistsAsyncMock.mockReturnValue(new Promise(() => {})) - for (let index = 0; index < 4; index += 1) { - void validateWorkingDirectoryAsync(`\\\\wsl.localhost\\Ubuntu\\blocking-${index}`).catch( - () => {} - ) - } - - await expect(validateWorkingDirectoryAsync(tempDir)).resolves.toBeUndefined() - }) - }) }) diff --git a/src/shared/git/branches/git-branch-cleanup-real-git.test.ts b/src/shared/git/branches/git-branch-cleanup-real-git.test.ts index ebf033dd59b..c4560304c4f 100644 --- a/src/shared/git/branches/git-branch-cleanup-real-git.test.ts +++ b/src/shared/git/branches/git-branch-cleanup-real-git.test.ts @@ -2,14 +2,12 @@ import { execFile } from 'node:child_process' import { mkdir, mkdtemp, realpath, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { pathToFileURL } from 'node:url' import { promisify } from 'node:util' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { branchHasNoUnmergedChangesOnAnyTarget, type GitBranchCleanupExec } from './git-branch-cleanup' -import { refreshBranchCleanupTargetRefs } from './git-branch-cleanup-fetch' import { GitCapabilityCache } from '../commands/git-capability-cache' import { removeTree } from '../../windows-transient-lock-removal' @@ -18,7 +16,6 @@ const execFileAsync = promisify(execFile) describe('branch cleanup with real Git', () => { let root = '' let remote = '' - let client = '' const git = (cwd: string, args: string[]) => execFileAsync('git', ['-c', 'commit.gpgSign=false', ...args], { cwd }) @@ -31,7 +28,6 @@ describe('branch cleanup with real Git', () => { beforeEach(async () => { root = await realpath(await mkdtemp(join(tmpdir(), 'orca-branch-cleanup-'))) remote = join(root, 'remote') - client = join(root, 'client') await mkdir(remote) await git(remote, ['init', '-q']) await git(remote, ['config', 'user.name', 'Orca Test']) @@ -39,137 +35,12 @@ describe('branch cleanup with real Git', () => { await git(remote, ['config', 'gc.auto', '0']) await commit(remote, 'base') await git(remote, ['branch', '-M', 'main']) - await git(remote, ['checkout', '-qb', 'unrelated-feature']) - await commit(remote, 'feature') - await git(remote, ['checkout', '-q', 'main']) - await git(root, ['clone', '-q', remote, client]) }) afterEach(async () => { await removeTree(root) }) - it('refreshes the requested base without fetching other branches or new tags', async () => { - const previousFeature = (await git(client, ['rev-parse', 'origin/unrelated-feature'])).stdout - await git(remote, ['checkout', '-q', 'unrelated-feature']) - await commit(remote, 'new-feature') - await git(remote, ['tag', 'new-feature-tag']) - await git(remote, ['checkout', '-q', 'main']) - await commit(remote, 'new-base') - - const refreshed = await refreshBranchCleanupTargetRefs( - (args) => git(client, args), - ['refs/remotes/origin/main'] - ) - - expect(refreshed).toEqual(['refs/remotes/origin/main']) - expect((await git(client, ['rev-parse', 'origin/main'])).stdout).toBe( - (await git(remote, ['rev-parse', 'main'])).stdout - ) - expect((await git(client, ['rev-parse', 'origin/unrelated-feature'])).stdout).toBe( - previousFeature - ) - expect((await git(client, ['tag', '--list'])).stdout).toBe('') - const unrelatedCommit = (await git(remote, ['rev-parse', 'unrelated-feature'])).stdout.trim() - await expect(git(client, ['cat-file', '-e', unrelatedCommit])).rejects.toThrow() - }) - - it('preserves shallow branches whose common ancestor is outside the clone', async () => { - await commit(remote, 'new-main') - const shallow = join(root, 'shallow') - await git(root, ['clone', '-q', '--depth=1', pathToFileURL(remote).href, shallow]) - await git(shallow, [ - 'fetch', - '-q', - '--depth=1', - 'origin', - 'refs/heads/unrelated-feature:refs/heads/feature' - ]) - const commands: string[][] = [] - const runGit: GitBranchCleanupExec = (args) => { - commands.push(args) - return git(shallow, args) - } - - expect((await git(shallow, ['rev-parse', '--is-shallow-repository'])).stdout.trim()).toBe( - 'true' - ) - await expect( - branchHasNoUnmergedChangesOnAnyTarget(runGit, 'feature', ['HEAD'], new GitCapabilityCache()) - ).resolves.toBe(false) - expect(commands.some((args) => args[0] === 'merge-tree')).toBe(false) - expect((await git(shallow, ['show', 'feature:feature'])).stdout).toBe('feature') - }) - - it.each([ - ['exact', '+refs/heads/unrelated-feature:refs/remotes/origin/main', 'refs/remotes/origin/main'], - [ - 'wildcard', - '+refs/heads/unrelated-*:refs/remotes/origin/base-*', - 'refs/remotes/origin/base-feature' - ] - ])('refreshes renamed tracking refs with an %s mapping', async (_label, refspec, target) => { - await git(client, ['config', 'remote.origin.fetch', refspec]) - await git(remote, ['checkout', '-q', 'unrelated-feature']) - await commit(remote, 'new-feature') - - const refreshed = await refreshBranchCleanupTargetRefs((args) => git(client, args), [target]) - - expect(refreshed).toEqual([target]) - expect((await git(client, ['rev-parse', target])).stdout).toBe( - (await git(remote, ['rev-parse', 'unrelated-feature'])).stdout - ) - }) - - it('preserves a tracking ref when its configured source is ambiguous', async () => { - const previousMain = (await git(client, ['rev-parse', 'origin/main'])).stdout - await git(client, [ - 'config', - '--add', - 'remote.origin.fetch', - '+refs/heads/unrelated-feature:refs/remotes/origin/main' - ]) - await commit(remote, 'new-main') - - await expect( - refreshBranchCleanupTargetRefs((args) => git(client, args), ['refs/remotes/origin/main']) - ).resolves.toEqual([]) - expect((await git(client, ['rev-parse', 'origin/main'])).stdout).toBe(previousMain) - }) - - it('does not bypass negative fetch mappings', async () => { - const previousFeature = (await git(client, ['rev-parse', 'origin/unrelated-feature'])).stdout - await git(client, ['config', '--add', 'remote.origin.fetch', '^refs/heads/unrelated-*']) - await git(remote, ['checkout', '-q', 'unrelated-feature']) - await commit(remote, 'excluded-feature') - - await expect( - refreshBranchCleanupTargetRefs( - (args) => git(client, args), - ['refs/remotes/origin/unrelated-feature'] - ) - ).resolves.toEqual([]) - expect((await git(client, ['rev-parse', 'origin/unrelated-feature'])).stdout).toBe( - previousFeature - ) - }) - - it('refreshes a live fallback base when the saved base was deleted upstream', async () => { - const previousMain = (await git(client, ['rev-parse', 'origin/main'])).stdout.trim() - await git(client, ['update-ref', 'refs/remotes/origin/deleted-base', previousMain]) - await commit(remote, 'new-main') - - await expect( - refreshBranchCleanupTargetRefs( - (args) => git(client, args), - ['refs/remotes/origin/deleted-base', 'refs/remotes/origin/main'] - ) - ).resolves.toEqual(['refs/remotes/origin/main']) - expect((await git(client, ['rev-parse', 'origin/main'])).stdout).toBe( - (await git(remote, ['rev-parse', 'main'])).stdout - ) - }) - it.each([ ['proves a squash-merged branch that merged main and whose lines main rewrote', true], ['preserves the same branch when main never squashed it', false] diff --git a/src/shared/git/branches/git-branch-cleanup.test.ts b/src/shared/git/branches/git-branch-cleanup.test.ts deleted file mode 100644 index 96c446d69e2..00000000000 --- a/src/shared/git/branches/git-branch-cleanup.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { - branchHasNoUnmergedChangesWithLazyTargetRefresh, - type GitBranchCleanupExec -} from './git-branch-cleanup' -import { GitCapabilityCache } from '../commands/git-capability-cache' -import { GIT_FETCH_SKIP_AUTO_MAINTENANCE_CONFIG_ARGS } from '../fetch/git-fetch-auto-maintenance' - -/** Answers the stale remote ref the way a merged branch would: merging it leaves the target tree unchanged. */ -function mergedProofResponses(): GitBranchCleanupExec { - const responses: Record = { - 'rev-parse --verify --quiet refs/remotes/origin/main^{commit}': 'target\n', - 'merge-base target refs/heads/feature/test': 'base\n', - 'diff --name-only --no-renames base refs/heads/feature/test': 'feature.txt\n', - 'merge-tree --write-tree target refs/heads/feature/test': 'merged-tree\n', - 'rev-parse --verify --quiet target^{tree}': 'merged-tree\n' - } - return vi.fn(async (args) => ({ stdout: responses[args.join(' ')] ?? '' })) -} - -describe('branchHasNoUnmergedChangesWithLazyTargetRefresh', () => { - it('preserves a branch when refresh fails even if the stale remote ref would prove it merged', async () => { - const proof = mergedProofResponses() - const runGit = vi.fn(async (args, options) => { - if (args[0] === 'remote') { - return { stdout: 'origin\n' } - } - if (args[0] === 'config') { - return { stdout: '+refs/heads/*:refs/remotes/origin/*\n' } - } - if (args.includes('fetch')) { - throw new Error('offline') - } - return proof(args, options) - }) - - await expect( - branchHasNoUnmergedChangesWithLazyTargetRefresh( - runGit, - 'feature/test', - ['refs/remotes/origin/main'], - new GitCapabilityCache() - ) - ).resolves.toBe(false) - expect(proof).not.toHaveBeenCalled() - }) - - it('refreshes before trusting a stale remote-tracking proof', async () => { - let refreshed = false - const runGit = vi.fn(async (args) => { - const command = args.join(' ') - if (command === 'remote') { - return { stdout: 'origin\n' } - } - if (args[0] === 'config') { - return { stdout: '+refs/heads/*:refs/remotes/origin/*\n' } - } - if (args.includes('fetch')) { - refreshed = true - return { stdout: '' } - } - if (command === 'rev-parse --verify --quiet refs/remotes/origin/main^{commit}') { - return { stdout: 'remote-target\n' } - } - if (command === 'rev-parse --verify --quiet origin/main^{commit}') { - return { stdout: 'short-remote-target\n' } - } - if (command === 'rev-parse --verify --quiet HEAD^{commit}') { - return { stdout: 'local-target\n' } - } - if (args[0] === 'merge-base') { - return { stdout: 'base\n' } - } - if (command === 'merge-tree --write-tree remote-target refs/heads/feature/test') { - return { stdout: refreshed ? 'changed-tree\n' : 'remote-tree\n' } - } - if (command === 'merge-tree --write-tree short-remote-target refs/heads/feature/test') { - return { stdout: refreshed ? 'changed-tree\n' : 'short-remote-tree\n' } - } - if (command === 'rev-parse --verify --quiet remote-target^{tree}') { - return { stdout: 'remote-tree\n' } - } - if (command === 'rev-parse --verify --quiet short-remote-target^{tree}') { - return { stdout: 'short-remote-tree\n' } - } - return { stdout: '' } - }) - - await expect( - branchHasNoUnmergedChangesWithLazyTargetRefresh( - runGit, - 'feature/test', - ['refs/remotes/origin/main', 'origin/main', 'HEAD'], - new GitCapabilityCache() - ) - ).resolves.toBe(false) - - expect(runGit.mock.calls.map(([args]) => args)).toContainEqual([ - ...GIT_FETCH_SKIP_AUTO_MAINTENANCE_CONFIG_ARGS, - 'fetch', - '--no-tags', - 'origin', - '+refs/heads/main:refs/remotes/origin/main' - ]) - }) -}) From fcb620d21964567bcb0df5eef531183667a1abd3 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 21:03:24 -0800 Subject: [PATCH 014/178] test: remove mock-native mobile queue and authority suites (#41826) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../MobileNativeChatQueuedMessages.test.tsx | 719 ------------------ .../connection/client-authority-epoch.test.ts | 20 - 2 files changed, 739 deletions(-) delete mode 100644 mobile/src/session/native-chat/input/MobileNativeChatQueuedMessages.test.tsx delete mode 100644 mobile/src/transport/connection/client-authority-epoch.test.ts diff --git a/mobile/src/session/native-chat/input/MobileNativeChatQueuedMessages.test.tsx b/mobile/src/session/native-chat/input/MobileNativeChatQueuedMessages.test.tsx deleted file mode 100644 index 095cb498a17..00000000000 --- a/mobile/src/session/native-chat/input/MobileNativeChatQueuedMessages.test.tsx +++ /dev/null @@ -1,719 +0,0 @@ -// @vitest-environment happy-dom -import { activateMobileUiLocale } from '../../../i18n/mobile-i18n' -import type { MobileQueuedMessageCard } from './mobile-structured-queued-message-cards' - -import { createElement, type ReactElement } from 'react' - -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { afterEach, describe, expect, it, vi } from 'vitest' - -import { MobileNativeChatQueuedMessages } from './MobileNativeChatQueuedMessages' -import type { ActionSheetAction } from '../../../components/ActionSheetModal' -import { MobileNativeChatQueueAbovePromptContext } from '../mobile-native-chat-lower-region-context' -import { createMobileQueuedHeadLight } from './mobile-queued-head-light' -import { QUEUED_ROW_SHOW_SETTLE_MS } from './mobile-queued-row-frame' -import { queuedMessageStyles as styles } from './mobile-native-chat-queued-message-styles' -import { pressedStyle } from '../../../theme/mobile-theme' - -let fontScale = 1 -let windowWidth = 390 -vi.mock('../../../components/DragReorderList', () => ({ DragReorderList: 'DragReorderList' })) -vi.mock('react-native-reanimated', async () => { - const { useRef } = await import('react') - return { - default: { ScrollView: 'ScrollView' }, - useAnimatedRef: () => useRef(null), - useSharedValue: (value: unknown) => useRef({ value }).current - } -}) - -vi.mock('react-native', () => ({ - useWindowDimensions: () => ({ fontScale, width: windowWidth }), - ActivityIndicator: 'ActivityIndicator', - Platform: { OS: 'ios' }, - Pressable: 'Pressable', - ScrollView: 'ScrollView', - StyleSheet: { create: (styles: unknown) => styles, hairlineWidth: 1 }, - Text: 'Text', - TextInput: 'TextInput', - View: 'View' -})) - -vi.mock('lucide-react-native', () => ({ - Image: 'Image', - AlertCircle: 'AlertCircle', - ArrowUpRight: 'ArrowUpRight', - Check: 'Check', - ChevronDown: 'ChevronDown', - ChevronUp: 'ChevronUp', - CornerDownRight: 'CornerDownRight', - ListEnd: 'ListEnd', - MoreHorizontal: 'MoreHorizontal', - Pencil: 'Pencil', - Play: 'Play', - Send: 'Send', - Trash2: 'Trash2', - X: 'X' -})) - -vi.mock('../../../components/ActionSheetModal', () => ({ ActionSheetModal: 'ActionSheetModal' })) -vi.mock('./MobileNativeChatBackgroundTasks', () => ({ - MobileNativeChatBackgroundTasks: 'BackgroundTasks' -})) -vi.mock('./MobileNativeChatDockedTaskList', () => ({ - MobileNativeChatDockedTaskList: 'DockedTaskList' -})) - -vi.mock('./MobileQueuedMessageComposer', () => ({ - MobileQueuedMessageComposer: 'QueuedMessageComposer' -})) - -function card( - overrides: Partial & { messageId: string } -): MobileQueuedMessageCard { - const text = overrides.text ?? `text of ${overrides.messageId}` - return { - position: 1, - text, - line: text, - state: 'waiting', - hold: overrides.state === 'returned' ? 'returned' : 'turn', - needsAttention: false, - caption: null, - ...overrides - } -} - -describe('MobileNativeChatQueuedMessages', () => { - let renderer: ReactTestRenderer | null = null - - afterEach(async () => { - vi.restoreAllMocks() - windowWidth = 390 - act(() => renderer?.unmount()) - renderer = null - await activateMobileUiLocale('en') - }) - - type Props = Parameters[0] - - function element(props: Props, abovePrompt = false): ReactElement { - return createElement( - MobileNativeChatQueueAbovePromptContext, - { value: abovePrompt }, - createElement(MobileNativeChatQueuedMessages, props) - ) - } - - async function mount(props: Props, abovePrompt = false): Promise { - const mounted = create(createElement('View')) - renderer = mounted - await act(async () => { - mounted.update(element(props, abovePrompt)) - }) - return mounted - } - - function rows(mounted: ReactTestRenderer) { - return mounted.root.findAll( - (node) => typeof node.type === 'string' && node.props.testID === 'queued-card-row' - ) - } - - function foldToggle(mounted: ReactTestRenderer) { - return mounted.root.find( - (node) => typeof node.type === 'string' && node.props.testID === 'queued-fold-toggle' - ) - } - - function menuAction(mounted: ReactTestRenderer, id: string) { - const actions: ActionSheetAction[] = sheet(mounted).props.actions - return actions.find((action) => action.id === id) - } - - function dragList(mounted: ReactTestRenderer) { - return mounted.root.findAll((node) => String(node.type) === 'DragReorderList') - } - - const three = () => [card({ messageId: 'a' }), card({ messageId: 'b' }), card({ messageId: 'c' })] - - function control(mounted: ReactTestRenderer, label: string, index = 0) { - return mounted.root.findAll( - (node) => typeof node.type === 'string' && node.props.accessibilityLabel === label - )[index]! - } - - function sheet(mounted: ReactTestRenderer) { - return mounted.root.find((node) => String(node.type) === 'ActionSheetModal') - } - - it("closes a card's menu for good once the card leaves, even if it comes back", async () => { - const mounted = await mount({ cards: [card({ messageId: 'a' }), card({ messageId: 'b' })] }) - await act(async () => - mounted.root.findAllByProps({ accessibilityLabel: 'More actions' })[0]!.props.onPress() - ) - expect(sheet(mounted).props.visible).toBe(true) - await act(async () => { - mounted.update(element({ cards: [card({ messageId: 'b' })] })) - }) - expect(sheet(mounted).props.visible).toBe(false) - await act(async () => { - mounted.update(element({ cards: [card({ messageId: 'a' }), card({ messageId: 'b' })] })) - }) - expect(sheet(mounted).props.visible).toBe(false) - }) - - it('runs one action per card at a time, however fast its controls are tapped', async () => { - let answer: (done: boolean) => void = () => undefined - const onSend = vi.fn(() => new Promise((resolve) => (answer = resolve))) - const onDelete = vi.fn(async () => true) - const mounted = await mount({ cards: [card({ messageId: 'w' })], onSend, onDelete }) - await act(async () => control(mounted, 'More actions').props.onPress()) - // Tap all actions before disabled state renders to expose same-frame duplication. - await act(async () => { - control(mounted, 'Send this message').props.onPress() - control(mounted, 'Send this message').props.onPress() - menuAction(mounted, 'deleteMessage')!.onPress() - }) - expect(onSend).toHaveBeenCalledTimes(1) - expect(onDelete).not.toHaveBeenCalled() - expect(control(mounted, 'More actions').props.disabled).toBe(true) - expect(menuAction(mounted, 'deleteMessage')!.disabled).toBe(true) - await act(async () => answer(true)) - expect(control(mounted, 'Send this message').props.disabled).toBe(false) - }) - - it('folds to the first message above a prompt card and opens from the more row', async () => { - const mounted = await mount({ cards: three() }, true) - expect(rows(mounted)).toHaveLength(1) - expect(foldToggle(mounted).props.accessibilityState).toEqual({ expanded: false }) - await act(async () => foldToggle(mounted).props.onPress()) - expect(rows(mounted)).toHaveLength(3) - expect(foldToggle(mounted).props.accessibilityState).toEqual({ expanded: true }) - await act(async () => foldToggle(mounted).props.onPress()) - expect(rows(mounted)).toHaveLength(1) - }) - - it.each([390, 320])( - 'lets the open list, not the fold row, give up height on a short %ipt screen', - async (width) => { - windowWidth = width - const mounted = await mount({ cards: three() }, true) - await act(async () => foldToggle(mounted).props.onPress()) - const flat = (style: unknown): Record => - Object.assign({}, ...[style].flat(Infinity).filter(Boolean)) - const box = mounted.root.find( - (node) => typeof node.type === 'string' && node.props.testID === 'queued-box' - ) - const scroll = mounted.root.find((node) => String(node.type) === 'ScrollView') - // The box shrinks under a height limit; the list scrolls so Show less below it stays whole. - expect(flat(box.props.style)).toMatchObject({ flexShrink: 1, minHeight: 0 }) - expect(flat(scroll.parent!.props.style)).toMatchObject({ flexShrink: 1, minHeight: 0 }) - expect(flat(scroll.props.style)).toMatchObject({ - maxHeight: width < 340 ? 264 : 212, - flexShrink: 1, - minHeight: 0 - }) - expect(flat(foldToggle(mounted).props.style({ pressed: false })).flexShrink).toBeUndefined() - } - ) - - it('opens by default above the composer, and folds again when a prompt card arrives', async () => { - const mounted = await mount({ cards: three() }) - expect(rows(mounted)).toHaveLength(3) - await act(async () => mounted.update(element({ cards: three() }, true))) - expect(rows(mounted)).toHaveLength(1) - }) - - it('folds again for a later prompt after the person opened the list above an earlier one', async () => { - const mounted = await mount({ cards: three() }, true) - await act(async () => foldToggle(mounted).props.onPress()) - expect(rows(mounted)).toHaveLength(3) - await act(async () => mounted.update(element({ cards: three() }))) - await act(async () => mounted.update(element({ cards: three() }, true))) - expect(rows(mounted)).toHaveLength(1) - }) - - it('opens for a message queued in the same render a prompt arrives', async () => { - const mounted = await mount({ cards: three() }) - // Folded by hand above the composer, then a prompt and a new own message arrive together. - await act(async () => foldToggle(mounted).props.onPress()) - expect(rows(mounted)).toHaveLength(1) - await act(async () => - mounted.update(element({ cards: [...three(), card({ messageId: 'd' })] }, true)) - ) - expect(rows(mounted)).toHaveLength(4) - }) - - it('scrolls a message the person queues into a folded list into view', async () => { - const scrollTo = vi.fn() - const mounted = create(createElement('View'), { - createNodeMock: (node) => - node.type === 'ScrollView' - ? { scrollTo, scrollToEnd: vi.fn(), flashScrollIndicators: vi.fn() } - : null - }) - renderer = mounted - await act(async () => mounted.update(element({ cards: three() }, true))) - await act(async () => - mounted.update(element({ cards: [...three(), card({ messageId: 'd' })] }, true)) - ) - expect(rows(mounted)).toHaveLength(4) - const scroll = mounted.root.find((node) => String(node.type) === 'ScrollView') - await act(async () => { - rows(mounted).forEach((row) => - row.props.onLayout({ nativeEvent: { layout: { height: 60 } } }) - ) - scroll.props.onLayout({ nativeEvent: { layout: { height: 100 } } }) - scroll.props.onContentSizeChange(0, 240) - }) - // The new last row spans 180–240 of a 100pt list. - expect(scrollTo).toHaveBeenLastCalledWith({ y: 140, animated: false }) - }) - - it("opens a folded list for a message the person queues, not for an agent's or a delete", async () => { - const mounted = await mount({ cards: three() }, true) - await act(async () => - mounted.update( - element( - { - cards: [ - ...three(), - card({ messageId: 'x', from: { kind: 'agent', senders: [], orchestration: null } }) - ] - }, - true - ) - ) - ) - expect(rows(mounted)).toHaveLength(1) - await act(async () => mounted.update(element({ cards: three().slice(0, 2) }, true))) - expect(rows(mounted)).toHaveLength(1) - await act(async () => - mounted.update(element({ cards: [...three().slice(0, 2), card({ messageId: 'd' })] }, true)) - ) - expect(rows(mounted)).toHaveLength(3) - }) - - it('opens a folded list for a failed message behind the fold, with its Send in reach', async () => { - const foldText = (mounted: ReactTestRenderer) => - foldToggle(mounted) - .findAll((node) => String(node.type) === 'Text') - .flatMap((node) => node.props.children) - .join('') - const failed = (messageId: string) => - card({ messageId, needsAttention: true, caption: "Couldn't send" }) - const mounted = await mount( - { - cards: [card({ messageId: 'a' }), failed('b'), card({ messageId: 'c' })], - agentWorking: true - }, - true - ) - expect(rows(mounted)).toHaveLength(3) - // Mid-turn the others steer, but the failed card offers its retry, Send. - const pills = mounted.root.findAll( - (node) => - typeof node.type === 'string' && - typeof node.props.accessibilityLabel === 'string' && - node.props.hitSlop !== undefined - ) - expect(pills.map((node) => node.props.accessibilityLabel)).toEqual([ - 'Submit without interrupting the model', - 'Send this message', - 'Submit without interrupting the model' - ]) - expect(pills[1]!.props.disabled).toBe(false) - expect(foldText(mounted)).not.toContain("Couldn't send") - // Folding is still the person's call, and the fold row only counts. - await act(async () => foldToggle(mounted).props.onPress()) - expect(rows(mounted)).toHaveLength(1) - expect(foldText(mounted)).toBe('2 more') - expect( - foldToggle(mounted).findAll((node) => String(node.type) === 'Text')[0]!.props - .accessibilityLabel - ).toBe('2 more, a message needs attention') - // An agent's new message does not reopen it for a failure the person already folded away. - await act(async () => - mounted.update( - element( - { - cards: [ - card({ messageId: 'a' }), - failed('b'), - card({ messageId: 'c' }), - card({ messageId: 'z', from: { kind: 'agent', senders: [], orchestration: null } }) - ] - }, - true - ) - ) - ) - expect(rows(mounted)).toHaveLength(1) - // A second card failing later opens it again. - await act(async () => - mounted.update(element({ cards: [card({ messageId: 'a' }), failed('b'), failed('c')] }, true)) - ) - expect(rows(mounted)).toHaveLength(3) - }) - - it('scrolls a failure that opens a short list into view', async () => { - const scrollTo = vi.fn() - const mounted = create(createElement('View'), { - createNodeMock: (node) => - node.type === 'ScrollView' - ? { scrollTo, scrollToEnd: vi.fn(), flashScrollIndicators: vi.fn() } - : null - }) - renderer = mounted - await act(async () => mounted.update(element({ cards: three() }, true))) - const failedThird = [ - card({ messageId: 'a' }), - card({ messageId: 'b' }), - card({ messageId: 'c', needsAttention: true, caption: "Couldn't send" }) - ] - await act(async () => mounted.update(element({ cards: failedThird }, true))) - expect(rows(mounted)).toHaveLength(3) - // Rows report before the list knows its own size, as when it has just opened. - await act(async () => { - rows(mounted).forEach((row, index) => - row.props.onLayout({ nativeEvent: { layout: { height: index === 2 ? 80 : 60 } } }) - ) - }) - expect(scrollTo).not.toHaveBeenCalled() - const scroll = mounted.root.find((node) => String(node.type) === 'ScrollView') - await act(async () => { - scroll.props.onLayout({ nativeEvent: { layout: { height: 100 } } }) - scroll.props.onContentSizeChange(0, 200) - }) - // The failed row spans 120–200 of a 100pt list, so the list scrolls to its end. - expect(scrollTo).toHaveBeenCalledWith({ y: 100, animated: false }) - }) - - it('places a failed row by the current order after a reorder that lays nothing out', async () => { - vi.useFakeTimers() - try { - const scrollTo = vi.fn() - const mounted = create(createElement('View'), { - createNodeMock: (node) => - node.type === 'ScrollView' - ? { scrollTo, scrollToEnd: vi.fn(), flashScrollIndicators: vi.fn() } - : null - }) - renderer = mounted - const ids = (order: string) => [...order].map((messageId) => card({ messageId })) - await act(async () => mounted.update(element({ cards: ids('abcd') }))) - const scroll = mounted.root.find((node) => String(node.type) === 'ScrollView') - await act(async () => { - scroll.props.onLayout({ nativeEvent: { layout: { height: 100 } } }) - scroll.props.onContentSizeChange(0, 240) - rows(mounted).forEach((row) => - row.props.onLayout({ nativeEvent: { layout: { height: 60 } } }) - ) - }) - // Equal-height rows reordered by transform: no row lays out again. - await act(async () => mounted.update(element({ cards: ids('bcda') }))) - scrollTo.mockClear() - await act(async () => - mounted.update( - element({ - cards: [...ids('bcd'), card({ messageId: 'a', needsAttention: true })] - }) - ) - ) - await act(async () => { - vi.advanceTimersByTime(QUEUED_ROW_SHOW_SETTLE_MS) - }) - // a is now last, spanning 180–240 of a 100pt list, not first. - expect(scrollTo).toHaveBeenCalledWith({ y: 140, animated: false }) - } finally { - vi.useRealTimers() - } - }) - - it('places a failed row by the height a returning message lays out at, not the one it left with', async () => { - const scrollTo = vi.fn() - const mounted = create(createElement('View'), { - createNodeMock: (node) => - node.type === 'ScrollView' - ? { scrollTo, scrollToEnd: vi.fn(), flashScrollIndicators: vi.fn() } - : null - }) - renderer = mounted - // Rows render in queue order. - const layRow = async (index: number, height: number) => { - const row = rows(mounted)[index]! - await act(async () => row.props.onLayout({ nativeEvent: { layout: { height } } })) - } - await act(async () => - mounted.update( - element({ - cards: [card({ messageId: 'a' }), card({ messageId: 'b' }), card({ messageId: 'c' })] - }) - ) - ) - const scroll = mounted.root.find((node) => String(node.type) === 'ScrollView') - await act(async () => { - scroll.props.onLayout({ nativeEvent: { layout: { height: 100 } } }) - scroll.props.onContentSizeChange(0, 300) - }) - // b leaves tall (an open editor or expanded text), then comes back as a plain 60pt row. - await layRow(0, 60) - await layRow(1, 180) - await layRow(2, 60) - await act(async () => - mounted.update(element({ cards: [card({ messageId: 'a' }), card({ messageId: 'c' })] })) - ) - scrollTo.mockClear() - await act(async () => - mounted.update( - element({ - cards: [ - card({ messageId: 'a' }), - card({ messageId: 'b' }), - card({ messageId: 'c', needsAttention: true, caption: "Couldn't send" }) - ] - }) - ) - ) - // The failed row reports before b does; b's old height must not place it. - await layRow(2, 60) - expect(scrollTo).not.toHaveBeenCalled() - await layRow(1, 60) - // c spans 120–180 of a 100pt list: the nearest scroll that shows it, clear of the edge fade. - expect(scrollTo).toHaveBeenCalledTimes(1) - expect(scrollTo).toHaveBeenCalledWith({ y: 96, animated: false }) - }) - - it('scrolls to a failure the list mounts open for, and settles when no row lays out again', async () => { - vi.useFakeTimers() - try { - const scrollTo = vi.fn() - const mounted = create(createElement('View'), { - createNodeMock: (node) => - node.type === 'ScrollView' - ? { scrollTo, scrollToEnd: vi.fn(), flashScrollIndicators: vi.fn() } - : null - }) - renderer = mounted - const failed = (messageId: string) => - card({ messageId, needsAttention: true, caption: "Couldn't send" }) - const lay = async (heights: number[]) => { - const scroll = mounted.root.find((node) => String(node.type) === 'ScrollView') - await act(async () => { - scroll.props.onLayout({ nativeEvent: { layout: { height: 100 } } }) - scroll.props.onContentSizeChange(0, 200) - rows(mounted).forEach((row, index) => - row.props.onLayout({ nativeEvent: { layout: { height: heights[index] } } }) - ) - }) - } - // Mounted above a prompt with a failure behind the fold: open, and its row is shown. - const opened = [card({ messageId: 'a' }), card({ messageId: 'b' }), failed('c')] - await act(async () => mounted.update(element({ cards: opened }, true))) - expect(rows(mounted)).toHaveLength(3) - await lay([60, 60, 80]) - expect(scrollTo).toHaveBeenLastCalledWith({ y: 100, animated: false }) - // Open above the composer, a row fails without changing height: no row lays out again, - // so the request settles on the heights the list already has. - await act(async () => - mounted.update( - element({ - cards: [card({ messageId: 'p' }), card({ messageId: 'q' }), card({ messageId: 'r' })] - }) - ) - ) - await lay([60, 60, 80]) - scrollTo.mockClear() - await act(async () => - mounted.update( - element({ cards: [card({ messageId: 'p' }), card({ messageId: 'q' }), failed('r')] }) - ) - ) - expect(scrollTo).not.toHaveBeenCalled() - await act(async () => { - vi.advanceTimersByTime(QUEUED_ROW_SHOW_SETTLE_MS) - }) - expect(scrollTo).toHaveBeenCalledWith({ y: 100, animated: false }) - } finally { - vi.useRealTimers() - } - }) - - it("places a failed row below an open inline editor at the editor's height", async () => { - const scrollTo = vi.fn() - const mounted = create(createElement('View'), { - createNodeMock: (node) => - node.type === 'ScrollView' - ? { scrollTo, scrollToEnd: vi.fn(), flashScrollIndicators: vi.fn() } - : null - }) - renderer = mounted - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: only messageId is read; the editor composer is mocked, so the RPC client is never touched. - const editor = { messageId: 'a' } as unknown as Props['editor'] - const cards = [card({ messageId: 'a' }), card({ messageId: 'b' }), card({ messageId: 'c' })] - await act(async () => mounted.update(element({ cards, editor }))) - await act(async () => - mounted.update( - element({ - cards: [cards[0]!, cards[1]!, card({ messageId: 'c', needsAttention: true })], - editor - }) - ) - ) - const scroll = mounted.root.find((node) => String(node.type) === 'ScrollView') - await act(async () => { - // The editor row is 200pt tall; the compact rows 60pt. - rows(mounted).forEach((row, index) => - row.props.onLayout({ - nativeEvent: { layout: { x: 0, y: 0, width: 0, height: index === 0 ? 200 : 60 } } - }) - ) - scroll.props.onLayout({ nativeEvent: { layout: { height: 100 } } }) - scroll.props.onContentSizeChange(0, 320) - }) - // The failed row spans 260–320, past the editor. - expect(scrollTo).toHaveBeenLastCalledWith({ y: 220, animated: false }) - }) - - it("fills the head's Steer pill while the composer previews Steer queued", async () => { - const headLight = createMobileQueuedHeadLight() - const mounted = await mount({ cards: three(), agentWorking: true, headLight }) - const pills = () => - mounted.root.findAll( - (node) => - typeof node.type === 'string' && - node.props.accessibilityLabel === 'Submit without interrupting the model' - ) - const label = (pill: ReturnType[number]) => - pill.find((node) => String(node.type) === 'Text').props.style - expect(pills()[0]!.props.style({ pressed: false })).not.toContain(styles.litAction) - await act(async () => headLight.set(true)) - const [head, second] = pills() - expect(head!.props.style({ pressed: false })).toContain(styles.litAction) - expect(label(head!)).toContain(styles.litActionLabel) - // A press still dims the filled pill. - const pressed = head!.props.style({ pressed: true }) - expect(pressed.indexOf(pressedStyle)).toBeGreaterThan(pressed.indexOf(styles.litAction)) - expect(second!.props.style({ pressed: false })).not.toContain(styles.litAction) - }) - - it('keeps a folded list folded when a whole new queue replaces it', async () => { - const mounted = await mount({ cards: three() }, true) - await act(async () => - mounted.update(element({ cards: [card({ messageId: 'p' }), card({ messageId: 'q' })] }, true)) - ) - expect(rows(mounted)).toHaveLength(1) - }) - - it('leads the first row with Queued and the spoken count, but not on a returned head', async () => { - const lead = (mounted: ReactTestRenderer) => - mounted.root.findAll( - (node) => typeof node.type === 'string' && node.props.accessibilityLiveRegion === 'polite' - ) - const mounted = await mount({ cards: three() }) - const [label] = lead(mounted) - expect(label!.props.accessibilityLabel).toBe('3 queued messages') - expect(label!.props.children).toBe('Queued') - await act(async () => mounted.update(element({ cards: [card({ messageId: 'a' })] }))) - expect(lead(mounted)[0]!.props.accessibilityLabel).toBe('1 queued message') - await act(async () => - mounted.update( - element({ cards: [card({ messageId: 'a', state: 'returned', caption: 'Returned' })] }) - ) - ) - expect(lead(mounted).some((node) => node.props.children === 'Queued')).toBe(false) - }) - - it('keeps an expanded message open across a fold and back', async () => { - const mounted = await mount({ cards: three() }) - const text = () => - mounted.root.findAll( - (node) => typeof node.type === 'function' && node.type.name === 'MobileQueuedCardText' - ) - await act(async () => text()[0]!.props.onToggle()) - expect(text()[0]!.props.expanded).toBe(true) - await act(async () => foldToggle(mounted).props.onPress()) - await act(async () => foldToggle(mounted).props.onPress()) - expect(text().map((node) => node.props.expanded)).toEqual([true, false, false]) - }) - - it('draws one message as a single row with no fold row', async () => { - const mounted = await mount({ cards: [card({ messageId: 'a' })] }, true) - expect(rows(mounted)).toHaveLength(1) - expect(mounted.root.findAll((node) => node.props.testID === 'queued-fold-toggle')).toHaveLength( - 0 - ) - }) - - it('offers drag only while two or more messages are open', async () => { - const onMove = vi.fn(async () => true) - const one = await mount({ cards: [card({ messageId: 'a' })], onMove }) - expect(dragList(one)).toHaveLength(0) - act(() => one.unmount()) - const folded = await mount({ cards: three(), onMove }, true) - expect(dragList(folded)).toHaveLength(0) - await act(async () => foldToggle(folded).props.onPress()) - expect(dragList(folded)).toHaveLength(1) - }) - - it("moves Delete into the row's menu and keeps Edit off a goal", async () => { - const onDelete = vi.fn(async () => true) - const onEdit = vi.fn(async () => true) - const mounted = await mount({ - cards: [card({ messageId: 'goal', command: true }), card({ messageId: 'text' })], - onDelete, - onEdit - }) - await act(async () => control(mounted, 'More actions', 0).props.onPress()) - expect(menuAction(mounted, 'editMessage')).toBeUndefined() - await act(async () => menuAction(mounted, 'deleteMessage')!.onPress()) - expect(onDelete).toHaveBeenCalledWith('goal') - await act(async () => control(mounted, 'More actions', 1).props.onPress()) - await act(async () => menuAction(mounted, 'editMessage')!.onPress()) - expect(onEdit).toHaveBeenCalledWith('text') - }) - - it('offers Clear all in the menu of a queue that still sends on its own', async () => { - const onClearAll = vi.fn(async () => true) - const mounted = await mount({ cards: three(), onClearAll, clearable: true }) - await act(async () => control(mounted, 'More actions').props.onPress()) - await act(async () => menuAction(mounted, 'clearAll')!.onPress()) - expect(onClearAll).toHaveBeenCalledWith(['a', 'b', 'c']) - }) - - describe('a paused queue', () => { - const waiting = card({ messageId: 'waiting-1', text: 'next' }) - - async function mountPaused( - props: Partial[0]> - ): Promise { - return mount({ - cards: [waiting], - onSend: vi.fn(async () => true), - onClearAll: vi.fn(async () => true), - ...props - }) - } - - function resumeButton(mounted: ReactTestRenderer) { - return control(mounted, 'Resume sending the queued messages') - } - - it('Resume admits one press at a time and releases a lost answer for retry', async () => { - let answer: (resumed: boolean) => void = () => undefined - const onResume = vi.fn(() => new Promise((resolve) => (answer = resolve))) - const mounted = await mountPaused({ pause: { reason: 'stopped' }, onResume }) - await act(async () => { - resumeButton(mounted).props.onPress() - resumeButton(mounted).props.onPress() - }) - expect(onResume).toHaveBeenCalledOnce() - expect(resumeButton(mounted).props.disabled).toBe(true) - await act(async () => answer(false)) - expect(resumeButton(mounted).props.disabled).toBe(false) - await act(async () => resumeButton(mounted).props.onPress()) - expect(onResume).toHaveBeenCalledTimes(2) - }) - }) -}) diff --git a/mobile/src/transport/connection/client-authority-epoch.test.ts b/mobile/src/transport/connection/client-authority-epoch.test.ts deleted file mode 100644 index 90693582755..00000000000 --- a/mobile/src/transport/connection/client-authority-epoch.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { createClientAuthorityEpoch } from './client-authority-epoch' - -describe('createClientAuthorityEpoch', () => { - it('expires a token when the client generation moves without a new update', () => { - const epoch = createClientAuthorityEpoch() - let generation = 1 - epoch.update({ - client: { getGeneration: () => generation }, - hostId: 'host-a', - executionHost: undefined, - worktreeId: 'wt-1' - }) - const token = epoch.token() - - generation = 2 - - expect(epoch.isCurrent(token)).toBe(false) - }) -}) From 255716e31da0f5fcb7a36f446890748decb57a2e Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 05:12:49 +0000 Subject: [PATCH 015/178] feat(cli): add orca workspace cleanup preview, run and report (#41831) * feat(cli): add orca workspace cleanup preview, run and report * fix(cli): sanitize cleanup output and label unknown host values * fix(cli): sanitize run triggers in cleanup report * refactor(cli): share keep-reason wording in cleanup output * fix(cli): label automation-run and orchestration-worker cleanup reasons --- docs/site/content/docs/cli/reference.mdx | 10 ++ skill-guides/orca-cli.md | 2 + src/cli/discovery/root-help-text-primary.ts | 5 + src/cli/skills/bundled-skill-guides.ts | 2 + src/cli/specs/index.ts | 2 + src/cli/specs/workspace-cleanup.ts | 40 +++++ .../workspaces/workspace-cleanup-format.ts | 153 ++++++++++++++++++ src/cli/workspaces/workspace-cleanup.ts | 86 ++++++++++ src/cli/workspaces/worktree-handler-groups.ts | 5 + .../cleanup/workspace-cleanup-run-summary.ts | 15 +- .../workspace-cleanup-run.ts | 7 + .../workspace-cleanup-verdict-labels.ts | 51 ++++++ 12 files changed, 366 insertions(+), 12 deletions(-) create mode 100644 src/cli/specs/workspace-cleanup.ts create mode 100644 src/cli/workspaces/workspace-cleanup-format.ts create mode 100644 src/cli/workspaces/workspace-cleanup.ts create mode 100644 src/shared/workspace-cleanup/workspace-cleanup-verdict-labels.ts diff --git a/docs/site/content/docs/cli/reference.mdx b/docs/site/content/docs/cli/reference.mdx index bd03318a771..e8314c890ff 100644 --- a/docs/site/content/docs/cli/reference.mdx +++ b/docs/site/content/docs/cli/reference.mdx @@ -151,6 +151,16 @@ Omitting a flag leaves its link unchanged. On `set`, literal `null` clears only Folder-based repositories can store numeric links through these commands, but a number does not supply a provider host or project. Without existing source context or a stored remote, pasted GitLab URLs are refused and provider details or clickable links may be unavailable. These flags use fields already supported by the runtime; an older runtime that predates a field may ignore it, so check `worktree show --json` after writing to an older host. +### Workspace cleanup + +```bash +orca workspace cleanup preview --json +orca workspace cleanup run --json +orca workspace cleanup report --json +``` + +`preview` shows what your cleanup rules would remove now and why every other workspace stays. `run` applies the rules now: each workspace is checked again right before removal and nothing is forced. `report` lists recent runs and when the next one is due. With no rule on, nothing is removed. Rules are set in Orca settings; these commands never change them. Add `--environment ` to run them on a paired server. + ## Ports ```bash diff --git a/skill-guides/orca-cli.md b/skill-guides/orca-cli.md index adc728e24b0..4a7bd3a498c 100644 --- a/skill-guides/orca-cli.md +++ b/skill-guides/orca-cli.md @@ -128,6 +128,8 @@ ORCA worktree rm --worktree id::: --force --json Ports: `ORCA ports --json` lists the listening ports Orca tied to the current workspace (`--worktree ` for another one, `--all` for every listener on that workspace's host). Each row has `port`, `pid`, `processName`, `connectHost`, `protocol`, and `kind` (`workspace`, `container`, or `external`). Only `workspace` rows have `owner` (with `owner.confidence`: `cwd` or `command`) and, when the dev server printed one in an Orca terminal, `advertisedUrl`. Use this to find a dev server's port instead of running `lsof` yourself. If `unavailableReason` is set, the scan could not run on that host. Native folder workspaces need `--all`, because Orca does not tie ports to them. SSH workspaces opened directly from this app report `unavailableReason` for now; a workspace on a managed Orca server is scanned on that server. +Workspace cleanup: `ORCA workspace cleanup preview --json` shows what the user's cleanup rules would remove now and why each other workspace stays (`rows[].evaluation`). `ORCA workspace cleanup run --json` runs the rules now, on the same path as the scheduled runner (every workspace is re-checked right before removal, nothing is forced), and prints the finished run's rows. `ORCA workspace cleanup report --json` lists recent runs; `nextRunAt` is null when no rule is on. Rules live in Orca settings; these commands never change them. Run `run` only when the user asks for cleanup. + Use `repo set --external-worktree-visibility show` to show a repo's non-Orca worktrees. `hide` hides them; `inherit` clears the repo override and follows the global default. Per-worktree visibility rules still apply. diff --git a/src/cli/discovery/root-help-text-primary.ts b/src/cli/discovery/root-help-text-primary.ts index dad044867cc..fc062da5526 100644 --- a/src/cli/discovery/root-help-text-primary.ts +++ b/src/cli/discovery/root-help-text-primary.ts @@ -122,6 +122,11 @@ export const ROOT_HELP_TEXT_PRIMARY = [ ' worktree ps Show a compact orchestration summary across worktrees', ' ports List the listening ports Orca detected for a workspace', '', + 'Workspace Cleanup:', + ' workspace cleanup preview Show what the cleanup rules would remove now', + ' workspace cleanup run Run the cleanup rules now', + ' workspace cleanup report Show the last cleanup runs', + '', 'Agents:', ' agent launch Start an agent in a worktree, as a chat or terminal per your settings', ' agent hooks Inspect and toggle Orca agent lifecycle hooks', diff --git a/src/cli/skills/bundled-skill-guides.ts b/src/cli/skills/bundled-skill-guides.ts index 9a7cebdb579..4815fccff34 100644 --- a/src/cli/skills/bundled-skill-guides.ts +++ b/src/cli/skills/bundled-skill-guides.ts @@ -356,6 +356,8 @@ const ORCA_CLI_MARKDOWN = [ "", "Ports: `ORCA ports --json` lists the listening ports Orca tied to the current workspace (`--worktree ` for another one, `--all` for every listener on that workspace's host). Each row has `port`, `pid`, `processName`, `connectHost`, `protocol`, and `kind` (`workspace`, `container`, or `external`). Only `workspace` rows have `owner` (with `owner.confidence`: `cwd` or `command`) and, when the dev server printed one in an Orca terminal, `advertisedUrl`. Use this to find a dev server's port instead of running `lsof` yourself. If `unavailableReason` is set, the scan could not run on that host. Native folder workspaces need `--all`, because Orca does not tie ports to them. SSH workspaces opened directly from this app report `unavailableReason` for now; a workspace on a managed Orca server is scanned on that server.", "", + "Workspace cleanup: `ORCA workspace cleanup preview --json` shows what the user's cleanup rules would remove now and why each other workspace stays (`rows[].evaluation`). `ORCA workspace cleanup run --json` runs the rules now, on the same path as the scheduled runner (every workspace is re-checked right before removal, nothing is forced), and prints the finished run's rows. `ORCA workspace cleanup report --json` lists recent runs; `nextRunAt` is null when no rule is on. Rules live in Orca settings; these commands never change them. Run `run` only when the user asks for cleanup.", + "", "Use `repo set --external-worktree-visibility show` to show a repo's non-Orca worktrees.", "`hide` hides them; `inherit` clears the repo override and follows the global default.", "Per-worktree visibility rules still apply.", diff --git a/src/cli/specs/index.ts b/src/cli/specs/index.ts index 33b953cfcba..33247b77bd4 100644 --- a/src/cli/specs/index.ts +++ b/src/cli/specs/index.ts @@ -34,6 +34,7 @@ import { PLUGIN_AUTHORING_COMMAND_SPECS, PLUGIN_RUNTIME_COMMAND_SPECS } from './ import { PAIR_COMMAND_SPECS } from './pair' import { PLUGIN_VIEW_COMMAND_SPECS } from './plugin-view' import { PLUGIN_INSTALL_COMMAND_SPECS } from './plugin-install' +import { WORKSPACE_CLEANUP_COMMAND_SPECS } from './workspace-cleanup' export const COMMAND_SPECS: CommandSpec[] = [ ...CORE_COMMAND_SPECS, @@ -43,6 +44,7 @@ export const COMMAND_SPECS: CommandSpec[] = [ ...PROJECT_COMMAND_SPECS, ...FILE_COMMAND_SPECS, ...AUTOMATION_COMMAND_SPECS, + ...WORKSPACE_CLEANUP_COMMAND_SPECS, ...BROWSER_BASIC_COMMAND_SPECS, ...BROWSER_ADVANCED_COMMAND_SPECS, ...ORCHESTRATION_COMMAND_SPECS.map(withFullRecordFlag), diff --git a/src/cli/specs/workspace-cleanup.ts b/src/cli/specs/workspace-cleanup.ts new file mode 100644 index 00000000000..344986df8fb --- /dev/null +++ b/src/cli/specs/workspace-cleanup.ts @@ -0,0 +1,40 @@ +import type { CommandSpec } from '../discovery/command-spec' +import { CLI_GLOBAL_FLAGS as GLOBAL_FLAGS } from '../../shared/cli-argument-boundary' + +const HOST_NOTE = + 'Runs on the Orca that owns the repos: this computer by default, or a paired server with --environment or --pairing-code. Rules are set in Orca settings; these commands never change them.' + +export const WORKSPACE_CLEANUP_COMMAND_SPECS: CommandSpec[] = [ + { + path: ['workspace', 'cleanup', 'preview'], + summary: 'Show what the cleanup rules would remove now, and why the rest stay', + usage: 'orca workspace cleanup preview [--json]', + allowedFlags: [...GLOBAL_FLAGS], + notes: [ + HOST_NOTE, + 'Removes nothing. Text output hides workspaces no rule watches (main workspaces, folder projects, projects with cleanup off); --json prints every row the host returned.' + ], + examples: ['orca workspace cleanup preview', 'orca workspace cleanup preview --json'] + }, + { + path: ['workspace', 'cleanup', 'run'], + destructive: true, + summary: 'Run the cleanup rules now and print what was removed, kept and failed', + usage: 'orca workspace cleanup run [--json]', + allowedFlags: [...GLOBAL_FLAGS], + notes: [ + HOST_NOTE, + 'Each workspace is checked again right before it is removed, and nothing is forced. If a run is already going, this waits for it and prints its result.', + 'With no rule on, nothing is removed.' + ], + examples: ['orca workspace cleanup run', 'orca workspace cleanup run --json'] + }, + { + path: ['workspace', 'cleanup', 'report'], + summary: 'Show the last cleanup runs and when the next one is due', + usage: 'orca workspace cleanup report [--json]', + allowedFlags: [...GLOBAL_FLAGS], + notes: [HOST_NOTE], + examples: ['orca workspace cleanup report', 'orca workspace cleanup report --json'] + } +] diff --git a/src/cli/workspaces/workspace-cleanup-format.ts b/src/cli/workspaces/workspace-cleanup-format.ts new file mode 100644 index 00000000000..6b51c538614 --- /dev/null +++ b/src/cli/workspaces/workspace-cleanup-format.ts @@ -0,0 +1,153 @@ +import { formatBytes } from '../../shared/format-bytes' +import { terminalSafeLine } from '../../shared/terminal/output/terminal-safe-text' +import type { + WorkspaceCleanupEvaluation, + WorkspaceCleanupKeepCause, + WorkspaceCleanupRuleReason, + WorkspaceCleanupWaiting +} from '../../shared/workspace-cleanup/workspace-cleanup-evaluation' +import { + WORKSPACE_CLEANUP_UNWATCHED_CAUSES, + type WorkspaceCleanupPreview, + type WorkspaceCleanupReport, + type WorkspaceCleanupRun, + type WorkspaceCleanupRunRow, + type WorkspaceCleanupWorkspaceRef +} from '../../shared/workspace-cleanup/workspace-cleanup-run' +import { + formatWorkspaceCleanupKeepCause, + formatWorkspaceCleanupRuleReason +} from '../../shared/workspace-cleanup/workspace-cleanup-verdict-labels' + +// The host reports no next run when no rule is on for any Git project it owns. +const NO_RULE_APPLIES = 'No cleanup rule is on for any project here' +export const NO_WORKSPACE_CLEANUP_RULE_ON = `${NO_RULE_APPLIES}, so nothing was removed.` + +// Names and errors come from the host; strip terminal controls so a label can't rewrite the screen. +function formatTable(header: string[], unsafeRows: string[][]): string { + const rows = unsafeRows.map((row) => row.map(terminalSafeLine)) + const widths = header.map((cell, column) => + Math.max(cell.length, ...rows.map((row) => row[column].length)) + ) + return [header, ...rows] + .map((row) => + row + .map((cell, column) => (column === row.length - 1 ? cell : cell.padEnd(widths[column]))) + .join(' ') + ) + .join('\n') +} + +function workspaceCells(ref: WorkspaceCleanupWorkspaceRef): string[] { + return [ref.displayName || ref.branch || ref.path, ref.repoName] +} + +function formatKeepWhy( + cause: WorkspaceCleanupKeepCause, + reason?: WorkspaceCleanupRuleReason, + waiting?: WorkspaceCleanupWaiting +): string { + const label = formatWorkspaceCleanupKeepCause(cause, waiting) + return reason ? `${label} (${formatWorkspaceCleanupRuleReason(reason)})` : label +} + +function formatWhy(evaluation: WorkspaceCleanupEvaluation): string { + return evaluation.action === 'keep' + ? formatKeepWhy(evaluation.cause, evaluation.reason, evaluation.waiting) + : formatWorkspaceCleanupRuleReason(evaluation.reason) +} + +function formatRunRowWhy(row: WorkspaceCleanupRunRow): string { + const parts: string[] = [] + if (row.outcome === 'failed') { + parts.push(row.error ?? 'failed') + } else if (row.keepCause) { + parts.push(formatKeepWhy(row.keepCause, row.reason, row.waiting)) + } else if (row.reason) { + parts.push(formatWorkspaceCleanupRuleReason(row.reason)) + } + if (row.keptOnRecheck) { + parts.push('found on re-check') + } + if (row.branchKept) { + parts.push(`branch ${row.branchKept} kept`) + } + return parts.join('; ') +} + +export function formatWorkspaceCleanupPreview(preview: WorkspaceCleanupPreview): string { + const watched = preview.rows.filter( + (row) => + row.evaluation.action !== 'keep' || + !WORKSPACE_CLEANUP_UNWATCHED_CAUSES.has(row.evaluation.cause) + ) + if (watched.length === 0) { + return preview.rows.some( + (row) => row.evaluation.action === 'keep' && row.evaluation.cause === 'rules-off' + ) + ? `${NO_RULE_APPLIES}, so nothing would be removed.` + : 'No workspace is watched by a cleanup rule.' + } + const removing = watched.filter((row) => row.evaluation.action !== 'keep').length + const table = formatTable( + ['WORKSPACE', 'PROJECT', 'ACTION', 'WHY'], + watched.map((row) => [...workspaceCells(row), row.evaluation.action, formatWhy(row.evaluation)]) + ) + return `Would remove ${removing}, keep ${watched.length - removing}.\n\n${table}` +} + +function formatRunSummary(run: WorkspaceCleanupRun): string { + const { counts } = run + const archived = counts.archived > 0 ? `, archived ${counts.archived}` : '' + const freed = run.freedBytes > 0 ? ` Freed ${formatBytes(run.freedBytes)}.` : '' + return `Removed ${counts.removed}${archived}, kept ${counts.kept}, failed ${counts.failed}.${freed}` +} + +export function formatWorkspaceCleanupRun(run: WorkspaceCleanupRun): string { + const lines = [formatRunSummary(run)] + if (run.error) { + lines.push(`The run stopped early: ${terminalSafeLine(run.error)}`) + } + if (run.rows.length > 0) { + lines.push( + '', + formatTable( + ['WORKSPACE', 'PROJECT', 'OUTCOME', 'WHY'], + run.rows.map((row) => [...workspaceCells(row), row.outcome, formatRunRowWhy(row)]) + ) + ) + } + return lines.join('\n') +} + +export function formatWorkspaceCleanupReport(report: WorkspaceCleanupReport): string { + const status = [ + report.nextRunAt === null + ? `${NO_RULE_APPLIES}, so no run is scheduled.` + : `Next run: ${new Date(report.nextRunAt).toISOString()}` + ] + if (report.running) { + status.push('A run is going now.') + } + const [latest, ...earlier] = report.runs + if (!latest) { + return `${status.join(' ')}\n\nNo cleanup runs yet.` + } + const lines = [ + status.join(' '), + '', + `Last run (${terminalSafeLine(latest.trigger)}, ${new Date(latest.startedAt).toISOString()}):`, + formatWorkspaceCleanupRun(latest) + ] + if (earlier.length > 0) { + lines.push( + '', + 'Earlier runs:', + ...earlier.map( + (run) => + `${new Date(run.startedAt).toISOString()} ${terminalSafeLine(run.trigger)} ${formatRunSummary(run)}${run.error ? ` ${terminalSafeLine(run.error)}` : ''}` + ) + ) + } + return lines.join('\n') +} diff --git a/src/cli/workspaces/workspace-cleanup.ts b/src/cli/workspaces/workspace-cleanup.ts new file mode 100644 index 00000000000..e8a18534109 --- /dev/null +++ b/src/cli/workspaces/workspace-cleanup.ts @@ -0,0 +1,86 @@ +import type { + WorkspaceCleanupPreview, + WorkspaceCleanupReport, + WorkspaceCleanupRun +} from '../../shared/workspace-cleanup/workspace-cleanup-run' +import type { RuntimeRpcSuccess } from '../../shared/runtime-rpc-envelope' +import type { CommandHandler, HandlerContext } from '../dispatch/dispatch' +import { printResult } from '../output/command-result-output' +import { RuntimeClientError } from '../runtime/types' +import { + formatWorkspaceCleanupPreview, + formatWorkspaceCleanupReport, + formatWorkspaceCleanupRun, + NO_WORKSPACE_CLEANUP_RULE_ON +} from './workspace-cleanup-format' + +// Preview reads Git for every workspace; a run also re-checks and removes each match before it answers. +const WORKSPACE_CLEANUP_EVALUATE_TIMEOUT_MS = 10 * 60_000 + +async function callWorkspaceCleanup( + client: HandlerContext['client'], + method: 'workspaceCleanup.preview' | 'workspaceCleanup.runNow' | 'workspaceCleanup.report', + params?: unknown +): Promise> { + try { + return await client.call( + method, + params, + method === 'workspaceCleanup.report' + ? undefined + : { timeoutMs: WORKSPACE_CLEANUP_EVALUATE_TIMEOUT_MS } + ) + } catch (error) { + if (error instanceof RuntimeClientError && error.code === 'method_not_found') { + throw new RuntimeClientError( + 'incompatible_runtime', + 'This Orca host does not support workspace cleanup commands yet. Update Orca on that host.' + ) + } + if ( + error instanceof RuntimeClientError && + error.code === 'runtime_timeout' && + method === 'workspaceCleanup.runNow' + ) { + throw new RuntimeClientError( + 'runtime_timeout', + 'Timed out waiting for the cleanup run. It may still be going on the host; check `orca workspace cleanup report`.' + ) + } + throw error + } +} + +export const WORKSPACE_CLEANUP_HANDLERS: Record = { + 'workspace cleanup preview': async ({ client, json }) => { + printResult( + await callWorkspaceCleanup(client, 'workspaceCleanup.preview'), + json, + formatWorkspaceCleanupPreview + ) + }, + 'workspace cleanup run': async ({ client, json }) => { + const response = await callWorkspaceCleanup( + client, + 'workspaceCleanup.runNow', + { trigger: 'cli' } + ) + // A run with no rule on watches nothing, so its receipt is empty. + const rulesOff = + !json && + response.result.rows.length === 0 && + !response.result.error && + (await callWorkspaceCleanup(client, 'workspaceCleanup.report')).result + .nextRunAt === null + printResult(response, json, (run) => + rulesOff ? NO_WORKSPACE_CLEANUP_RULE_ON : formatWorkspaceCleanupRun(run) + ) + }, + 'workspace cleanup report': async ({ client, json }) => { + printResult( + await callWorkspaceCleanup(client, 'workspaceCleanup.report'), + json, + formatWorkspaceCleanupReport + ) + } +} diff --git a/src/cli/workspaces/worktree-handler-groups.ts b/src/cli/workspaces/worktree-handler-groups.ts index b45f5ded6e3..7d009dfddcd 100644 --- a/src/cli/workspaces/worktree-handler-groups.ts +++ b/src/cli/workspaces/worktree-handler-groups.ts @@ -18,5 +18,10 @@ export const WORKTREE_HANDLER_GROUPS: readonly HandlerGroup[] = [ name: 'worktree-activate', keys: ['worktree activate'], load: async () => (await import('./worktree-activate.js')).WORKTREE_ACTIVATE_HANDLERS + }, + { + name: 'workspace-cleanup', + keys: ['workspace cleanup preview', 'workspace cleanup run', 'workspace cleanup report'], + load: async () => (await import('./workspace-cleanup.js')).WORKSPACE_CLEANUP_HANDLERS } ] diff --git a/src/main/workspaces/cleanup/workspace-cleanup-run-summary.ts b/src/main/workspaces/cleanup/workspace-cleanup-run-summary.ts index 156775e8245..b865a5ea5b1 100644 --- a/src/main/workspaces/cleanup/workspace-cleanup-run-summary.ts +++ b/src/main/workspaces/cleanup/workspace-cleanup-run-summary.ts @@ -1,21 +1,12 @@ -import type { - WorkspaceCleanupEvaluation, - WorkspaceCleanupKeepCause -} from '../../../shared/workspace-cleanup/workspace-cleanup-evaluation' +import type { WorkspaceCleanupEvaluation } from '../../../shared/workspace-cleanup/workspace-cleanup-evaluation' import { WORKSPACE_CLEANUP_REPORT_ROW_LIMIT, + WORKSPACE_CLEANUP_UNWATCHED_CAUSES, type WorkspaceCleanupRun, type WorkspaceCleanupRunOutcome, type WorkspaceCleanupRunRow } from '../../../shared/workspace-cleanup/workspace-cleanup-run' -// Workspaces no rule watches are not part of a run's receipt. -const UNWATCHED_CAUSES: ReadonlySet = new Set([ - 'not-removable', - 'project-off', - 'rules-off' -]) - export type WorkspaceCleanupKeptOutcome = Pick< WorkspaceCleanupRunRow, 'outcome' | 'reason' | 'keepCause' | 'waiting' @@ -56,7 +47,7 @@ export function summarizeWorkspaceCleanupRun(args: { error?: string }): WorkspaceCleanupRun { const rows = args.rows.filter( - (row) => row.keepCause === undefined || !UNWATCHED_CAUSES.has(row.keepCause) + (row) => row.keepCause === undefined || !WORKSPACE_CLEANUP_UNWATCHED_CAUSES.has(row.keepCause) ) const counts: Record = { removed: 0, diff --git a/src/shared/workspace-cleanup/workspace-cleanup-run.ts b/src/shared/workspace-cleanup/workspace-cleanup-run.ts index b7ed1134c99..9e762ff0992 100644 --- a/src/shared/workspace-cleanup/workspace-cleanup-run.ts +++ b/src/shared/workspace-cleanup/workspace-cleanup-run.ts @@ -6,6 +6,13 @@ import type { WorkspaceCleanupWaiting } from './workspace-cleanup-evaluation' +// Workspaces no rule watches are not part of a run's receipt. +export const WORKSPACE_CLEANUP_UNWATCHED_CAUSES: ReadonlySet = new Set([ + 'not-removable', + 'project-off', + 'rules-off' +]) + export const WORKSPACE_CLEANUP_REPORT_RUN_LIMIT = 20 // Matches T3 Code's report cap; a run over hundreds of workspaces must not bloat the sidecar. export const WORKSPACE_CLEANUP_REPORT_ROW_LIMIT = 200 diff --git a/src/shared/workspace-cleanup/workspace-cleanup-verdict-labels.ts b/src/shared/workspace-cleanup/workspace-cleanup-verdict-labels.ts new file mode 100644 index 00000000000..0cfbcb268c8 --- /dev/null +++ b/src/shared/workspace-cleanup/workspace-cleanup-verdict-labels.ts @@ -0,0 +1,51 @@ +import type { + WorkspaceCleanupKeepCause, + WorkspaceCleanupRuleReason, + WorkspaceCleanupWaiting +} from './workspace-cleanup-evaluation' + +/** Plain-words names for the rule that matched a workspace. */ +export const WORKSPACE_CLEANUP_RULE_REASON_LABELS: Record = { + merged: 'merged', + unchanged: 'no changes', + inactive: 'inactive', + 'automation-run': 'finished automation run', + 'orchestration-worker': 'released orchestration worker' +} + +/** Plain-words names for why the runner left a workspace alone. */ +export const WORKSPACE_CLEANUP_KEEP_CAUSE_LABELS: Record = { + 'not-removable': 'main workspace or folder project', + 'project-off': 'cleanup off for this project', + 'rules-off': 'no rule on', + pinned: 'pinned', + 'no-rule-matched': 'no rule matched', + waiting: 'waiting', + 'recently-active': 'used recently', + 'active-workspace': 'active workspace', + 'agent-running': 'agent running', + 'terminal-running': 'terminal running a process', + 'terminal-unknown': 'terminal state unknown', + 'unsaved-editor-changes': 'unsaved editor changes', + 'uncommitted-changes': 'uncommitted changes', + 'unpushed-commits': 'unpushed commits', + 'ignored-files': 'ignored local files', + 'git-unknown': 'Git status unknown', + 'archive-unavailable': 'archive not available', + 'archive-script': 'has an archive script', + removing: 'already being removed' +} + +// A newer host can send a value this build has no label for; show it raw rather than nothing. +export function formatWorkspaceCleanupRuleReason(reason: WorkspaceCleanupRuleReason): string { + return WORKSPACE_CLEANUP_RULE_REASON_LABELS[reason] ?? reason +} + +export function formatWorkspaceCleanupKeepCause( + cause: WorkspaceCleanupKeepCause, + waiting?: WorkspaceCleanupWaiting +): string { + return cause === 'waiting' && waiting + ? `waiting ${waiting.elapsedDays} of ${waiting.afterDays} days` + : (WORKSPACE_CLEANUP_KEEP_CAUSE_LABELS[cause] ?? cause) +} From 479f564829ba347cd8df404b9ee34ece65133e7c Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Fri, 13 Nov 2026 00:18:52 -0500 Subject: [PATCH 016/178] perf(orchestration): rank empty-check hint candidates in one pass over the Run (#41779) * perf(orchestration): rank empty-check hint candidates in one pass over the Run * perf(orchestration): take the oldest candidate without spreading arguments --- .../worker-report-wait-candidates.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/runtime/orchestration/db/worker-terminal/worker-report-wait-candidates.ts b/src/main/runtime/orchestration/db/worker-terminal/worker-report-wait-candidates.ts index 3ee96e9326e..afa6371cfe9 100644 --- a/src/main/runtime/orchestration/db/worker-terminal/worker-report-wait-candidates.ts +++ b/src/main/runtime/orchestration/db/worker-terminal/worker-report-wait-candidates.ts @@ -56,10 +56,12 @@ const SEED_SQL = `WITH seed(id) AS ( FROM seed CROSS JOIN dispatch_contexts d ON d.id = seed.id AND d.run_id = ?1 ${CANDIDATE_JOINS}` -/** Newest-first position in the Run, which is how worker-list pages it. */ -const RUN_POSITION_SQL = `SELECT requested.value AS database_id, - (SELECT COUNT(*) FROM dispatch_contexts WHERE run_id = ?2 AND rowid > requested.value) AS position - FROM json_each(?1) AS requested` +/** Newest-first position in the Run, which is how worker-list pages it; one pass down to the oldest candidate (?3). */ +const RUN_POSITION_SQL = `WITH ranked AS ( + SELECT rowid AS database_id, ROW_NUMBER() OVER (ORDER BY rowid DESC) - 1 AS position + FROM dispatch_contexts WHERE run_id = ?2 AND rowid >= ?3) + SELECT database_id, position FROM ranked + WHERE database_id IN (SELECT value FROM json_each(?1))` /** The row `offset` rows from `rowid` in the Run, newer or older. */ const NEWER_ROW_SQL = `SELECT rowid AS database_id FROM dispatch_contexts @@ -116,9 +118,11 @@ export function listWorkerReportWaitCandidates(this: OrchestrationDb, runId: str } const candidates = seeded.filter(isCandidate) // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the query selects exactly these columns. - const positions = this.db - .prepare(RUN_POSITION_SQL) - .all(JSON.stringify(candidates.map((row) => row.database_id)), runId) as { + const positions = this.db.prepare(RUN_POSITION_SQL).all( + JSON.stringify(candidates.map((row) => row.database_id)), + runId, + candidates.reduce((min, row) => Math.min(min, row.database_id), Infinity) + ) as { database_id: number position: number }[] From 98bcd7943a02a32e263393130890939f3c427711 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 21:30:08 -0800 Subject: [PATCH 017/178] refactor: organize text generation around concrete owners (#41838) * refactor: organize text-generation modules around concrete owners * refactor: combine imports from the defining CLI command owner --- config/main-sync-fs-baseline.txt | 2 +- .../scripts/ci/sharding/ci-shard-timings.json | 43 +- src/main/acp/text-task/acp-text-task.ts | 2 +- .../branch-rename-failure-output.ts | 2 +- .../first-work/first-work-branch-rename.ts | 6 +- .../first-work-generation-backoff.ts | 2 +- .../first-work-generation-target.ts | 4 +- .../first-work-workspace-title-rename.ts | 2 +- src/main/grok/grok-text-task.ts | 6 +- ...lesystem-git-commit-generation-handlers.ts | 6 +- ...filesystem-git-model-discovery-handlers.ts | 10 +- ...em-git-pull-request-generation-handlers.ts | 4 +- .../filesystem-git-status-staging.test.ts | 2 +- .../filesystem/filesystem-handler-context.ts | 2 +- ...pull-request-linked-issue-lifetime.test.ts | 2 +- .../filesystem-source-control-ai-targets.ts | 4 +- .../filesystem-wsl-stopped-distro-git.test.ts | 2 +- ...ilesystem-wsl-stopped-distro-reads.test.ts | 2 +- src/main/ipc/filesystem/filesystem.test.ts | 2 +- src/main/ipc/filesystem/filesystem.ts | 2 +- src/main/ipc/jira/issue-summary-generation.ts | 4 +- .../register-core-handlers.ts | 2 +- src/main/ipc/speech.ts | 2 +- .../structured-chat-naming-types.ts | 2 +- .../native-chat/structured-chat-naming.ts | 2 +- .../text-tasks/opencode-text-task-events.ts | 2 +- .../text-tasks/opencode-text-task-http.ts | 2 +- .../opencode-text-task-server-ready.ts | 2 +- .../opencode/text-tasks/opencode-text-task.ts | 8 +- .../wsl/opencode-wsl-text-task-entry.ts | 4 +- .../text-tasks/wsl/opencode-wsl-text-task.ts | 6 +- .../host-account-launch-env-state.ts | 2 +- .../environment/runtime-account-controller.ts | 2 +- .../structured-chat-naming-contract.test.ts | 110 ---- .../naming/structured-chat-naming-runtime.ts | 4 +- .../git/runtime-git-command-target.ts | 2 +- .../runtime-git-generation-admission.test.ts | 4 +- .../git/runtime-git-generation-commands.ts | 4 +- .../git/runtime-git-generation-context.ts | 4 +- .../jira/runtime-jira-commands.ts | 2 +- .../runtime-mobile-dictation-controller.ts | 2 +- .../runtime-local-worktree-create.test.ts | 4 +- src/main/speech/dictation-cleanup-service.ts | 8 +- .../agent-generation-request.ts | 8 +- .../commit-message-text-generation.ts | 64 +-- .../command-environment-process.test.ts | 2 +- .../commit-message-agent-environment.test.ts | 2 +- .../commit-message-agent-environment.ts | 10 +- ...ssage-text-generation-cancellation.test.ts | 4 +- ...e-text-generation-local-subprocess.test.ts | 4 +- ...e-text-generation-remote-execution.test.ts | 2 +- ...it-message-text-generation-test-harness.ts | 2 +- .../source-control-agent-cli-path.test.ts | 4 +- ...ource-control-agent-hook-isolation.test.ts | 0 .../source-control-agent-launch.test.ts | 4 +- .../source-control-agent-launch.ts | 20 +- .../source-control-agent-termination.ts | 6 +- .../source-control-command-line-budget.ts | 9 +- .../source-control-generation-lanes.ts | 2 +- .../source-control-generation-limits.ts | 0 .../source-control-local-generation.ts | 14 +- .../source-control-local-process.test.ts | 6 +- .../source-control-local-process.ts | 21 +- .../source-control-remote-generation.ts | 10 +- .../source-control-supervised-agent-spawn.ts | 15 +- .../source-control-wsl-launch.test.ts | 105 ++++ .../agent-failure-output.test.ts | 0 .../{ => failures}/agent-failure-output.ts | 2 +- .../antigravity-generation-stream.test.ts | 8 +- ...xt-generation-failure-sanitization.test.ts | 2 +- .../copilot-unavailable-model-retry.ts | 6 +- .../kimi-generation-failure.test.ts | 6 +- .../{ => failures}/kimi-generation-failure.ts | 6 +- .../source-control-agent-failure.ts | 16 +- .../codex-model-list-discovery.ts | 16 +- .../commit-message-model-discovery-policy.ts | 18 +- .../commit-message-model-discovery.ts | 28 +- ...ge-text-generation-model-discovery.test.ts | 10 +- .../model-discovery-host-cache.ts | 14 +- .../source-control-kimi-policy.ts | 6 +- .../source-control-policy-probe.ts | 12 +- .../source-control-read-only-policy.test.ts | 8 +- .../source-control-read-only-policy.ts | 16 +- .../source-control-omp-prompt-file.test.ts | 24 +- .../source-control-prompt-files.ts | 8 +- .../source-control-prompt-producer.ts | 0 .../source-control-wsl-prompt.test.ts | 149 +----- .../pull-request-context.test.ts | 482 ------------------ .../pull-request-context.ts | 8 +- .../pull-request-remote-ref-probes.ts | 8 +- ...essage-text-generation-branch-name.test.ts | 4 +- ...e-text-generation-generated-output.test.ts | 8 +- ...message-text-generation-regression.test.ts | 2 +- ...nversation-name-generation-request.test.ts | 10 +- .../conversation-name-generation-request.ts | 10 +- .../dictation-cleanup-generation-request.ts | 16 +- .../jira-issue-summary-requests.test.ts | 25 +- .../jira-issue-summary-requests.ts | 12 +- .../jira-issue-summary-text-generation.ts | 14 +- ...source-control-text-generation-requests.ts | 31 +- .../source-control-text-generation-types.ts | 2 +- .../source-control-wsl-launch.test.ts | 245 --------- .../chat-naming-catalog-work.test-fixture.ts | 66 --- 103 files changed, 467 insertions(+), 1436 deletions(-) delete mode 100644 src/main/runtime/agent-session/naming/structured-chat-naming-contract.test.ts rename src/main/text-generation/{ => execution}/command-environment-process.test.ts (98%) rename src/main/text-generation/{ => execution}/commit-message-agent-environment.test.ts (98%) rename src/main/text-generation/{ => execution}/commit-message-agent-environment.ts (93%) rename src/main/text-generation/{ => execution}/commit-message-text-generation-cancellation.test.ts (97%) rename src/main/text-generation/{ => execution}/commit-message-text-generation-local-subprocess.test.ts (95%) rename src/main/text-generation/{ => execution}/commit-message-text-generation-remote-execution.test.ts (95%) rename src/main/text-generation/{ => execution}/commit-message-text-generation-test-harness.ts (97%) rename src/main/text-generation/{ => execution}/source-control-agent-cli-path.test.ts (93%) rename src/main/text-generation/{ => execution}/source-control-agent-hook-isolation.test.ts (100%) rename src/main/text-generation/{ => execution}/source-control-agent-launch.test.ts (95%) rename src/main/text-generation/{ => execution}/source-control-agent-launch.ts (83%) rename src/main/text-generation/{ => execution}/source-control-agent-termination.ts (70%) rename src/main/text-generation/{ => execution}/source-control-command-line-budget.ts (80%) rename src/main/text-generation/{ => execution}/source-control-generation-lanes.ts (88%) rename src/main/text-generation/{ => execution}/source-control-generation-limits.ts (100%) rename src/main/text-generation/{ => execution}/source-control-local-generation.ts (93%) rename src/main/text-generation/{ => execution}/source-control-local-process.test.ts (94%) rename src/main/text-generation/{ => execution}/source-control-local-process.ts (92%) rename src/main/text-generation/{ => execution}/source-control-remote-generation.ts (86%) rename src/main/text-generation/{ => execution}/source-control-supervised-agent-spawn.ts (75%) create mode 100644 src/main/text-generation/execution/source-control-wsl-launch.test.ts rename src/main/text-generation/{ => failures}/agent-failure-output.test.ts (100%) rename src/main/text-generation/{ => failures}/agent-failure-output.ts (94%) rename src/main/text-generation/{ => failures}/antigravity-generation-stream.test.ts (94%) rename src/main/text-generation/{ => failures}/commit-message-text-generation-failure-sanitization.test.ts (98%) rename src/main/text-generation/{ => failures}/copilot-unavailable-model-retry.ts (86%) rename src/main/text-generation/{ => failures}/kimi-generation-failure.test.ts (96%) rename src/main/text-generation/{ => failures}/kimi-generation-failure.ts (96%) rename src/main/text-generation/{ => failures}/source-control-agent-failure.ts (91%) rename src/main/text-generation/{ => models}/codex-model-list-discovery.ts (88%) rename src/main/text-generation/{ => models}/commit-message-model-discovery-policy.ts (79%) rename src/main/text-generation/{ => models}/commit-message-model-discovery.ts (89%) rename src/main/text-generation/{ => models}/commit-message-text-generation-model-discovery.test.ts (88%) rename src/main/text-generation/{ => models}/model-discovery-host-cache.ts (84%) rename src/main/text-generation/{ => policies}/source-control-kimi-policy.ts (86%) rename src/main/text-generation/{ => policies}/source-control-policy-probe.ts (87%) rename src/main/text-generation/{ => policies}/source-control-read-only-policy.test.ts (95%) rename src/main/text-generation/{ => policies}/source-control-read-only-policy.ts (92%) rename src/main/text-generation/{ => prompts}/source-control-omp-prompt-file.test.ts (93%) rename src/main/text-generation/{ => prompts}/source-control-prompt-files.ts (95%) rename src/main/text-generation/{ => prompts}/source-control-prompt-producer.ts (100%) rename src/main/text-generation/{ => prompts}/source-control-wsl-prompt.test.ts (74%) delete mode 100644 src/main/text-generation/pull-request-context.test.ts rename src/main/text-generation/{ => pull-request-context}/pull-request-context.ts (94%) rename src/main/text-generation/{ => pull-request-context}/pull-request-remote-ref-probes.ts (94%) rename src/main/text-generation/{ => requests}/commit-message-text-generation-branch-name.test.ts (93%) rename src/main/text-generation/{ => requests}/commit-message-text-generation-generated-output.test.ts (94%) rename src/main/text-generation/{ => requests}/commit-message-text-generation-regression.test.ts (96%) rename src/main/text-generation/{ => requests}/conversation-name-generation-request.test.ts (88%) rename src/main/text-generation/{ => requests}/conversation-name-generation-request.ts (78%) rename src/main/text-generation/{ => requests}/dictation-cleanup-generation-request.ts (70%) rename src/main/text-generation/{ => requests}/jira-issue-summary-requests.test.ts (82%) rename src/main/text-generation/{ => requests}/jira-issue-summary-requests.ts (89%) rename src/main/text-generation/{ => requests}/jira-issue-summary-text-generation.ts (76%) rename src/main/text-generation/{ => requests}/source-control-text-generation-requests.ts (85%) delete mode 100644 src/main/text-generation/source-control-wsl-launch.test.ts delete mode 100644 src/shared/chat-naming-catalog-work.test-fixture.ts diff --git a/config/main-sync-fs-baseline.txt b/config/main-sync-fs-baseline.txt index 98994ee5617..4952f5ff34b 100644 --- a/config/main-sync-fs-baseline.txt +++ b/config/main-sync-fs-baseline.txt @@ -533,7 +533,7 @@ 3 src/main/startup/windows-startup/windows-user-data-acl.ts 6 src/main/startup/linux-display/x-display-artifacts.ts 2 src/main/stats/stats-file-loader.ts -2 src/main/text-generation/source-control-prompt-files.ts +2 src/main/text-generation/prompts/source-control-prompt-files.ts 1 src/main/updater/lifecycle/windows-update-install-directory.ts 9 src/main/updater/linux-package/appimage-runtime-identity.ts 1 src/main/updater/linux-package/linux-package-install-command.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 4c66f4ecdf9..33abcbd63e0 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -3913,28 +3913,27 @@ "src/main/pty/history/terminal-history-gc-fs-call-count.test.ts": 157, "src/main/pty/history/terminal-history-gc.test.ts": 824, "src/main/pty/history/terminal-history-tombstone-retry.test.ts": 715, - "src/main/text-generation/agent-failure-output.test.ts": 49, - "src/main/text-generation/antigravity-generation-stream.test.ts": 479, - "src/main/text-generation/command-environment-process.test.ts": 1044, - "src/main/text-generation/commit-message-agent-environment.test.ts": 95, - "src/main/text-generation/commit-message-text-generation-branch-name.test.ts": 496, - "src/main/text-generation/commit-message-text-generation-cancellation.test.ts": 1171, - "src/main/text-generation/commit-message-text-generation-failure-sanitization.test.ts": 466, - "src/main/text-generation/commit-message-text-generation-generated-output.test.ts": 521, - "src/main/text-generation/commit-message-text-generation-local-subprocess.test.ts": 480, - "src/main/text-generation/commit-message-text-generation-model-discovery.test.ts": 504, - "src/main/text-generation/commit-message-text-generation-regression.test.ts": 408, - "src/main/text-generation/commit-message-text-generation-remote-execution.test.ts": 469, - "src/main/text-generation/conversation-name-generation-request.test.ts": 507, - "src/main/text-generation/jira-issue-summary-requests.test.ts": 276, - "src/main/text-generation/kimi-generation-failure.test.ts": 151, - "src/main/text-generation/pull-request-context.test.ts": 97, - "src/main/text-generation/source-control-agent-cli-path.test.ts": 615, - "src/main/text-generation/source-control-agent-launch.test.ts": 1879, - "src/main/text-generation/source-control-local-process.test.ts": 1834, - "src/main/text-generation/source-control-omp-prompt-file.test.ts": 6676, - "src/main/text-generation/source-control-wsl-launch.test.ts": 482, - "src/main/text-generation/source-control-wsl-prompt.test.ts": 2440, + "src/main/text-generation/failures/agent-failure-output.test.ts": 49, + "src/main/text-generation/failures/antigravity-generation-stream.test.ts": 479, + "src/main/text-generation/execution/command-environment-process.test.ts": 1044, + "src/main/text-generation/execution/commit-message-agent-environment.test.ts": 95, + "src/main/text-generation/requests/commit-message-text-generation-branch-name.test.ts": 496, + "src/main/text-generation/execution/commit-message-text-generation-cancellation.test.ts": 1171, + "src/main/text-generation/failures/commit-message-text-generation-failure-sanitization.test.ts": 466, + "src/main/text-generation/requests/commit-message-text-generation-generated-output.test.ts": 521, + "src/main/text-generation/execution/commit-message-text-generation-local-subprocess.test.ts": 480, + "src/main/text-generation/models/commit-message-text-generation-model-discovery.test.ts": 504, + "src/main/text-generation/requests/commit-message-text-generation-regression.test.ts": 408, + "src/main/text-generation/execution/commit-message-text-generation-remote-execution.test.ts": 469, + "src/main/text-generation/requests/conversation-name-generation-request.test.ts": 507, + "src/main/text-generation/requests/jira-issue-summary-requests.test.ts": 276, + "src/main/text-generation/failures/kimi-generation-failure.test.ts": 151, + "src/main/text-generation/execution/source-control-agent-cli-path.test.ts": 615, + "src/main/text-generation/execution/source-control-agent-launch.test.ts": 1879, + "src/main/text-generation/execution/source-control-local-process.test.ts": 1834, + "src/main/text-generation/prompts/source-control-omp-prompt-file.test.ts": 6676, + "src/main/text-generation/execution/source-control-wsl-launch.test.ts": 482, + "src/main/text-generation/prompts/source-control-wsl-prompt.test.ts": 2440, "src/main/tray/system-tray.test.ts": 556, "src/main/tray/tray-dev-badge.test.ts": 51, "src/main/updater/updater-events.test.ts": 280, diff --git a/src/main/acp/text-task/acp-text-task.ts b/src/main/acp/text-task/acp-text-task.ts index a2ad61e3bb9..aff89f861de 100644 --- a/src/main/acp/text-task/acp-text-task.ts +++ b/src/main/acp/text-task/acp-text-task.ts @@ -7,7 +7,7 @@ import { waitForPromiseWithSignal } from '../../../shared/abort-signal-reason' import type { StructuredAgentSessionAdapter } from '../../native-chat/agent-session-wire/providers/structured-agent-session-adapter' import { ProviderAcquisitionStarts } from '../../provider-process/provider-acquisition-starts' import { removeAntigravitySignInSessionFiles } from '../../antigravity/antigravity-sign-in-session-files' -import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from '../../text-generation/source-control-generation-limits' +import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from '../../text-generation/execution/source-control-generation-limits' import { resolveAcpLaunchInvocation } from '../launch/acp-structured-launch-resolution' import { acpLaunchSpecForAccount } from '../launch/acp-launch-command' import { prepareAcpLaunch } from '../launch/acp-structured-launch-preparation' diff --git a/src/main/agent-hooks/first-work/branch-rename-failure-output.ts b/src/main/agent-hooks/first-work/branch-rename-failure-output.ts index 6bf1648d4e8..f779cf5cc43 100644 --- a/src/main/agent-hooks/first-work/branch-rename-failure-output.ts +++ b/src/main/agent-hooks/first-work/branch-rename-failure-output.ts @@ -1,7 +1,7 @@ import { formatAgentGenerationFailureOutputForDisplay, type AgentGenerationFailureOutput -} from '../../text-generation/agent-failure-output' +} from '../../text-generation/failures/agent-failure-output' // Keep full CLI diagnostics local and ephemeral; persist only the sanitized failure badge. const MAX_ENTRIES = 32 diff --git a/src/main/agent-hooks/first-work/first-work-branch-rename.ts b/src/main/agent-hooks/first-work/first-work-branch-rename.ts index cf0314f20e9..efaaf640f9b 100644 --- a/src/main/agent-hooks/first-work/first-work-branch-rename.ts +++ b/src/main/agent-hooks/first-work/first-work-branch-rename.ts @@ -24,10 +24,10 @@ import { } from '../../git/worktrees/checkout/branch-rename' import { generateBranchNameFromContext, - resolveTextGenerationParams + resolveCommitMessageSettings as resolveTextGenerationParams } from '../../text-generation/commit-message-text-generation' -import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/commit-message-agent-environment' -import type { AgentGenerationFailureOutput } from '../../text-generation/agent-failure-output' +import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/execution/commit-message-agent-environment' +import type { AgentGenerationFailureOutput } from '../../text-generation/failures/agent-failure-output' import { resolveGenerationTarget } from './first-work-generation-target' import { runFolderWorkspaceTitleAutoRename } from './first-work-workspace-title-rename' import { isWslMoveRepoBlocked } from '../../persistence/moving-wsl-projects/orchestration/wsl-move-background-backoff' diff --git a/src/main/agent-hooks/first-work/first-work-generation-backoff.ts b/src/main/agent-hooks/first-work/first-work-generation-backoff.ts index 76074de126c..9d6616e8b6b 100644 --- a/src/main/agent-hooks/first-work/first-work-generation-backoff.ts +++ b/src/main/agent-hooks/first-work/first-work-generation-backoff.ts @@ -1,4 +1,4 @@ -import type { AgentGenerationFailureOutput } from '../../text-generation/agent-failure-output' +import type { AgentGenerationFailureOutput } from '../../text-generation/failures/agent-failure-output' // Usage-limit failures spawned hundreds of sessions on repeated working events (#9705); back off until recovery. export const FIRST_WORK_GENERATION_RETRY_DELAYS_MS: readonly number[] = [30_000, 120_000, 1_800_000] diff --git a/src/main/agent-hooks/first-work/first-work-generation-target.ts b/src/main/agent-hooks/first-work/first-work-generation-target.ts index a7cd93095dd..0df5fbafe84 100644 --- a/src/main/agent-hooks/first-work/first-work-generation-target.ts +++ b/src/main/agent-hooks/first-work/first-work-generation-target.ts @@ -1,8 +1,8 @@ -import type { CommitMessageGenerationTarget } from '../../text-generation/commit-message-text-generation' +import type { CommitMessageGenerationTarget } from '../../text-generation/source-control-text-generation-types' import { prepareLocalCommitMessageAgentEnv, type CommitMessageAgentEnvironmentResolvers -} from '../../text-generation/commit-message-agent-environment' +} from '../../text-generation/execution/commit-message-agent-environment' export async function resolveGenerationTarget( worktreePath: string, diff --git a/src/main/agent-hooks/first-work/first-work-workspace-title-rename.ts b/src/main/agent-hooks/first-work/first-work-workspace-title-rename.ts index d7d98c4a678..355cc57d039 100644 --- a/src/main/agent-hooks/first-work/first-work-workspace-title-rename.ts +++ b/src/main/agent-hooks/first-work/first-work-workspace-title-rename.ts @@ -1,7 +1,7 @@ import { humanizeBranchSlug } from '../../../shared/source-control/ai/naming/branch-name-from-work' import { generateBranchNameFromContext, - resolveTextGenerationParams + resolveCommitMessageSettings as resolveTextGenerationParams } from '../../text-generation/commit-message-text-generation' import { resolveGenerationTarget } from './first-work-generation-target' import type { FirstWorkBranchRenameDeps } from './first-work-branch-rename' diff --git a/src/main/grok/grok-text-task.ts b/src/main/grok/grok-text-task.ts index 3f497293a64..7d953b44d66 100644 --- a/src/main/grok/grok-text-task.ts +++ b/src/main/grok/grok-text-task.ts @@ -3,9 +3,9 @@ import { resolveProviderChildEnv, type ProviderProcessLaunch } from '../provider-process/provider-process-launch' -import { runLocalSourceControlPlan } from '../text-generation/source-control-local-process' -import { spawnSourceControlAgent } from '../text-generation/source-control-agent-launch' -import { cancelLocalGeneration } from '../text-generation/source-control-generation-lanes' +import { runLocalSourceControlPlan } from '../text-generation/execution/source-control-local-process' +import { spawnSourceControlAgent } from '../text-generation/execution/source-control-agent-launch' +import { cancelLocalGeneration } from '../text-generation/execution/source-control-generation-lanes' export const GROK_TEXT_TASK_ENV = { GROK_STORAGE_MODE: 'local' } as const diff --git a/src/main/ipc/filesystem/filesystem-git-commit-generation-handlers.ts b/src/main/ipc/filesystem/filesystem-git-commit-generation-handlers.ts index f327e81233b..fba3d8d737b 100644 --- a/src/main/ipc/filesystem/filesystem-git-commit-generation-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-commit-generation-handlers.ts @@ -8,9 +8,9 @@ import type { ResolvedSourceControlAiGenerationParams } from '../../../shared/so import { cancelGenerateCommitMessageLocal, generateCommitMessageFromContext, - resolveCommitMessageSettings, - type GenerateCommitMessageResult + resolveCommitMessageSettings } from '../../text-generation/commit-message-text-generation' +import type { GenerateCommitMessageResult } from '../../text-generation/source-control-text-generation-types' import { getCommitMessageModelDiscoveryHostKey } from '../../../shared/source-control/ai/generation/commit-message-host-key' import { getStagedCommitContext } from '../../git/source-control/mutations/staged-commit-context' import { SSH_HOST_NOT_SERVED_MESSAGE } from '../../providers/ssh-host-not-served-error' @@ -18,7 +18,7 @@ import { resolveRegisteredWorktreePath } from '../worktrees/removal/registered-w import { getLocalGitOptionsForRegisteredWorktree } from '../local-worktree-runtime-options' import { withLinkedIssueDraftContext } from '../../../shared/source-control/ai/recipes/source-control-ai-action-variables' import { resolveSourceControlAiLinkedIssue } from '../source-control/source-control-ai-linked-issue' -import { prepareLocalCommitMessageAgentEnv } from '../../text-generation/commit-message-agent-environment' +import { prepareLocalCommitMessageAgentEnv } from '../../text-generation/execution/commit-message-agent-environment' import type { FilesystemHandlerContext } from './filesystem-handler-context' import { getLocalAgentRuntimeTarget, diff --git a/src/main/ipc/filesystem/filesystem-git-model-discovery-handlers.ts b/src/main/ipc/filesystem/filesystem-git-model-discovery-handlers.ts index 03ea1c0ebff..a9e0c655b08 100644 --- a/src/main/ipc/filesystem/filesystem-git-model-discovery-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-model-discovery-handlers.ts @@ -1,11 +1,9 @@ import { ipcMain } from 'electron' import type { TuiAgent } from '../../../shared/agent-launch/catalog/tui-agent' -import type { CommitMessageAgentRuntimeTarget } from '../../text-generation/commit-message-agent-environment' -import { - discoverCommitMessageModelsLocal, - type DiscoverCommitMessageModelsResult -} from '../../text-generation/commit-message-text-generation' -import { prepareLocalCommitMessageAgentEnv } from '../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentRuntimeTarget } from '../../text-generation/execution/commit-message-agent-environment' +import { discoverCommitMessageModelsLocal } from '../../text-generation/commit-message-text-generation' +import type { DiscoverCommitMessageModelsResult } from '../../text-generation/source-control-text-generation-types' +import { prepareLocalCommitMessageAgentEnv } from '../../text-generation/execution/commit-message-agent-environment' import { parseWslPath } from '../../wsl/filesystem/unc-path-access' import { resolveModelDiscoveryLocalPath, diff --git a/src/main/ipc/filesystem/filesystem-git-pull-request-generation-handlers.ts b/src/main/ipc/filesystem/filesystem-git-pull-request-generation-handlers.ts index 1b0f45c9732..be2c6ea08ae 100644 --- a/src/main/ipc/filesystem/filesystem-git-pull-request-generation-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-pull-request-generation-handlers.ts @@ -13,8 +13,8 @@ import { type GeneratePullRequestFieldsResult } from '../../text-generation/commit-message-text-generation' import { getCommitMessageModelDiscoveryHostKey } from '../../../shared/source-control/ai/generation/commit-message-host-key' -import { getPullRequestDraftContext } from '../../text-generation/pull-request-context' -import { prepareLocalCommitMessageAgentEnv } from '../../text-generation/commit-message-agent-environment' +import { getPullRequestDraftContext } from '../../text-generation/pull-request-context/pull-request-context' +import { prepareLocalCommitMessageAgentEnv } from '../../text-generation/execution/commit-message-agent-environment' import { SSH_HOST_NOT_SERVED_MESSAGE } from '../../providers/ssh-host-not-served-error' import { resolveRegisteredWorktreePath } from '../worktrees/removal/registered-worktree-roots-cache' import { getLocalGitOptionsForRegisteredWorktree } from '../local-worktree-runtime-options' diff --git a/src/main/ipc/filesystem/filesystem-git-status-staging.test.ts b/src/main/ipc/filesystem/filesystem-git-status-staging.test.ts index 33fb1a301a5..0e319889d32 100644 --- a/src/main/ipc/filesystem/filesystem-git-status-staging.test.ts +++ b/src/main/ipc/filesystem/filesystem-git-status-staging.test.ts @@ -101,7 +101,7 @@ vi.mock( async () => (await import('./filesystem-test-harness')).textGenerationModuleMock ) vi.mock( - '../../text-generation/pull-request-context', + '../../text-generation/pull-request-context/pull-request-context', async () => (await import('./filesystem-test-harness')).pullRequestContextMock ) vi.mock( diff --git a/src/main/ipc/filesystem/filesystem-handler-context.ts b/src/main/ipc/filesystem/filesystem-handler-context.ts index 025fbc2d6ad..6c0b3b8b145 100644 --- a/src/main/ipc/filesystem/filesystem-handler-context.ts +++ b/src/main/ipc/filesystem/filesystem-handler-context.ts @@ -1,7 +1,7 @@ import type { ChildProcess } from 'node:child_process' import type { FileHandle } from 'node:fs/promises' import type { Store } from '../../persistence/loading-store/store' -import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/execution/commit-message-agent-environment' import type { SenderScopedRequestCancellations } from '../sender-scoped-request-cancellation' import { cleanupLocalTransferPath } from './filesystem-download-promotion' diff --git a/src/main/ipc/filesystem/filesystem-pull-request-linked-issue-lifetime.test.ts b/src/main/ipc/filesystem/filesystem-pull-request-linked-issue-lifetime.test.ts index 86c73a66ca9..6b2734c80ba 100644 --- a/src/main/ipc/filesystem/filesystem-pull-request-linked-issue-lifetime.test.ts +++ b/src/main/ipc/filesystem/filesystem-pull-request-linked-issue-lifetime.test.ts @@ -103,7 +103,7 @@ vi.mock( async () => (await import('./filesystem-test-harness')).textGenerationModuleMock ) vi.mock( - '../../text-generation/pull-request-context', + '../../text-generation/pull-request-context/pull-request-context', async () => (await import('./filesystem-test-harness')).pullRequestContextMock ) vi.mock( diff --git a/src/main/ipc/filesystem/filesystem-source-control-ai-targets.ts b/src/main/ipc/filesystem/filesystem-source-control-ai-targets.ts index c6989cd5318..ce081af348a 100644 --- a/src/main/ipc/filesystem/filesystem-source-control-ai-targets.ts +++ b/src/main/ipc/filesystem/filesystem-source-control-ai-targets.ts @@ -4,8 +4,8 @@ import { getLocalProjectWorktreeGitOptions, type LocalProjectWorktreeGitOptions } from '../../workspaces/execution-host/project-runtime-git-options' -import type { CommitMessageAgentRuntimeTarget } from '../../text-generation/commit-message-agent-environment' -import type { CommitMessageGenerationTarget } from '../../text-generation/commit-message-text-generation' +import type { CommitMessageAgentRuntimeTarget } from '../../text-generation/execution/commit-message-agent-environment' +import type { CommitMessageGenerationTarget } from '../../text-generation/source-control-text-generation-types' import { resolve } from 'node:path' import { listRepoWorktreeGraph } from '../../workspaces/catalog/repo-worktrees' import { resolveAuthorizedPath } from './access/filesystem-auth' diff --git a/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-git.test.ts b/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-git.test.ts index d1116168f01..8849a669fae 100644 --- a/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-git.test.ts +++ b/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-git.test.ts @@ -113,7 +113,7 @@ vi.mock( async () => (await import('./filesystem-test-harness')).textGenerationModuleMock ) vi.mock( - '../../text-generation/pull-request-context', + '../../text-generation/pull-request-context/pull-request-context', async () => (await import('./filesystem-test-harness')).pullRequestContextMock ) vi.mock( diff --git a/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-reads.test.ts b/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-reads.test.ts index bb72b4352bb..3ff31e0fbc1 100644 --- a/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-reads.test.ts +++ b/src/main/ipc/filesystem/filesystem-wsl-stopped-distro-reads.test.ts @@ -109,7 +109,7 @@ vi.mock( async () => (await import('./filesystem-test-harness')).textGenerationModuleMock ) vi.mock( - '../../text-generation/pull-request-context', + '../../text-generation/pull-request-context/pull-request-context', async () => (await import('./filesystem-test-harness')).pullRequestContextMock ) vi.mock( diff --git a/src/main/ipc/filesystem/filesystem.test.ts b/src/main/ipc/filesystem/filesystem.test.ts index a39a0764766..159c295902e 100644 --- a/src/main/ipc/filesystem/filesystem.test.ts +++ b/src/main/ipc/filesystem/filesystem.test.ts @@ -107,7 +107,7 @@ vi.mock( async () => (await import('./filesystem-test-harness')).textGenerationModuleMock ) vi.mock( - '../../text-generation/pull-request-context', + '../../text-generation/pull-request-context/pull-request-context', async () => (await import('./filesystem-test-harness')).pullRequestContextMock ) vi.mock( diff --git a/src/main/ipc/filesystem/filesystem.ts b/src/main/ipc/filesystem/filesystem.ts index 014f16a429a..742810fe10e 100644 --- a/src/main/ipc/filesystem/filesystem.ts +++ b/src/main/ipc/filesystem/filesystem.ts @@ -1,5 +1,5 @@ import type { Store } from '../../persistence/loading-store/store' -import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/execution/commit-message-agent-environment' import { registerLocalLogTailHandlers } from '../local-log-tail' import { createSenderScopedRequestCancellations } from '../sender-scoped-request-cancellation' import { diff --git a/src/main/ipc/jira/issue-summary-generation.ts b/src/main/ipc/jira/issue-summary-generation.ts index 07d1df0a864..85b1d907eb5 100644 --- a/src/main/ipc/jira/issue-summary-generation.ts +++ b/src/main/ipc/jira/issue-summary-generation.ts @@ -4,11 +4,11 @@ import { CancelGenerateIssueSummary } from '../../../shared/rpc-contract/jira-params' import type { Store } from '../../persistence/loading-store/store' -import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/execution/commit-message-agent-environment' import { cancelJiraIssueSummaryRequest, generateJiraIssueSummaryRequest -} from '../../text-generation/jira-issue-summary-requests' +} from '../../text-generation/requests/jira-issue-summary-requests' import { addFanoutListener } from '../../runtime/events/event-listener-fanout' export function registerJiraIssueSummaryGenerationHandlers( diff --git a/src/main/ipc/register-core-handlers/register-core-handlers.ts b/src/main/ipc/register-core-handlers/register-core-handlers.ts index 7b308ca9c09..d03934f8ea6 100644 --- a/src/main/ipc/register-core-handlers/register-core-handlers.ts +++ b/src/main/ipc/register-core-handlers/register-core-handlers.ts @@ -10,7 +10,7 @@ import type { Store } from '../../persistence/loading-store/store' import type { OrcaRuntimeService } from '../../runtime/orca-runtime' import type { StatsCollector } from '../../stats/collector' import { registerFilesystemHandlers } from '../filesystem/filesystem' -import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/execution/commit-message-agent-environment' import { registerFilesystemWatcherHandlers } from '../filesystem/watchers/filesystem-watcher-handlers' import { registerGitHubHandlers } from '../source-control/github' import { registerGitLabHandlers } from '../source-control/gitlab' diff --git a/src/main/ipc/speech.ts b/src/main/ipc/speech.ts index 102a758c7a4..284a731a74a 100644 --- a/src/main/ipc/speech.ts +++ b/src/main/ipc/speech.ts @@ -28,7 +28,7 @@ import type { DictationContext, OpenAiCompatibleSpeechProvider } from '../../sha import type { Store } from '../persistence/loading-store/store' import type { DictationCleanupResult } from '../../shared/dictation-cleanup' import { cancelDictationCleanup, cleanUpDictatedText } from '../speech/dictation-cleanup-service' -import type { CommitMessageAgentEnvironmentResolvers } from '../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentEnvironmentResolvers } from '../text-generation/execution/commit-message-agent-environment' // Ten minutes of fast speech is under 20,000 characters; anything far past it isn't dictation. const MAX_CLEANUP_TEXT_LENGTH = 100_000 diff --git a/src/main/native-chat/conversation-naming/structured-chat-naming-types.ts b/src/main/native-chat/conversation-naming/structured-chat-naming-types.ts index c50f67ab337..f476ff55552 100644 --- a/src/main/native-chat/conversation-naming/structured-chat-naming-types.ts +++ b/src/main/native-chat/conversation-naming/structured-chat-naming-types.ts @@ -1,7 +1,7 @@ import type { AgentSessionRecord } from '../../../shared/agent-session/lifecycle/agent-session-record' import type { GlobalSettings } from '../../../shared/global-settings-types' import type { AgentSessionRecordStore } from '../../runtime/agent-session/records/agent-session-record-store' -import type { AgentGenerationFailureOutput } from '../../text-generation/agent-failure-output' +import type { AgentGenerationFailureOutput } from '../../text-generation/failures/agent-failure-output' import type { StructuredAgentSessionLogger } from '../agent-session-wire/host/structured-agent-session-logger' import type { StructuredAgentSessionAdapter } from '../agent-session-wire/providers/structured-agent-session-adapter' import type { AgentSessionJournal } from '../agent-session-journal/journal-store' diff --git a/src/main/native-chat/structured-chat-naming.ts b/src/main/native-chat/structured-chat-naming.ts index 3a44c2fe1b5..f4bb04594d9 100644 --- a/src/main/native-chat/structured-chat-naming.ts +++ b/src/main/native-chat/structured-chat-naming.ts @@ -23,7 +23,7 @@ import { sliceAtCodeUnitLimit } from '../../shared/surrogate-safe-text-slice' import { formatAgentGenerationFailureOutputForDisplay, type AgentGenerationFailureOutput -} from '../text-generation/agent-failure-output' +} from '../text-generation/failures/agent-failure-output' import type { StructuredAgentSessionConversationNaming, StructuredChatNamingFailure diff --git a/src/main/opencode/text-tasks/opencode-text-task-events.ts b/src/main/opencode/text-tasks/opencode-text-task-events.ts index b0db40e4d25..79550f67e4a 100644 --- a/src/main/opencode/text-tasks/opencode-text-task-events.ts +++ b/src/main/opencode/text-tasks/opencode-text-task-events.ts @@ -1,6 +1,6 @@ import { z } from 'zod' import { SearchSubprocessLineAccumulator } from '../../../shared/search-subprocess-lines' -import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES } from '../../text-generation/source-control-generation-limits' +import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES } from '../../text-generation/execution/source-control-generation-limits' import type { createOpenCodeTextTaskHttp } from './opencode-text-task-http' import { openCodeTextTaskUrl } from './opencode-text-task-http' diff --git a/src/main/opencode/text-tasks/opencode-text-task-http.ts b/src/main/opencode/text-tasks/opencode-text-task-http.ts index 8ec5fd55fb6..38436bf0964 100644 --- a/src/main/opencode/text-tasks/opencode-text-task-http.ts +++ b/src/main/opencode/text-tasks/opencode-text-task-http.ts @@ -1,5 +1,5 @@ import { z } from 'zod' -import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES } from '../../text-generation/source-control-generation-limits' +import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES } from '../../text-generation/execution/source-control-generation-limits' const envelope = z.looseObject({ data: z.unknown().optional() }) diff --git a/src/main/opencode/text-tasks/opencode-text-task-server-ready.ts b/src/main/opencode/text-tasks/opencode-text-task-server-ready.ts index 3814a4a9d32..2a07ca2b6d2 100644 --- a/src/main/opencode/text-tasks/opencode-text-task-server-ready.ts +++ b/src/main/opencode/text-tasks/opencode-text-task-server-ready.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import { SearchSubprocessLineAccumulator } from '../../../shared/search-subprocess-lines' import { waitForPromiseWithSignal } from '../../../shared/abort-signal-reason' -import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES } from '../../text-generation/source-control-generation-limits' +import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES } from '../../text-generation/execution/source-control-generation-limits' import type { SpawnedSourceControlAgentProcess } from '../../text-generation/source-control-text-generation-types' const lease = z.object({ url: z.url() }) diff --git a/src/main/opencode/text-tasks/opencode-text-task.ts b/src/main/opencode/text-tasks/opencode-text-task.ts index d56a8ebe0b4..c7f970e4114 100644 --- a/src/main/opencode/text-tasks/opencode-text-task.ts +++ b/src/main/opencode/text-tasks/opencode-text-task.ts @@ -1,18 +1,18 @@ import { mergeCommandEnvironment } from '../../../shared/command-environment' import { textGenerationFailure } from '../../../shared/text-generation-failure-copy' -import { cleanAgentAnswer } from '../../text-generation/source-control-agent-failure' +import { cleanAgentAnswer } from '../../text-generation/failures/source-control-agent-failure' import { wslDistroForCommand } from '../../../shared/wsl-paths' import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' import { OpenCodeTextTaskError, runOwnedOpenCodeTextTask } from './opencode-owned-text-task' import { runOpenCodeWslTextTask } from './wsl/opencode-wsl-text-task' -import { killSourceControlAgentProcess } from '../../text-generation/source-control-agent-termination' +import { killSourceControlAgentProcess } from '../../text-generation/execution/source-control-agent-termination' import { clearLocalGenerationCancelToken, localGenerationLaneKey, setLocalGenerationCancelToken -} from '../../text-generation/source-control-generation-lanes' +} from '../../text-generation/execution/source-control-generation-lanes' import { processEnvWithoutAgentSession } from '../../../shared/agent-launch/startup/parent-agent-session-env' -import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from '../../text-generation/source-control-generation-limits' +import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from '../../text-generation/execution/source-control-generation-limits' import type { InternalTextGenerationResult, LocalGenerationTarget, diff --git a/src/main/opencode/text-tasks/wsl/opencode-wsl-text-task-entry.ts b/src/main/opencode/text-tasks/wsl/opencode-wsl-text-task-entry.ts index 7938efaadde..3b8a7bf7f3f 100644 --- a/src/main/opencode/text-tasks/wsl/opencode-wsl-text-task-entry.ts +++ b/src/main/opencode/text-tasks/wsl/opencode-wsl-text-task-entry.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import { SearchSubprocessLineAccumulator } from '../../../../shared/search-subprocess-lines' -import { killSourceControlAgentProcess } from '../../../text-generation/source-control-agent-termination' -import { spawnSupervisedAgent } from '../../../text-generation/source-control-supervised-agent-spawn' +import { killSourceControlAgentProcess } from '../../../text-generation/execution/source-control-agent-termination' +import { spawnSupervisedAgent } from '../../../text-generation/execution/source-control-supervised-agent-spawn' import { OPENCODE_WSL_TEXT_TASK_CLEANUP_MS, OPENCODE_WSL_TEXT_TASK_REQUEST_MAX_BYTES, diff --git a/src/main/opencode/text-tasks/wsl/opencode-wsl-text-task.ts b/src/main/opencode/text-tasks/wsl/opencode-wsl-text-task.ts index f04f11f207b..28190433ae4 100644 --- a/src/main/opencode/text-tasks/wsl/opencode-wsl-text-task.ts +++ b/src/main/opencode/text-tasks/wsl/opencode-wsl-text-task.ts @@ -10,9 +10,9 @@ import { ensureWslPinnedRuntime } from '../../../wsl/execution/wsl-pinned-runtim import { runWslProcess, type WslSpec } from '../../../wsl/execution/wsl-runner' import { describeWslProcessFailure } from '../../../wsl/execution/wsl-process-failure' import { translateArgsForWsl } from '../../../git/command-runner/host-routing/wsl-path-translation' -import { spawnSourceControlAgent } from '../../../text-generation/source-control-agent-launch' -import { killSourceControlAgentProcess } from '../../../text-generation/source-control-agent-termination' -import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES } from '../../../text-generation/source-control-generation-limits' +import { spawnSourceControlAgent } from '../../../text-generation/execution/source-control-agent-launch' +import { killSourceControlAgentProcess } from '../../../text-generation/execution/source-control-agent-termination' +import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES } from '../../../text-generation/execution/source-control-generation-limits' import { waitForProcessExitUntil } from '../../../provider-process/provider-process-exit-deadline' import type { TextGenerationOperation, diff --git a/src/main/runtime/agent-session/environment/host-account-launch-env-state.ts b/src/main/runtime/agent-session/environment/host-account-launch-env-state.ts index fb4ce68d467..370212121c2 100644 --- a/src/main/runtime/agent-session/environment/host-account-launch-env-state.ts +++ b/src/main/runtime/agent-session/environment/host-account-launch-env-state.ts @@ -1,6 +1,6 @@ import { retireAtRestCommandContext } from '../../../native-chat/agent-session-wire/options/at-rest-command-context' import { agentModelCatalogStore } from '../../../native-chat/agent-model-catalog/agent-model-catalog-store' -import type { CommitMessageAgentEnvironmentResolvers } from '../../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentEnvironmentResolvers } from '../../../text-generation/execution/commit-message-agent-environment' import { hostAccountLaunchEnvFor, type HostAccountLaunchEnv diff --git a/src/main/runtime/agent-session/environment/runtime-account-controller.ts b/src/main/runtime/agent-session/environment/runtime-account-controller.ts index 477dda58cd7..ecdae1f811a 100644 --- a/src/main/runtime/agent-session/environment/runtime-account-controller.ts +++ b/src/main/runtime/agent-session/environment/runtime-account-controller.ts @@ -23,7 +23,7 @@ import type { RateLimitState } from '../../../../shared/rate-limit-types' import type { CodexResetCreditExpectedScope } from '../../../../shared/codex-reset-credit-scope' -import type { CommitMessageAgentEnvironmentResolvers } from '../../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentEnvironmentResolvers } from '../../../text-generation/execution/commit-message-agent-environment' import { getClaudeSelectionTargetForAccount, getSelectedClaudeAccountIdForTarget, diff --git a/src/main/runtime/agent-session/naming/structured-chat-naming-contract.test.ts b/src/main/runtime/agent-session/naming/structured-chat-naming-contract.test.ts deleted file mode 100644 index 42e5379c62b..00000000000 --- a/src/main/runtime/agent-session/naming/structured-chat-naming-contract.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { getDefaultSettings } from '../../../../shared/constants' -import { agentSessionRecordFixture } from '../../../../shared/agent-session/lifecycle/agent-session-record.test-fixture' - -import type { ExecutionHostId } from '../../../../shared/execution-host' -import { savedChatNamingCatalog } from '../../../../shared/chat-naming-catalog-work.test-fixture' -import type { SpawnSourceControlAgent } from '../../../text-generation/source-control-text-generation-types' - -import { structuredChatNamingDeps } from './structured-chat-naming-runtime' -import type { StructuredAgentSessionAdapter } from '../../../native-chat/agent-session-wire/providers/structured-agent-session-adapter' - -import type * as OwnedOpenCodeTask from '../../../opencode/text-tasks/opencode-owned-text-task' - -const boundary = vi.hoisted(() => ({ - spawn: vi.fn(), - native: vi.fn>(), - openCode: vi.fn( - async (_input: { cwd: string; env?: NodeJS.ProcessEnv; prompt: string }) => 'Owned title' - ), - account: vi.fn((_provider: string, id: string) => ({ - XDG_DATA_HOME: `/profiles/${id}/data`, - XDG_STATE_HOME: `/profiles/${id}/state` - })) -})) -vi.mock('../../../opencode/text-tasks/opencode-owned-text-task', async (importOriginal) => ({ - ...(await importOriginal()), - runOwnedOpenCodeTextTask: boundary.openCode -})) -vi.mock('../../../text-generation/source-control-agent-launch', () => ({ - spawnSourceControlAgent: boundary.spawn -})) -vi.mock('../../../native-chat/agent-session-wire/host/structured-agent-session-registry', () => ({ - getStructuredAgentSessionHost: () => ({ deps: { adapter: { generateText: boundary.native } } }) -})) -vi.mock('../../../managed-data-accounts/service', () => ({ - getManagedDataAccountService: () => ({ - restoreOriginalEnvironment: () => {}, - environmentForAccount: boundary.account - }) -})) -beforeEach(() => { - vi.clearAllMocks() - boundary.spawn.mockReset() - boundary.native.mockReset() - boundary.openCode.mockClear() - vi.stubEnv('ORCA_PI_SOURCE_AGENT_DIR', undefined) -}) -afterEach(() => { - vi.restoreAllMocks() - vi.unstubAllEnvs() -}) - -function rig() { - const settings = getDefaultSettings('/isolated') - settings.defaultTuiAgent = 'codex' - settings.sourceControlAi = savedChatNamingCatalog(0) - const env = { - PATH: '/usr/bin', - PI_CODING_AGENT_DIR: '/configured/pi', - OPENCODE_CONFIG_DIR: '/configured/oc', - OPENCODE_AUTH_CONTENT: 'current-account-sentinel' - } - const runtime = { - resolveWorkspace: vi.fn( - async (): Promise<{ path: string; executionHostId: ExecutionHostId }> => ({ - path: '/workspace/folder', - executionHostId: 'local' - }) - ), - resolveNamingEnvironment: vi.fn(async () => env), - getAgentEnvResolvers: () => ({ resolveBaseEnvironment: async () => env }), - workerTaskTitle: () => null, - retitleOpenTab: () => {} - } - return { - settings, - runtime, - deps: structuredChatNamingDeps(() => ({ getSettings: () => settings }), runtime, { - warn: vi.fn(), - error: vi.fn() - }) - } -} - -it.each(['opencode', 'opencode2'] as const)( - 'routes %s naming into the owned task with its selected account', - async (provider) => { - const state = rig() - const record = agentSessionRecordFixture() - record.provider = provider - record.accountHome = { - kind: 'opencode', - locator: { kind: 'managed', managedProfileId: 'recorded' } - } - expect(await state.deps.generate(record, 'Repair login')).toEqual({ name: 'Owned title' }) - expect(boundary.openCode).toHaveBeenCalledWith( - expect.objectContaining({ - cwd: '/workspace/folder', - prompt: expect.stringContaining('Repair login'), - env: expect.objectContaining({ - XDG_DATA_HOME: '/profiles/recorded/data', - XDG_STATE_HOME: '/profiles/recorded/state', - OPENCODE_AUTH_CONTENT: undefined - }) - }) - ) - expect(boundary.spawn).not.toHaveBeenCalled() - expect(boundary.account).toHaveBeenCalledWith('opencode', 'recorded') - } -) diff --git a/src/main/runtime/agent-session/naming/structured-chat-naming-runtime.ts b/src/main/runtime/agent-session/naming/structured-chat-naming-runtime.ts index 7c83f6b87b7..514828410a5 100644 --- a/src/main/runtime/agent-session/naming/structured-chat-naming-runtime.ts +++ b/src/main/runtime/agent-session/naming/structured-chat-naming-runtime.ts @@ -15,7 +15,7 @@ import type { AgentLaunchProfileSettings } from '../../../../shared/agent-launch import { chatNamingFailure } from '../../../../shared/chat-naming-failure-copy' import { agentSessionCurrentContextRows } from '../../../../shared/agent-session/agent-session-context-clear' import { LOCAL_EXECUTION_HOST_ID, type ExecutionHostId } from '../../../../shared/execution-host' -import type { CommitMessageAgentEnvironmentResolvers } from '../../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentEnvironmentResolvers } from '../../../text-generation/execution/commit-message-agent-environment' import type { StructuredChatNamingDeps } from '../../../native-chat/conversation-naming/structured-chat-naming-types' import type { StructuredAgentSessionLogger } from '../../../native-chat/agent-session-wire/host/structured-agent-session-logger' import { getStructuredAgentSessionHost } from '../../../native-chat/agent-session-wire/host/structured-agent-session-registry' @@ -24,7 +24,7 @@ import { firstStructuredAgentSessionPrompt } from '../../../../shared/agent-sess import { LOCAL_COMMIT_MESSAGE_HOST_KEY } from '../../../../shared/source-control/ai/generation/commit-message-host-key' import { generateConversationNameFromContext, - resolveTextGenerationParams + resolveCommitMessageSettings as resolveTextGenerationParams } from '../../../text-generation/commit-message-text-generation' import { resolveGenerationTarget } from '../../../agent-hooks/first-work/first-work-generation-target' import { deferredStructuredChatTitle } from '../../../native-chat/conversation-naming/structured-chat-title-handover' diff --git a/src/main/runtime/integrations/git/runtime-git-command-target.ts b/src/main/runtime/integrations/git/runtime-git-command-target.ts index 3468c107f35..fb0f30e8116 100644 --- a/src/main/runtime/integrations/git/runtime-git-command-target.ts +++ b/src/main/runtime/integrations/git/runtime-git-command-target.ts @@ -13,7 +13,7 @@ import type { LocalGitProvider } from '../../../providers/git/local-provider' import { SshHostNotServedError } from '../../../providers/ssh-host-not-served-error' -import type { CommitMessageAgentEnvironmentResolvers } from '../../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentEnvironmentResolvers } from '../../../text-generation/execution/commit-message-agent-environment' import type { PullRequestLinkedIssueMeta } from '../../../source-control/pull-request-linked-issue' import { normalizeRuntimeRelativePath } from '../../files/paths/runtime-relative-paths' diff --git a/src/main/runtime/integrations/git/runtime-git-generation-admission.test.ts b/src/main/runtime/integrations/git/runtime-git-generation-admission.test.ts index a04459eb4b2..1a459683ebb 100644 --- a/src/main/runtime/integrations/git/runtime-git-generation-admission.test.ts +++ b/src/main/runtime/integrations/git/runtime-git-generation-admission.test.ts @@ -23,7 +23,7 @@ vi.mock('../../../git/command-runner/git-exec-file', async (importOriginal) => ( ...(await importOriginal>()), gitExecFileAsync: mocks.gitExecFileAsync })) -vi.mock('../../../text-generation/pull-request-context', () => ({ +vi.mock('../../../text-generation/pull-request-context/pull-request-context', () => ({ getPullRequestDraftContext: mocks.getPullRequestDraftContext })) vi.mock('../../../source-control/pull-request-linked-issue', () => ({ @@ -33,7 +33,7 @@ vi.mock('../../../source-control/pull-request-linked-issue', () => ({ return linkedLookup.run() } })) -vi.mock('../../../text-generation/commit-message-agent-environment', () => ({ +vi.mock('../../../text-generation/execution/commit-message-agent-environment', () => ({ prepareLocalCommitMessageAgentEnv: mocks.prepareLocalCommitMessageAgentEnv })) vi.mock('../../../text-generation/commit-message-text-generation', () => ({ diff --git a/src/main/runtime/integrations/git/runtime-git-generation-commands.ts b/src/main/runtime/integrations/git/runtime-git-generation-commands.ts index a26dc0e0a35..f81be24f0c3 100644 --- a/src/main/runtime/integrations/git/runtime-git-generation-commands.ts +++ b/src/main/runtime/integrations/git/runtime-git-generation-commands.ts @@ -11,7 +11,7 @@ import { getStagedCommitContext } from '../../../git/source-control/mutations/st import { SSH_HOST_NOT_SERVED_MESSAGE } from '../../../providers/ssh-host-not-served-error' import { loadPullRequestLinkedIssue } from '../../../source-control/pull-request-linked-issue' import { resolveHostedReviewBodyForGeneration } from '../../../source-control/pull-request-template' -import { prepareLocalCommitMessageAgentEnv } from '../../../text-generation/commit-message-agent-environment' +import { prepareLocalCommitMessageAgentEnv } from '../../../text-generation/execution/commit-message-agent-environment' import { cancelGenerateCommitMessageLocal, cancelGeneratePullRequestFieldsLocal, @@ -25,7 +25,7 @@ import type { GenerateCommitMessageResult } from '../../../text-generation/source-control-text-generation-types' import type { GeneratePullRequestFieldsResult } from '../../../text-generation/commit-message-text-generation' -import { getPullRequestDraftContext } from '../../../text-generation/pull-request-context' +import { getPullRequestDraftContext } from '../../../text-generation/pull-request-context/pull-request-context' import { localGitOptionsForTarget, runtimeGitRouteForTarget, diff --git a/src/main/runtime/integrations/git/runtime-git-generation-context.ts b/src/main/runtime/integrations/git/runtime-git-generation-context.ts index defe825d5b8..f636028dea0 100644 --- a/src/main/runtime/integrations/git/runtime-git-generation-context.ts +++ b/src/main/runtime/integrations/git/runtime-git-generation-context.ts @@ -1,13 +1,13 @@ import type { GlobalSettings } from '../../../../shared/global-settings-types' import { gitExecFileAsync } from '../../../git/command-runner/git-exec-file' -import type { getPullRequestDraftContext } from '../../../text-generation/pull-request-context' +import type { getPullRequestDraftContext } from '../../../text-generation/pull-request-context/pull-request-context' import { mergeLegacyCommitMessageAiIntoSourceControlAi } from '../../../../shared/source-control/ai/settings/source-control-ai-legacy-reconciliation' import type { SourceControlAiSettingsInput, ResolvedSourceControlAiGenerationParams } from '../../../../shared/source-control/ai/source-control-ai-operation-resolution' import type { SourceControlAiOperation } from '../../../../shared/source-control/ai/settings/source-control-ai-settings-types' -import type { CommitMessageAgentRuntimeTarget } from '../../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentRuntimeTarget } from '../../../text-generation/execution/commit-message-agent-environment' import type { CommitMessageGenerationTarget } from '../../../text-generation/source-control-text-generation-types' import type { PullRequestLinkedIssueMeta } from '../../../source-control/pull-request-linked-issue' import { diff --git a/src/main/runtime/integrations/jira/runtime-jira-commands.ts b/src/main/runtime/integrations/jira/runtime-jira-commands.ts index e1faae96bab..ee4e7d126c4 100644 --- a/src/main/runtime/integrations/jira/runtime-jira-commands.ts +++ b/src/main/runtime/integrations/jira/runtime-jira-commands.ts @@ -6,7 +6,7 @@ import { generateJiraIssueSummaryRequest, cancelJiraIssueSummaryRequest, type JiraIssueSummaryGenerationHost -} from '../../../text-generation/jira-issue-summary-requests' +} from '../../../text-generation/requests/jira-issue-summary-requests' import type { JiraConnectArgs, JiraCreateIssueArgs, diff --git a/src/main/runtime/speech/runtime-mobile-dictation-controller.ts b/src/main/runtime/speech/runtime-mobile-dictation-controller.ts index 12e31c2d0b4..b8fcd55e23f 100644 --- a/src/main/runtime/speech/runtime-mobile-dictation-controller.ts +++ b/src/main/runtime/speech/runtime-mobile-dictation-controller.ts @@ -16,7 +16,7 @@ import { hostDictationTerms } from './runtime-dictation-terms' import { dictationTextSettingsFor } from '../../../shared/dictation-cleanup' import type { DictationTerm } from '../../../shared/dictation-dictionary' import { cleanUpDictatedText } from '../../speech/dictation-cleanup-service' -import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/commit-message-agent-environment' +import type { CommitMessageAgentEnvironmentResolvers } from '../../text-generation/execution/commit-message-agent-environment' import { DictationTranscript, type DictationTranscriptSnapshot diff --git a/src/main/runtime/workspaces/creation/runtime-local-worktree-create.test.ts b/src/main/runtime/workspaces/creation/runtime-local-worktree-create.test.ts index 23ace84b92c..d8790fc72d0 100644 --- a/src/main/runtime/workspaces/creation/runtime-local-worktree-create.test.ts +++ b/src/main/runtime/workspaces/creation/runtime-local-worktree-create.test.ts @@ -15,13 +15,13 @@ const { } = await vi.hoisted(async () => await import('./runtime-local-worktree-create-test-harness')) vi.mock('../../../text-generation/commit-message-text-generation', () => ({ - resolveTextGenerationParams: () => ({ + resolveCommitMessageSettings: () => ({ ok: true, params: { agentId: 'claude', model: 'fixture' } }), generateBranchNameFromContext: async () => ({ success: true, slug: 'fix-auth' }) })) -vi.mock('../../../text-generation/commit-message-agent-environment', () => ({ +vi.mock('../../../text-generation/execution/commit-message-agent-environment', () => ({ prepareLocalCommitMessageAgentEnv: async () => ({ ok: true }) })) diff --git a/src/main/speech/dictation-cleanup-service.ts b/src/main/speech/dictation-cleanup-service.ts index 6ae234774e9..3d7a6c5325e 100644 --- a/src/main/speech/dictation-cleanup-service.ts +++ b/src/main/speech/dictation-cleanup-service.ts @@ -12,10 +12,10 @@ import { getCommitMessageModel } from '../../shared/source-control/ai/generation import { prepareLocalCommitMessageAgentEnv, type CommitMessageAgentEnvironmentResolvers -} from '../text-generation/commit-message-agent-environment' -import { resolveTextGenerationParams } from '../text-generation/commit-message-text-generation' -import { generateDictationCleanup } from '../text-generation/dictation-cleanup-generation-request' -import { cancelLocalGeneration } from '../text-generation/source-control-generation-lanes' +} from '../text-generation/execution/commit-message-agent-environment' +import { resolveCommitMessageSettings as resolveTextGenerationParams } from '../text-generation/commit-message-text-generation' +import { generateDictationCleanup } from '../text-generation/requests/dictation-cleanup-generation-request' +import { cancelLocalGeneration } from '../text-generation/execution/source-control-generation-lanes' export type DictationCleanupDeps = { getSettings: () => Parameters[0] & { voice?: VoiceSettings } diff --git a/src/main/text-generation/agent-generation-request.ts b/src/main/text-generation/agent-generation-request.ts index 4275bcb86bb..04d8d595a46 100644 --- a/src/main/text-generation/agent-generation-request.ts +++ b/src/main/text-generation/agent-generation-request.ts @@ -10,11 +10,11 @@ import { } from '../../shared/source-control/ai/generation/commit-message-plan' import type { ResolvedSourceControlAiGenerationParams } from '../../shared/source-control/ai/source-control-ai-operation-resolution' import { openCodeVariantRetryPlan } from '../../shared/opencode-generation-command' -import { copilotUnavailableModelRetryPlan } from './copilot-unavailable-model-retry' -import { runLocalPlanForAgent } from './source-control-local-generation' +import { copilotUnavailableModelRetryPlan } from './failures/copilot-unavailable-model-retry' +import { runLocalPlanForAgent } from './execution/source-control-local-generation' import { runOpenCodeTextTask } from '../opencode/text-tasks/opencode-text-task' -import { cleanAgentAnswer, emptyResultFailure } from './source-control-agent-failure' -import { runRemoteSourceControlPlan } from './source-control-remote-generation' +import { cleanAgentAnswer, emptyResultFailure } from './failures/source-control-agent-failure' +import { runRemoteSourceControlPlan } from './execution/source-control-remote-generation' import type { CommitMessageGenerationTarget, InternalTextGenerationResult, diff --git a/src/main/text-generation/commit-message-text-generation.ts b/src/main/text-generation/commit-message-text-generation.ts index 6e85a41e920..ee8eebd78e8 100644 --- a/src/main/text-generation/commit-message-text-generation.ts +++ b/src/main/text-generation/commit-message-text-generation.ts @@ -3,7 +3,7 @@ import type { BranchNameWorkContext } from '../../shared/source-control/ai/namin import type { ConversationNameContext } from '../../shared/source-control/ai/generation/conversation-name-generation' import type { CommitMessageDraftContext } from '../../shared/source-control/ai/generation/commit-message-generation' import { LOCAL_COMMIT_MESSAGE_HOST_KEY } from '../../shared/source-control/ai/generation/commit-message-host-key' -import type { CommandTemplateBackslash } from '../../shared/source-control/ai/generation/commit-message-prompt' + import type { GeneratedPullRequestFields, PullRequestDraftContext @@ -18,51 +18,33 @@ import type { TuiAgent } from '../../shared/agent-launch/catalog/tui-agent' import { discoverModelsLocal, type CommitMessageModelDiscoveryLocalOptions -} from './commit-message-model-discovery' -import { discoverModelsWithHostCache } from './model-discovery-host-cache' -import { spawnSourceControlAgent } from './source-control-agent-launch' -import { generateConversationName } from './conversation-name-generation-request' -import { cancelLocalGeneration } from './source-control-generation-lanes' +} from './models/commit-message-model-discovery' +import { discoverModelsWithHostCache } from './models/model-discovery-host-cache' +import { spawnSourceControlAgent } from './execution/source-control-agent-launch' +import { generateConversationName } from './requests/conversation-name-generation-request' +import { cancelLocalGeneration } from './execution/source-control-generation-lanes' +import { commandBackslashMode } from './agent-generation-request' import { - commandBackslashMode as resolveCommandBackslashMode, generateBranchName, generateCommitMessage, - generatePullRequestFields, - trimGeneratedCommitMessage as trimCommitMessage -} from './source-control-text-generation-requests' + generatePullRequestFields +} from './requests/source-control-text-generation-requests' import type { CommitMessageGenerationTarget, DiscoverCommitMessageModelsResult, GenerateBranchNameResult, GenerateConversationNameResult, GenerateCommitMessageResult, - GeneratePullRequestFieldsResult as GenericGeneratePullRequestFieldsResult, - RemoteCommitMessageExecResult, - TextGenerationOperation + GeneratePullRequestFieldsResult as GenericGeneratePullRequestFieldsResult } from './source-control-text-generation-types' -export type GenerateCommitMessageParams = ResolvedSourceControlAiGenerationParams -export type { - CommitMessageGenerationTarget, - CommitMessageModelDiscoveryLocalOptions, - DiscoverCommitMessageModelsResult, - GenerateBranchNameResult, - GenerateConversationNameResult, - GenerateCommitMessageResult, - RemoteCommitMessageExecResult, - TextGenerationOperation -} export type GeneratePullRequestFieldsResult = GenericGeneratePullRequestFieldsResult type ResolveCommitMessageSettingsResult = - | { ok: true; params: GenerateCommitMessageParams } + | { ok: true; params: ResolvedSourceControlAiGenerationParams } | ({ ok: false } & TextGenerationFailureFields) -export function trimGeneratedCommitMessage(message: string): string { - return trimCommitMessage(message) -} - export function resolveCommitMessageSettings( settings: Parameters[0]['settings'], discoveryHostKey = LOCAL_COMMIT_MESSAGE_HOST_KEY, @@ -78,22 +60,6 @@ export function resolveCommitMessageSettings( return resolved.ok ? { ok: true, params: resolved.value.params } : resolved } -export function resolveTextGenerationParams( - settings: Parameters[0]['settings'], - discoveryHostKey = LOCAL_COMMIT_MESSAGE_HOST_KEY, - operation: AiTextOperation = 'commitMessage', - repo?: Pick | null -): ResolveCommitMessageSettingsResult { - return resolveCommitMessageSettings(settings, discoveryHostKey, operation, repo) -} - -export function commandBackslashMode( - target: CommitMessageGenerationTarget, - platform: NodeJS.Platform = process.platform -): CommandTemplateBackslash { - return resolveCommandBackslashMode(target, platform) -} - export function discoverCommitMessageModelsLocal( agentId: TuiAgent, env: NodeJS.ProcessEnv | undefined, @@ -126,7 +92,7 @@ export function cancelGeneratePullRequestFieldsLocal(cwd: string): void { export function generateCommitMessageFromContext( context: CommitMessageDraftContext, - params: GenerateCommitMessageParams, + params: ResolvedSourceControlAiGenerationParams, target: CommitMessageGenerationTarget ): Promise { return generateCommitMessage({ context, params, target, spawnAgent: spawnSourceControlAgent }) @@ -134,7 +100,7 @@ export function generateCommitMessageFromContext( export function generatePullRequestFieldsFromContext( context: PullRequestDraftContext, - params: GenerateCommitMessageParams, + params: ResolvedSourceControlAiGenerationParams, target: CommitMessageGenerationTarget ): Promise { return generatePullRequestFields({ context, params, target, spawnAgent: spawnSourceControlAgent }) @@ -142,7 +108,7 @@ export function generatePullRequestFieldsFromContext( export function generateBranchNameFromContext( context: BranchNameWorkContext, - params: GenerateCommitMessageParams, + params: ResolvedSourceControlAiGenerationParams, target: CommitMessageGenerationTarget ): Promise { return generateBranchName({ context, params, target, spawnAgent: spawnSourceControlAgent }) @@ -150,7 +116,7 @@ export function generateBranchNameFromContext( export function generateConversationNameFromContext( context: ConversationNameContext, - params: GenerateCommitMessageParams, + params: ResolvedSourceControlAiGenerationParams, target: CommitMessageGenerationTarget, shouldExecute?: () => boolean ): Promise { diff --git a/src/main/text-generation/command-environment-process.test.ts b/src/main/text-generation/execution/command-environment-process.test.ts similarity index 98% rename from src/main/text-generation/command-environment-process.test.ts rename to src/main/text-generation/execution/command-environment-process.test.ts index 13ffb231212..19a49f2c792 100644 --- a/src/main/text-generation/command-environment-process.test.ts +++ b/src/main/text-generation/execution/command-environment-process.test.ts @@ -5,7 +5,7 @@ import { afterAll, describe, expect, it } from 'vitest' import { discoverCommitMessageModelsLocal, generateCommitMessageFromContext -} from './commit-message-text-generation' +} from '../commit-message-text-generation' const folder = mkdtempSync(join(tmpdir(), 'orca-command-env-')) const script = join(folder, 'agent.cjs') diff --git a/src/main/text-generation/commit-message-agent-environment.test.ts b/src/main/text-generation/execution/commit-message-agent-environment.test.ts similarity index 98% rename from src/main/text-generation/commit-message-agent-environment.test.ts rename to src/main/text-generation/execution/commit-message-agent-environment.test.ts index d3e5f4bd2fd..f90cc8cb368 100644 --- a/src/main/text-generation/commit-message-agent-environment.test.ts +++ b/src/main/text-generation/execution/commit-message-agent-environment.test.ts @@ -3,7 +3,7 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it, vi } from 'vitest' import { prepareLocalCommitMessageAgentEnv } from './commit-message-agent-environment' -import '../codex-accounts/managed-homes/host-codex-managed-home-ownership' +import '../../codex-accounts/managed-homes/host-codex-managed-home-ownership' const originalEnv = { ...process.env } const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform') diff --git a/src/main/text-generation/commit-message-agent-environment.ts b/src/main/text-generation/execution/commit-message-agent-environment.ts similarity index 93% rename from src/main/text-generation/commit-message-agent-environment.ts rename to src/main/text-generation/execution/commit-message-agent-environment.ts index 7ce44a57547..806dc0d4fed 100644 --- a/src/main/text-generation/commit-message-agent-environment.ts +++ b/src/main/text-generation/execution/commit-message-agent-environment.ts @@ -1,11 +1,11 @@ -import type { ClaudeRuntimeAuthPreparation } from '../claude-accounts/runtime-auth/runtime-auth-types' -import { applyClaudeEnvPatch } from '../claude-accounts/runtime-auth/environment' -import { readShellStartupEnvVar } from '../pty/shell-startup/shell-startup-env' -import { parseWslUncPath } from '../../shared/wsl-paths' +import type { ClaudeRuntimeAuthPreparation } from '../../claude-accounts/runtime-auth/runtime-auth-types' +import { applyClaudeEnvPatch } from '../../claude-accounts/runtime-auth/environment' +import { readShellStartupEnvVar } from '../../pty/shell-startup/shell-startup-env' +import { parseWslUncPath } from '../../../shared/wsl-paths' import { processEnvWithoutAgentSession, stripInheritedAgentSessionEnv -} from '../../shared/agent-launch/startup/parent-agent-session-env' +} from '../../../shared/agent-launch/startup/parent-agent-session-env' export type CommitMessageAgentEnvironmentResolvers = { prepareForCodexLaunch?: ( diff --git a/src/main/text-generation/commit-message-text-generation-cancellation.test.ts b/src/main/text-generation/execution/commit-message-text-generation-cancellation.test.ts similarity index 97% rename from src/main/text-generation/commit-message-text-generation-cancellation.test.ts rename to src/main/text-generation/execution/commit-message-text-generation-cancellation.test.ts index 01e281c9a37..83133beb676 100644 --- a/src/main/text-generation/commit-message-text-generation-cancellation.test.ts +++ b/src/main/text-generation/execution/commit-message-text-generation-cancellation.test.ts @@ -5,7 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { cancelGenerateCommitMessageLocal, generateCommitMessageFromContext -} from './commit-message-text-generation' +} from '../commit-message-text-generation' import { createChildTerminationExpectation, createMockDiscoveryChild, @@ -16,7 +16,7 @@ const { terminateWindowsProcessTreeMock } = vi.hoisted(() => ({ terminateWindowsProcessTreeMock: vi.fn(async () => {}) })) -vi.mock('../pty/process-retirement/windows-process-tree-kill', () => ({ +vi.mock('../../pty/process-retirement/windows-process-tree-kill', () => ({ terminateWindowsProcessTree: terminateWindowsProcessTreeMock })) diff --git a/src/main/text-generation/commit-message-text-generation-local-subprocess.test.ts b/src/main/text-generation/execution/commit-message-text-generation-local-subprocess.test.ts similarity index 95% rename from src/main/text-generation/commit-message-text-generation-local-subprocess.test.ts rename to src/main/text-generation/execution/commit-message-text-generation-local-subprocess.test.ts index 9c5df2c8a15..28e3af02068 100644 --- a/src/main/text-generation/commit-message-text-generation-local-subprocess.test.ts +++ b/src/main/text-generation/execution/commit-message-text-generation-local-subprocess.test.ts @@ -1,7 +1,7 @@ import { spawn } from 'node:child_process' import type * as ChildProcess from 'node:child_process' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { generateCommitMessageFromContext } from './commit-message-text-generation' +import { generateCommitMessageFromContext } from '../commit-message-text-generation' import { createChildTerminationExpectation, withPlatform @@ -11,7 +11,7 @@ const { terminateWindowsProcessTreeMock } = vi.hoisted(() => ({ terminateWindowsProcessTreeMock: vi.fn(async () => {}) })) -vi.mock('../pty/process-retirement/windows-process-tree-kill', () => ({ +vi.mock('../../pty/process-retirement/windows-process-tree-kill', () => ({ terminateWindowsProcessTree: terminateWindowsProcessTreeMock })) diff --git a/src/main/text-generation/commit-message-text-generation-remote-execution.test.ts b/src/main/text-generation/execution/commit-message-text-generation-remote-execution.test.ts similarity index 95% rename from src/main/text-generation/commit-message-text-generation-remote-execution.test.ts rename to src/main/text-generation/execution/commit-message-text-generation-remote-execution.test.ts index 6cb496d90e5..bb0ec740533 100644 --- a/src/main/text-generation/commit-message-text-generation-remote-execution.test.ts +++ b/src/main/text-generation/execution/commit-message-text-generation-remote-execution.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { generateCommitMessageFromContext } from './commit-message-text-generation' +import { generateCommitMessageFromContext } from '../commit-message-text-generation' describe('generateCommitMessageFromContext', () => { it('uses a prepared remote execution plan instead of running git on the remote side', async () => { diff --git a/src/main/text-generation/commit-message-text-generation-test-harness.ts b/src/main/text-generation/execution/commit-message-text-generation-test-harness.ts similarity index 97% rename from src/main/text-generation/commit-message-text-generation-test-harness.ts rename to src/main/text-generation/execution/commit-message-text-generation-test-harness.ts index 948fb2e80c9..e9e923fa3ae 100644 --- a/src/main/text-generation/commit-message-text-generation-test-harness.ts +++ b/src/main/text-generation/execution/commit-message-text-generation-test-harness.ts @@ -1,6 +1,6 @@ import { ChildProcess } from 'node:child_process' import { PassThrough } from 'node:stream' -import { isProviderRecord } from '../provider-process/provider-json-record' +import { isProviderRecord } from '../../provider-process/provider-json-record' import { expect, vi } from 'vitest' export type MockDiscoveryChild = ChildProcess & { diff --git a/src/main/text-generation/source-control-agent-cli-path.test.ts b/src/main/text-generation/execution/source-control-agent-cli-path.test.ts similarity index 93% rename from src/main/text-generation/source-control-agent-cli-path.test.ts rename to src/main/text-generation/execution/source-control-agent-cli-path.test.ts index 307d353d149..027c206b3eb 100644 --- a/src/main/text-generation/source-control-agent-cli-path.test.ts +++ b/src/main/text-generation/execution/source-control-agent-cli-path.test.ts @@ -2,8 +2,8 @@ import { chmodSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' -import { quotePosixShell } from '../../shared/shell/launch/shell-argument-quoting' -import { generateConversationNameFromContext } from './commit-message-text-generation' +import { quotePosixShell } from '../../../shared/shell/launch/shell-argument-quoting' +import { generateConversationNameFromContext } from '../commit-message-text-generation' const homes: string[] = [] diff --git a/src/main/text-generation/source-control-agent-hook-isolation.test.ts b/src/main/text-generation/execution/source-control-agent-hook-isolation.test.ts similarity index 100% rename from src/main/text-generation/source-control-agent-hook-isolation.test.ts rename to src/main/text-generation/execution/source-control-agent-hook-isolation.test.ts diff --git a/src/main/text-generation/source-control-agent-launch.test.ts b/src/main/text-generation/execution/source-control-agent-launch.test.ts similarity index 95% rename from src/main/text-generation/source-control-agent-launch.test.ts rename to src/main/text-generation/execution/source-control-agent-launch.test.ts index b11df24d2ac..cda7896d791 100644 --- a/src/main/text-generation/source-control-agent-launch.test.ts +++ b/src/main/text-generation/execution/source-control-agent-launch.test.ts @@ -5,8 +5,8 @@ import { join } from 'node:path' import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' import type * as RunProcess from '@orca/process-host' import type { ProcessSpec } from '@orca/process-host/process-spec' -import '../provider-process/provider-process-supervisor' -import { generateCommitMessageFromContext } from './commit-message-text-generation' +import '../../provider-process/provider-process-supervisor' +import { generateCommitMessageFromContext } from '../commit-message-text-generation' import './commit-message-text-generation-test-harness' import './source-control-agent-launch' diff --git a/src/main/text-generation/source-control-agent-launch.ts b/src/main/text-generation/execution/source-control-agent-launch.ts similarity index 83% rename from src/main/text-generation/source-control-agent-launch.ts rename to src/main/text-generation/execution/source-control-agent-launch.ts index 4f257cb93a2..ac9c59a2e74 100644 --- a/src/main/text-generation/source-control-agent-launch.ts +++ b/src/main/text-generation/execution/source-control-agent-launch.ts @@ -1,18 +1,20 @@ -import { wslDistroForCommand } from '../../shared/wsl-paths' +import { wslDistroForCommand } from '../../../shared/wsl-paths' import { spawnProcess } from '@orca/process-host' -import { withCliRuntimeOnPath } from '../../shared/node-cli-command-resolution' -import { ORCA_AGENT_HOOKS_DISABLED_ENV } from '../../shared/agent-hook-listener/transport/agent-hook-suppression-env' -import { quotePosixShell } from '../../shared/shell/launch/shell-argument-quoting' -import { resolveCliCommand } from '../codex-cli/command' -import { wslAwareSpawn } from '../git/command-runner/host-routing/wsl-aware-spawn' -import { AGENT_PANE_HOOK_ENV_KEYS } from '../ipc/pty/host-env/spawn-env-keys' -import { processEnvWithoutAgentSession } from '../../shared/agent-launch/startup/parent-agent-session-env' +import { + withCliRuntimeOnPath, + resolveCliCommand +} from '../../../shared/node-cli-command-resolution' +import { ORCA_AGENT_HOOKS_DISABLED_ENV } from '../../../shared/agent-hook-listener/transport/agent-hook-suppression-env' +import { quotePosixShell } from '../../../shared/shell/launch/shell-argument-quoting' +import { wslAwareSpawn } from '../../git/command-runner/host-routing/wsl-aware-spawn' +import { AGENT_PANE_HOOK_ENV_KEYS } from '../../ipc/pty/host-env/spawn-env-keys' +import { processEnvWithoutAgentSession } from '../../../shared/agent-launch/startup/parent-agent-session-env' import { spawnSupervisedAgent } from './source-control-supervised-agent-spawn' import type { SourceControlAgentSpawnInput, SpawnedSourceControlAgentProcess, SpawnSourceControlAgent -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' const WSL_LAUNCHER_ENV_KEYS = [ 'ComSpec', diff --git a/src/main/text-generation/source-control-agent-termination.ts b/src/main/text-generation/execution/source-control-agent-termination.ts similarity index 70% rename from src/main/text-generation/source-control-agent-termination.ts rename to src/main/text-generation/execution/source-control-agent-termination.ts index f0d7f8663b1..d8d89ebbf1d 100644 --- a/src/main/text-generation/source-control-agent-termination.ts +++ b/src/main/text-generation/execution/source-control-agent-termination.ts @@ -1,6 +1,6 @@ -import { stopSupervisedChildProcess } from '../provider-process/supervised-child-process-stop' -import { terminateWindowsProcessTree } from '../pty/process-retirement/windows-process-tree-kill' -import type { SpawnedSourceControlAgentProcess } from './source-control-text-generation-types' +import { stopSupervisedChildProcess } from '../../provider-process/supervised-child-process-stop' +import { terminateWindowsProcessTree } from '../../pty/process-retirement/windows-process-tree-kill' +import type { SpawnedSourceControlAgentProcess } from '../source-control-text-generation-types' export const SOURCE_CONTROL_KILL_SITE = 'source-control-text-generation' diff --git a/src/main/text-generation/source-control-command-line-budget.ts b/src/main/text-generation/execution/source-control-command-line-budget.ts similarity index 80% rename from src/main/text-generation/source-control-command-line-budget.ts rename to src/main/text-generation/execution/source-control-command-line-budget.ts index 35c3b42c461..9b93330861c 100644 --- a/src/main/text-generation/source-control-command-line-budget.ts +++ b/src/main/text-generation/execution/source-control-command-line-budget.ts @@ -1,9 +1,12 @@ import { textGenerationFailure, type TextGenerationFailureFields -} from '../../shared/text-generation-failure-copy' -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' -import { commandLineLength, MAX_COMMAND_LINE_CHARS } from '../../shared/windows-command-line-budget' +} from '../../../shared/text-generation-failure-copy' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' +import { + commandLineLength, + MAX_COMMAND_LINE_CHARS +} from '../../../shared/windows-command-line-budget' // Linux caps each argv entry in bytes (128KiB on 4KiB pages), before total argv capacity; reserve headroom for single-prompt arguments. const LINUX_SINGLE_ARGUMENT_BYTE_BUDGET = 120 * 1024 diff --git a/src/main/text-generation/source-control-generation-lanes.ts b/src/main/text-generation/execution/source-control-generation-lanes.ts similarity index 88% rename from src/main/text-generation/source-control-generation-lanes.ts rename to src/main/text-generation/execution/source-control-generation-lanes.ts index ee6081c738c..5dda38b67e6 100644 --- a/src/main/text-generation/source-control-generation-lanes.ts +++ b/src/main/text-generation/execution/source-control-generation-lanes.ts @@ -1,4 +1,4 @@ -import type { TextGenerationOperation } from './source-control-text-generation-types' +import type { TextGenerationOperation } from '../source-control-text-generation-types' const cancelTokensByLane = new Map void>() diff --git a/src/main/text-generation/source-control-generation-limits.ts b/src/main/text-generation/execution/source-control-generation-limits.ts similarity index 100% rename from src/main/text-generation/source-control-generation-limits.ts rename to src/main/text-generation/execution/source-control-generation-limits.ts diff --git a/src/main/text-generation/source-control-local-generation.ts b/src/main/text-generation/execution/source-control-local-generation.ts similarity index 93% rename from src/main/text-generation/source-control-local-generation.ts rename to src/main/text-generation/execution/source-control-local-generation.ts index 0b96407a53d..dcbd88392b0 100644 --- a/src/main/text-generation/source-control-local-generation.ts +++ b/src/main/text-generation/execution/source-control-local-generation.ts @@ -1,26 +1,26 @@ -import { wslDistroForCommand } from '../../shared/wsl-paths' -import { mergeCommandEnvironment } from '../../shared/command-environment' -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' +import { wslDistroForCommand } from '../../../shared/wsl-paths' +import { mergeCommandEnvironment } from '../../../shared/command-environment' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' import { resolveCodexHomeProcessLockKeyForSpawnEnv, withCodexHomeProcessLock -} from '../codex-cli/codex-home-process-lock' +} from '../../codex-cli/codex-home-process-lock' import { clearLocalGenerationCancelToken, localGenerationLaneKey, setLocalGenerationCancelToken } from './source-control-generation-lanes' import { runLocalSourceControlPlan } from './source-control-local-process' -import { prepareSourceControlReadOnlyPlan } from './source-control-read-only-policy' +import { prepareSourceControlReadOnlyPlan } from '../policies/source-control-read-only-policy' import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from './source-control-generation-limits' -import { textGenerationFailure } from '../../shared/text-generation-failure-copy' +import { textGenerationFailure } from '../../../shared/text-generation-failure-copy' import type { InternalTextGenerationResult, LocalGenerationTarget, LocalProcessExecution, SpawnSourceControlAgent, TextGenerationOperation -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' export function runLocalPlanForAgent(input: { agentId: string diff --git a/src/main/text-generation/source-control-local-process.test.ts b/src/main/text-generation/execution/source-control-local-process.test.ts similarity index 94% rename from src/main/text-generation/source-control-local-process.test.ts rename to src/main/text-generation/execution/source-control-local-process.test.ts index 28174bd29e6..de2b0e3bd9f 100644 --- a/src/main/text-generation/source-control-local-process.test.ts +++ b/src/main/text-generation/execution/source-control-local-process.test.ts @@ -3,16 +3,16 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it, vi } from 'vitest' -import { cancelGenerateCommitMessageLocal } from './commit-message-text-generation' +import { cancelGenerateCommitMessageLocal } from '../commit-message-text-generation' import { spawnSourceControlAgent } from './source-control-agent-launch' -import { generateCommitMessage } from './source-control-text-generation-requests' +import { generateCommitMessage } from '../requests/source-control-text-generation-requests' const { terminateTreeMock } = vi.hoisted(() => ({ terminateTreeMock: vi.fn(async () => true) })) -vi.mock('../provider-process/provider-process-teardown', () => ({ +vi.mock('../../provider-process/provider-process-teardown', () => ({ terminateProviderProcessTree: terminateTreeMock })) diff --git a/src/main/text-generation/source-control-local-process.ts b/src/main/text-generation/execution/source-control-local-process.ts similarity index 92% rename from src/main/text-generation/source-control-local-process.ts rename to src/main/text-generation/execution/source-control-local-process.ts index db91e525792..15149b1ef4b 100644 --- a/src/main/text-generation/source-control-local-process.ts +++ b/src/main/text-generation/execution/source-control-local-process.ts @@ -1,18 +1,17 @@ -import { textGenerationFailure } from '../../shared/text-generation-failure-copy' +import { textGenerationFailure } from '../../../shared/text-generation-failure-copy' import { killSourceControlAgentProcess } from './source-control-agent-termination' -import { wslDistroForCommand } from '../../shared/wsl-paths' -import { prepareWslPrompt, stageLocalPromptFiles } from './source-control-prompt-files' -import { producePromptRecords } from './source-control-prompt-producer' +import { wslDistroForCommand } from '../../../shared/wsl-paths' +import { prepareWslPrompt, stageLocalPromptFiles } from '../prompts/source-control-prompt-files' +import { producePromptRecords } from '../prompts/source-control-prompt-producer' import { argumentBudgetFailure } from './source-control-command-line-budget' -export { fitsLocalCommandLine } from './source-control-command-line-budget' -import { removeTree } from '../../shared/windows-transient-lock-removal' +import { removeTree } from '../../../shared/windows-transient-lock-removal' import { WindowsCmdLineBreakError } from '@orca/process-host/windows-command-line' -import { WindowsCommandLineBudgetError } from '../../shared/windows-command-line-budget' -import { supervisedProviderSpawnFailure } from '../provider-process/provider-spawn-failure-report' +import { WindowsCommandLineBudgetError } from '../../../shared/windows-command-line-budget' +import { supervisedProviderSpawnFailure } from '../../provider-process/provider-spawn-failure-report' import { finalizeFromAgentOutput, userFacingUnsafeWindowsBatchArgs -} from './source-control-agent-failure' +} from '../failures/source-control-agent-failure' import { clearLocalGenerationCancelToken, localGenerationLaneKey, @@ -27,9 +26,7 @@ import type { LocalProcessExecution, SpawnedSourceControlAgentProcess, LocalSourceControlPlanInput -} from './source-control-text-generation-types' - -export * from './source-control-agent-termination' +} from '../source-control-text-generation-types' export function runLocalSourceControlPlan( input: LocalSourceControlPlanInput diff --git a/src/main/text-generation/source-control-remote-generation.ts b/src/main/text-generation/execution/source-control-remote-generation.ts similarity index 86% rename from src/main/text-generation/source-control-remote-generation.ts rename to src/main/text-generation/execution/source-control-remote-generation.ts index 554bda8172d..e4c67ece744 100644 --- a/src/main/text-generation/source-control-remote-generation.ts +++ b/src/main/text-generation/execution/source-control-remote-generation.ts @@ -1,17 +1,17 @@ -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' -import { isSshRequestOutcomeUnverifiable } from '../ssh/protocol/ssh-channel-multiplexer' -import { WINDOWS_BATCH_UNSAFE_ARGUMENTS_ERROR } from '../../shared/windows-batch-spawn' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' +import { isSshRequestOutcomeUnverifiable } from '../../ssh/protocol/ssh-channel-multiplexer' +import { WINDOWS_BATCH_UNSAFE_ARGUMENTS_ERROR } from '../../../shared/windows-batch-spawn' import { finalizeFromAgentOutput, userFacingUnsafeWindowsBatchArgs -} from './source-control-agent-failure' +} from '../failures/source-control-agent-failure' import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from './source-control-generation-limits' import type { InternalTextGenerationResult, RemoteCommitMessageExecResult, RemoteGenerationTarget, TextGenerationOperation -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' export async function runRemoteSourceControlPlan(input: { plan: CommitMessagePlan diff --git a/src/main/text-generation/source-control-supervised-agent-spawn.ts b/src/main/text-generation/execution/source-control-supervised-agent-spawn.ts similarity index 75% rename from src/main/text-generation/source-control-supervised-agent-spawn.ts rename to src/main/text-generation/execution/source-control-supervised-agent-spawn.ts index 70132c52640..10ae41e65b6 100644 --- a/src/main/text-generation/source-control-supervised-agent-spawn.ts +++ b/src/main/text-generation/execution/source-control-supervised-agent-spawn.ts @@ -1,14 +1,17 @@ import { spawnProcess } from '@orca/process-host' -import { pathEnvRelativeTo, withCliRuntimeOnPath } from '../../shared/node-cli-command-resolution' -import { resolveGuestNativeAgentCommand } from '../codex-cli/command' -import { createProviderSpawnSpec } from '../provider-process/provider-process-supervisor' -import { guardSupervisedProviderGroup } from '../provider-process/provider-group-orphan-guard' +import { + pathEnvRelativeTo, + withCliRuntimeOnPath +} from '../../../shared/node-cli-command-resolution' +import { resolveGuestNativeAgentCommand } from '../../codex-cli/command' +import { createProviderSpawnSpec } from '../../provider-process/provider-process-supervisor' +import { guardSupervisedProviderGroup } from '../../provider-process/provider-group-orphan-guard' import { SOURCE_CONTROL_KILL_SITE } from './source-control-agent-termination' -import { guestNativeSpawnEnv } from '../ipc/wsl-guest-windows-drive-path' +import { guestNativeSpawnEnv } from '../../ipc/wsl-guest-windows-drive-path' import type { SourceControlAgentSpawnInput, SpawnedSourceControlAgentProcess -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' // Supervisor owns the agent group so parent quit/death still stops generation. export function spawnSupervisedAgent( diff --git a/src/main/text-generation/execution/source-control-wsl-launch.test.ts b/src/main/text-generation/execution/source-control-wsl-launch.test.ts new file mode 100644 index 00000000000..3a9e2c60713 --- /dev/null +++ b/src/main/text-generation/execution/source-control-wsl-launch.test.ts @@ -0,0 +1,105 @@ +import type * as ChildProcess from 'node:child_process' +import { mkdtempSync, writeFileSync, realpathSync } from 'node:fs' +import { rm } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { expect, it, vi, afterEach } from 'vitest' +import { quotePosixShell } from '../../../shared/shell/launch/shell-argument-quoting' +import '../../../shared/windows-command-line-budget' +import { withPlatform } from './commit-message-text-generation-test-harness' +import { spawnSourceControlAgent } from './source-control-agent-launch' +import './source-control-local-generation' + +const { spawnMock } = vi.hoisted(() => ({ spawnMock: vi.fn() })) +vi.mock('node:child_process', async (original) => { + const actual = await original() + spawnMock.mockImplementation(actual.spawn) + return { ...actual, spawn: spawnMock } +}) +vi.mock('../../wsl/discovery/wsl-background-distro-gate', () => ({ + noteWslDistroLaunched: vi.fn() +})) +const nativeSpawn = (await vi.importActual('node:child_process')).spawn +afterEach(() => { + spawnMock.mockReset().mockImplementation(nativeSpawn) +}) + +it.skipIf(process.platform === 'win32')( + 'clears profile-restored hook identity after login while preserving shell function commands', + async () => { + const directory = realpathSync(mkdtempSync(join(tmpdir(), 'orca-wsl-hook-isolation-'))) + const profile = join(directory, 'rc') + const shell = join(directory, 'bash') + const reader = join(directory, 'reader.cjs') + writeFileSync( + shell, + `#!/bin/sh\nexec /bin/bash --noprofile --rcfile ${quotePosixShell(profile)} -ic "$2"\n`, + { mode: 0o700 } + ) + writeFileSync( + join(directory, 'getent'), + `#!/bin/sh\nprintf '%s\\n' ${quotePosixShell(`user:x:1:1:User:${directory}:${shell}`)}\n`, + { mode: 0o700 } + ) + writeFileSync( + profile, + `export ORCA_PANE_KEY=profile-pane ORCA_AGENT_PANE=profile-pane ORCA_AGENT_HOOK_PORT=54321 ORCA_AGENT_HOOKS_DISABLED=0 +export CODEX_HOME=profile-managed-home +review_agent() { ${quotePosixShell(process.execPath)} ${quotePosixShell(reader)} "$@"; } +` + ) + writeFileSync( + reader, + `console.log(JSON.stringify({ + pane: process.env.ORCA_PANE_KEY ?? null, + alias: process.env.ORCA_AGENT_PANE ?? null, + port: process.env.ORCA_AGENT_HOOK_PORT ?? null, + disabled: process.env.ORCA_AGENT_HOOKS_DISABLED, + home: process.env.CODEX_HOME, + args: process.argv.slice(2) +})) +` + ) + // Run the real guest login command locally; only replace the Windows executable boundary. + spawnMock.mockImplementationOnce((_program, args, options) => + nativeSpawn('/bin/sh', ['-c', args.at(-1)], options) + ) + try { + const child = withPlatform('win32', () => + spawnSourceControlAgent({ + binary: 'review_agent', + args: ['literal $HOME'], + wslDistro: 'Review', + env: { + ...process.env, + HOME: directory, + SHELL: shell, + PATH: `${directory}:${process.env.PATH}` + }, + stdinMode: 'pipe', + useCwdForNative: true + }) + ) + let stdout = '' + let stderr = '' + child.stdout?.on('data', (chunk) => (stdout += chunk)) + child.stderr?.on('data', (chunk) => (stderr += chunk)) + const closed = new Promise((resolve, reject) => { + child.on('error', reject) + child.on('close', resolve) + }) + child.stdin?.end() + expect(await closed, stderr).toBe(0) + expect(JSON.parse(stdout)).toEqual({ + pane: null, + alias: null, + port: null, + disabled: '1', + home: 'profile-managed-home', + args: ['literal $HOME'] + }) + } finally { + await rm(directory, { recursive: true, force: true }) + } + } +) diff --git a/src/main/text-generation/agent-failure-output.test.ts b/src/main/text-generation/failures/agent-failure-output.test.ts similarity index 100% rename from src/main/text-generation/agent-failure-output.test.ts rename to src/main/text-generation/failures/agent-failure-output.test.ts diff --git a/src/main/text-generation/agent-failure-output.ts b/src/main/text-generation/failures/agent-failure-output.ts similarity index 94% rename from src/main/text-generation/agent-failure-output.ts rename to src/main/text-generation/failures/agent-failure-output.ts index 7b4e7f6c684..b68e578160e 100644 --- a/src/main/text-generation/agent-failure-output.ts +++ b/src/main/text-generation/failures/agent-failure-output.ts @@ -1,4 +1,4 @@ -import { stripAnsiControlSequences } from '../../shared/source-control/ai/generation/commit-message-agent-output' +import { stripAnsiControlSequences } from '../../../shared/source-control/ai/generation/commit-message-agent-output' /** Bounded failure output is display-on-demand only; never persist or sync it. */ export type AgentGenerationFailureOutput = { diff --git a/src/main/text-generation/antigravity-generation-stream.test.ts b/src/main/text-generation/failures/antigravity-generation-stream.test.ts similarity index 94% rename from src/main/text-generation/antigravity-generation-stream.test.ts rename to src/main/text-generation/failures/antigravity-generation-stream.test.ts index 588c7a782eb..6c1f6ed1ea8 100644 --- a/src/main/text-generation/antigravity-generation-stream.test.ts +++ b/src/main/text-generation/failures/antigravity-generation-stream.test.ts @@ -1,14 +1,14 @@ import { describe, expect, it, vi } from 'vitest' -import { parseAntigravityGenerationOutput } from '../../shared/antigravity-generation-stream' +import { parseAntigravityGenerationOutput } from '../../../shared/antigravity-generation-stream' import { generateCommitMessageFromContext, generatePullRequestFieldsFromContext -} from './commit-message-text-generation' -import { generatePullRequestFields } from './source-control-text-generation-requests' +} from '../commit-message-text-generation' +import { generatePullRequestFields } from '../requests/source-control-text-generation-requests' import { createMockDiscoveryChild, withPlatform -} from './commit-message-text-generation-test-harness' +} from '../execution/commit-message-text-generation-test-harness' const resultEvent = (response: string, status = 'SUCCESS', error?: string): string => JSON.stringify({ event: 'result', result: { status, response, error } }) diff --git a/src/main/text-generation/commit-message-text-generation-failure-sanitization.test.ts b/src/main/text-generation/failures/commit-message-text-generation-failure-sanitization.test.ts similarity index 98% rename from src/main/text-generation/commit-message-text-generation-failure-sanitization.test.ts rename to src/main/text-generation/failures/commit-message-text-generation-failure-sanitization.test.ts index 37c7ddaa563..2a1977e2825 100644 --- a/src/main/text-generation/commit-message-text-generation-failure-sanitization.test.ts +++ b/src/main/text-generation/failures/commit-message-text-generation-failure-sanitization.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { generateCommitMessageFromContext } from './commit-message-text-generation' +import { generateCommitMessageFromContext } from '../commit-message-text-generation' describe('generateCommitMessageFromContext', () => { it('exposes raw CLI failure output only after path sanitization', async () => { diff --git a/src/main/text-generation/copilot-unavailable-model-retry.ts b/src/main/text-generation/failures/copilot-unavailable-model-retry.ts similarity index 86% rename from src/main/text-generation/copilot-unavailable-model-retry.ts rename to src/main/text-generation/failures/copilot-unavailable-model-retry.ts index 65038935df2..9248d36ba65 100644 --- a/src/main/text-generation/copilot-unavailable-model-retry.ts +++ b/src/main/text-generation/failures/copilot-unavailable-model-retry.ts @@ -1,6 +1,6 @@ -import { findOptionOccurrence } from '../../shared/command-option-occurrence' -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' -import { escapeRegex } from '../../shared/string-utils' +import { findOptionOccurrence } from '../../../shared/command-option-occurrence' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' +import { escapeRegex } from '../../../shared/string-utils' import type { AgentGenerationFailureOutput } from './agent-failure-output' const PROMPT_VALUE_OPTIONS = ['--prompt', '-p'] diff --git a/src/main/text-generation/kimi-generation-failure.test.ts b/src/main/text-generation/failures/kimi-generation-failure.test.ts similarity index 96% rename from src/main/text-generation/kimi-generation-failure.test.ts rename to src/main/text-generation/failures/kimi-generation-failure.test.ts index a888e6fee27..ab37b8665da 100644 --- a/src/main/text-generation/kimi-generation-failure.test.ts +++ b/src/main/text-generation/failures/kimi-generation-failure.test.ts @@ -3,13 +3,13 @@ import { realKimiPrOutput, realKimiOccupiedOutput, sourceShapedKimiOutput -} from '../../shared/__fixtures__/kimi-generation-source-shaped' -import type * as DnsDiagnosticModule from '../network/macos-tailscale-dns-diagnostic' +} from '../../../shared/__fixtures__/kimi-generation-source-shaped' +import type * as DnsDiagnosticModule from '../../network/macos-tailscale-dns-diagnostic' import { finalizeFromAgentOutput } from './source-control-agent-failure' const log = vi.spyOn(console, 'error').mockImplementation(() => {}) afterAll(() => log.mockRestore()) -vi.mock('../network/macos-tailscale-dns-diagnostic', async (original) => { +vi.mock('../../network/macos-tailscale-dns-diagnostic', async (original) => { const actual = await original() return { ...actual, diff --git a/src/main/text-generation/kimi-generation-failure.ts b/src/main/text-generation/failures/kimi-generation-failure.ts similarity index 96% rename from src/main/text-generation/kimi-generation-failure.ts rename to src/main/text-generation/failures/kimi-generation-failure.ts index fd106d13dad..1ea8da564d2 100644 --- a/src/main/text-generation/kimi-generation-failure.ts +++ b/src/main/text-generation/failures/kimi-generation-failure.ts @@ -1,9 +1,9 @@ -import { stripAnsiControlSequences } from '../../shared/source-control/ai/generation/commit-message-agent-output' -import { withMacTailscaleDnsHint } from '../network/macos-tailscale-dns-diagnostic' +import { stripAnsiControlSequences } from '../../../shared/source-control/ai/generation/commit-message-agent-output' +import { withMacTailscaleDnsHint } from '../../network/macos-tailscale-dns-diagnostic' import { textGenerationFailure, type TextGenerationFailureFields -} from '../../shared/text-generation-failure-copy' +} from '../../../shared/text-generation-failure-copy' function emptyInputFailure(input: string): TextGenerationFailureFields | null { switch (input) { diff --git a/src/main/text-generation/source-control-agent-failure.ts b/src/main/text-generation/failures/source-control-agent-failure.ts similarity index 91% rename from src/main/text-generation/source-control-agent-failure.ts rename to src/main/text-generation/failures/source-control-agent-failure.ts index 6fff7048b24..6e718d745dc 100644 --- a/src/main/text-generation/source-control-agent-failure.ts +++ b/src/main/text-generation/failures/source-control-agent-failure.ts @@ -2,8 +2,8 @@ import { cleanGeneratedText, excerptAgentFailureOutput, sanitizeAgentFailureDetail -} from '../../shared/source-control/ai/generation/commit-message-agent-output' -import { withMacTailscaleDnsHint } from '../network/macos-tailscale-dns-diagnostic' +} from '../../../shared/source-control/ai/generation/commit-message-agent-output' +import { withMacTailscaleDnsHint } from '../../network/macos-tailscale-dns-diagnostic' import { captureAgentGenerationFailureOutput, type AgentGenerationFailureOutput @@ -11,19 +11,19 @@ import { import type { InternalTextGenerationResult, TextGenerationOperation -} from './source-control-text-generation-types' -import { parseOpenCodeGenerationOutput } from '../../shared/opencode-generation-output' -import { parseAntigravityGenerationOutput } from '../../shared/antigravity-generation-stream' +} from '../source-control-text-generation-types' +import { parseOpenCodeGenerationOutput } from '../../../shared/opencode-generation-output' +import { parseAntigravityGenerationOutput } from '../../../shared/antigravity-generation-stream' import { findKimiGenerationFailure, parseKimiGenerationOutput -} from '../../shared/kimi-generation-output' +} from '../../../shared/kimi-generation-output' import { formatKimiGenerationFailure } from './kimi-generation-failure' -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' import { textGenerationFailure, type TextGenerationFailureFields -} from '../../shared/text-generation-failure-copy' +} from '../../../shared/text-generation-failure-copy' // Their stdout is a name or the user's own dictated words, not a diagnostic worth keeping in the error. const OPERATIONS_WITHOUT_STDOUT_DETAIL: ReadonlySet = new Set([ diff --git a/src/main/text-generation/codex-model-list-discovery.ts b/src/main/text-generation/models/codex-model-list-discovery.ts similarity index 88% rename from src/main/text-generation/codex-model-list-discovery.ts rename to src/main/text-generation/models/codex-model-list-discovery.ts index 688de29c597..022045816ef 100644 --- a/src/main/text-generation/codex-model-list-discovery.ts +++ b/src/main/text-generation/models/codex-model-list-discovery.ts @@ -1,17 +1,17 @@ import { StringDecoder } from 'node:string_decoder' import type { Writable } from 'node:stream' -import type { AgentSessionModelOption } from '../../shared/agent-session/agent-session-wire' -import type { CommitMessageModel } from '../../shared/source-control/ai/generation/commit-message-agent-spec' -import { createIncrementalNdjsonFramer } from '../../shared/main-process-ndjson-framer' -import { createCodexAppServerRecordDispatcher } from '../codex/app-server/codex-app-server-record-dispatch' -import { isProviderRecord } from '../provider-process/provider-json-record' -import { listCodexModelPages } from '../codex/structured/catalog/codex-structured-model-catalog' -import type { AgentModelProbeSpec } from '../../shared/agent-model-probe-spec' +import type { AgentSessionModelOption } from '../../../shared/agent-session/agent-session-wire' +import type { CommitMessageModel } from '../../../shared/source-control/ai/generation/commit-message-agent-spec' +import { createIncrementalNdjsonFramer } from '../../../shared/main-process-ndjson-framer' +import { createCodexAppServerRecordDispatcher } from '../../codex/app-server/codex-app-server-record-dispatch' +import { isProviderRecord } from '../../provider-process/provider-json-record' +import { listCodexModelPages } from '../../codex/structured/catalog/codex-structured-model-catalog' +import type { AgentModelProbeSpec } from '../../../shared/agent-model-probe-spec' import { discoveredModelsResult } from './commit-message-model-discovery-policy' import type { DiscoverCommitMessageModelsResult, SpawnedSourceControlAgentProcess -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' const APP_SERVER_EXIT_GRACE_MS = 1_500 diff --git a/src/main/text-generation/commit-message-model-discovery-policy.ts b/src/main/text-generation/models/commit-message-model-discovery-policy.ts similarity index 79% rename from src/main/text-generation/commit-message-model-discovery-policy.ts rename to src/main/text-generation/models/commit-message-model-discovery-policy.ts index 1c4f8d4d5fa..4b0881dce3a 100644 --- a/src/main/text-generation/commit-message-model-discovery-policy.ts +++ b/src/main/text-generation/models/commit-message-model-discovery-policy.ts @@ -1,12 +1,12 @@ -import { buildClaudeModelListArgs } from '../../shared/claude-model-list-probe' -import type { CommitMessageModel } from '../../shared/source-control/ai/generation/commit-message-agent-spec' -import { CODEX_SHORT_LIVED_PROBE_APP_SERVER_ARGS } from '../codex-cli/codex-read-only-app-server-args' -import type { CommandTemplateBackslash } from '../../shared/source-control/ai/generation/commit-message-prompt' -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' -import { planAgentBinary } from '../../shared/agent-launch/startup/agent-command-plan' -import type { AgentModelProbeSpec } from '../../shared/agent-model-probe-spec' -import { formatAgentCliFailureMessage } from './source-control-agent-failure' -import type { DiscoverCommitMessageModelsResult } from './source-control-text-generation-types' +import { buildClaudeModelListArgs } from '../../../shared/claude-model-list-probe' +import type { CommitMessageModel } from '../../../shared/source-control/ai/generation/commit-message-agent-spec' +import { CODEX_SHORT_LIVED_PROBE_APP_SERVER_ARGS } from '../../codex-cli/codex-read-only-app-server-args' +import type { CommandTemplateBackslash } from '../../../shared/source-control/ai/generation/commit-message-prompt' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' +import { planAgentBinary } from '../../../shared/agent-launch/startup/agent-command-plan' +import type { AgentModelProbeSpec } from '../../../shared/agent-model-probe-spec' +import { formatAgentCliFailureMessage } from '../failures/source-control-agent-failure' +import type { DiscoverCommitMessageModelsResult } from '../source-control-text-generation-types' export function staticModelDiscoveryResult( spec: AgentModelProbeSpec, diff --git a/src/main/text-generation/commit-message-model-discovery.ts b/src/main/text-generation/models/commit-message-model-discovery.ts similarity index 89% rename from src/main/text-generation/commit-message-model-discovery.ts rename to src/main/text-generation/models/commit-message-model-discovery.ts index 636320651e9..bac340513a0 100644 --- a/src/main/text-generation/commit-message-model-discovery.ts +++ b/src/main/text-generation/models/commit-message-model-discovery.ts @@ -1,18 +1,18 @@ -import { isMissingProviderExecutable } from '../provider-process/provider-executable-missing' -import { mergeCommandEnvironment } from '../../shared/command-environment' -import type { CommandTemplateBackslash } from '../../shared/source-control/ai/generation/commit-message-prompt' +import { isMissingProviderExecutable } from '../../provider-process/provider-executable-missing' +import { mergeCommandEnvironment } from '../../../shared/command-environment' +import type { CommandTemplateBackslash } from '../../../shared/source-control/ai/generation/commit-message-prompt' import { getAgentModelProbeSpec, type AgentModelProbeSpec -} from '../../shared/agent-model-probe-spec' -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' +} from '../../../shared/agent-model-probe-spec' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' import { runClaudeBackgroundAttempts, type ClaudeBackgroundFailedExit -} from '../../shared/claude-background-run-isolation' -import type { TuiAgent } from '../../shared/agent-launch/catalog/tui-agent' -import { resolveCodexHomeProcessLockKeyForSpawnEnv } from '../codex-cli/codex-home-process-lock' -import { supervisedProviderSpawnFailure } from '../provider-process/provider-spawn-failure-report' +} from '../../../shared/claude-background-run-isolation' +import type { TuiAgent } from '../../../shared/agent-launch/catalog/tui-agent' +import { resolveCodexHomeProcessLockKeyForSpawnEnv } from '../../codex-cli/codex-home-process-lock' +import { supervisedProviderSpawnFailure } from '../../provider-process/provider-spawn-failure-report' import { finalizeModelDiscoveryOutput, planModelDiscovery, @@ -21,21 +21,21 @@ import { import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES, SOURCE_CONTROL_GENERATION_TIMEOUT_MS -} from './source-control-generation-limits' -import { runCodexProcessWithHomeLock } from './source-control-local-generation' -import { formatAgentCliFailureMessage } from './source-control-agent-failure' +} from '../execution/source-control-generation-limits' +import { runCodexProcessWithHomeLock } from '../execution/source-control-local-generation' +import { formatAgentCliFailureMessage } from '../failures/source-control-agent-failure' import { settleCodexModelListing, startCodexModelListDiscovery, type CodexModelListDiscovery } from './codex-model-list-discovery' -import { killSourceControlAgentProcess } from './source-control-local-process' +import { killSourceControlAgentProcess } from '../execution/source-control-agent-termination' import type { DiscoverCommitMessageModelsResult, LocalProcessExecution, SpawnedSourceControlAgentProcess, SpawnSourceControlAgent -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' export type CommitMessageModelDiscoveryLocalOptions = { cwd?: string diff --git a/src/main/text-generation/commit-message-text-generation-model-discovery.test.ts b/src/main/text-generation/models/commit-message-text-generation-model-discovery.test.ts similarity index 88% rename from src/main/text-generation/commit-message-text-generation-model-discovery.test.ts rename to src/main/text-generation/models/commit-message-text-generation-model-discovery.test.ts index e90ec3ca2a4..05f7a410b7a 100644 --- a/src/main/text-generation/commit-message-text-generation-model-discovery.test.ts +++ b/src/main/text-generation/models/commit-message-text-generation-model-discovery.test.ts @@ -1,23 +1,23 @@ import { spawn } from 'node:child_process' import type * as ChildProcess from 'node:child_process' -import type * as CliCommandResolution from '../../shared/node-cli-command-resolution' +import type * as CliCommandResolution from '../../../shared/node-cli-command-resolution' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { discoverCommitMessageModelsLocal } from './commit-message-text-generation' +import { discoverCommitMessageModelsLocal } from '../commit-message-text-generation' import { answerCodexAppServer, createMockDiscoveryChild -} from './commit-message-text-generation-test-harness' +} from '../execution/commit-message-text-generation-test-harness' const { terminateWindowsProcessTreeMock } = vi.hoisted(() => ({ terminateWindowsProcessTreeMock: vi.fn(async () => {}) })) -vi.mock('../pty/process-retirement/windows-process-tree-kill', () => ({ +vi.mock('../../pty/process-retirement/windows-process-tree-kill', () => ({ terminateWindowsProcessTree: terminateWindowsProcessTreeMock })) // These protocol fixtures use fake processes; installed host CLIs belong to the CLI-path suite. -vi.mock('../../shared/node-cli-command-resolution', async (importOriginal) => ({ +vi.mock('../../../shared/node-cli-command-resolution', async (importOriginal) => ({ ...(await importOriginal()), resolveCliCommand: (command: string) => command })) diff --git a/src/main/text-generation/model-discovery-host-cache.ts b/src/main/text-generation/models/model-discovery-host-cache.ts similarity index 84% rename from src/main/text-generation/model-discovery-host-cache.ts rename to src/main/text-generation/models/model-discovery-host-cache.ts index 5f8bbc55ef9..bd3659245c2 100644 --- a/src/main/text-generation/model-discovery-host-cache.ts +++ b/src/main/text-generation/models/model-discovery-host-cache.ts @@ -1,11 +1,11 @@ -import type { TuiAgent } from '../../shared/agent-launch/catalog/tui-agent' -import { InFlightPromiseDedupe, stableInFlightKey } from '../../shared/in-flight-promise-dedupe' -import { setBoundedScopeCacheEntry } from '../../shared/native-chat/native-chat-scope-cache' -import { PRIMARY_AGENT_DIR_ENV_BY_KIND } from '../../shared/pi-agent-kind' -import { onAgentsRefreshedOnHost } from '../preflight/agents-refreshed-on-host' +import type { TuiAgent } from '../../../shared/agent-launch/catalog/tui-agent' +import { InFlightPromiseDedupe, stableInFlightKey } from '../../../shared/in-flight-promise-dedupe' +import { setBoundedScopeCacheEntry } from '../../../shared/native-chat/native-chat-scope-cache' +import { PRIMARY_AGENT_DIR_ENV_BY_KIND } from '../../../shared/pi-agent-kind' +import { onAgentsRefreshedOnHost } from '../../preflight/agents-refreshed-on-host' import type { CommitMessageModelDiscoveryLocalOptions } from './commit-message-model-discovery' -import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from './source-control-generation-limits' -import type { DiscoverCommitMessageModelsResult } from './source-control-text-generation-types' +import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from '../execution/source-control-generation-limits' +import type { DiscoverCommitMessageModelsResult } from '../source-control-text-generation-types' // Pickers list these on open; Codex's listing starts an app-server. Each maps to the config-dir var // that picks its login (mirrors prepareShellConfigDirEnv). diff --git a/src/main/text-generation/source-control-kimi-policy.ts b/src/main/text-generation/policies/source-control-kimi-policy.ts similarity index 86% rename from src/main/text-generation/source-control-kimi-policy.ts rename to src/main/text-generation/policies/source-control-kimi-policy.ts index 8f76561d51e..c64f2d18f96 100644 --- a/src/main/text-generation/source-control-kimi-policy.ts +++ b/src/main/text-generation/policies/source-control-kimi-policy.ts @@ -1,11 +1,11 @@ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml' import { z } from 'zod' -import { findOptionOccurrence } from '../../shared/command-option-occurrence' +import { findOptionOccurrence } from '../../../shared/command-option-occurrence' import { compactRecipeOptions, KIMI_VALUE_OPTIONS -} from '../../shared/source-control/ai/generation/commit-message-generation-options' -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' +} from '../../../shared/source-control/ai/generation/commit-message-generation-options' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' const object = z.record(z.string(), z.unknown()) export async function prepareKimiTextTaskPolicy( diff --git a/src/main/text-generation/source-control-policy-probe.ts b/src/main/text-generation/policies/source-control-policy-probe.ts similarity index 87% rename from src/main/text-generation/source-control-policy-probe.ts rename to src/main/text-generation/policies/source-control-policy-probe.ts index bc2380d953a..137fc7a7743 100644 --- a/src/main/text-generation/source-control-policy-probe.ts +++ b/src/main/text-generation/policies/source-control-policy-probe.ts @@ -1,13 +1,13 @@ -import { abortSignalReason } from '../../shared/abort-signal-reason' -import { mergeCommandEnvironment } from '../../shared/command-environment' -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' -import { killSourceControlAgentProcess } from './source-control-agent-termination' -import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES } from './source-control-generation-limits' +import { abortSignalReason } from '../../../shared/abort-signal-reason' +import { mergeCommandEnvironment } from '../../../shared/command-environment' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' +import { killSourceControlAgentProcess } from '../execution/source-control-agent-termination' +import { MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES } from '../execution/source-control-generation-limits' import type { LocalGenerationTarget, LocalProcessExecution, SpawnSourceControlAgent -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' /** Effective configuration may contain credentials; keep both streams private even on failure. */ export function runSourceControlPolicyProbe(input: { diff --git a/src/main/text-generation/source-control-read-only-policy.test.ts b/src/main/text-generation/policies/source-control-read-only-policy.test.ts similarity index 95% rename from src/main/text-generation/source-control-read-only-policy.test.ts rename to src/main/text-generation/policies/source-control-read-only-policy.test.ts index 0f14626cb77..8cdf82b978d 100644 --- a/src/main/text-generation/source-control-read-only-policy.test.ts +++ b/src/main/text-generation/policies/source-control-read-only-policy.test.ts @@ -3,11 +3,11 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { parse as parseYaml } from 'yaml' import { afterEach, expect, it, vi } from 'vitest' -import { planCommitMessageGeneration } from '../../shared/source-control/ai/generation/commit-message-plan' +import { planCommitMessageGeneration } from '../../../shared/source-control/ai/generation/commit-message-plan' import { prepareSourceControlReadOnlyPlan } from './source-control-read-only-policy' -import { stageLocalPromptFiles } from './source-control-prompt-files' -import './source-control-local-generation' -import './source-control-generation-lanes' +import { stageLocalPromptFiles } from '../prompts/source-control-prompt-files' +import '../execution/source-control-local-generation' +import '../execution/source-control-generation-lanes' const { probe } = vi.hoisted(() => ({ probe: vi.fn() })) vi.mock('./source-control-policy-probe', () => ({ runSourceControlPolicyProbe: probe })) diff --git a/src/main/text-generation/source-control-read-only-policy.ts b/src/main/text-generation/policies/source-control-read-only-policy.ts similarity index 92% rename from src/main/text-generation/source-control-read-only-policy.ts rename to src/main/text-generation/policies/source-control-read-only-policy.ts index 498f1069dc6..51231ac289a 100644 --- a/src/main/text-generation/source-control-read-only-policy.ts +++ b/src/main/text-generation/policies/source-control-read-only-policy.ts @@ -4,23 +4,23 @@ import path from 'node:path' import { prepareKimiTextTaskPolicy } from './source-control-kimi-policy' import { parse as parseJson, type ParseError } from 'jsonc-parser' import { z } from 'zod' -import { findOptionOccurrence } from '../../shared/command-option-occurrence' +import { findOptionOccurrence } from '../../../shared/command-option-occurrence' import { compactRecipeOptions, KIMI_VALUE_OPTIONS -} from '../../shared/source-control/ai/generation/commit-message-generation-options' -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' -import { parseWslUncPath, wslDistroForCommand } from '../../shared/wsl-paths' -import { CODEX_DISABLE_PLUGINS_ARGS } from '../codex-cli/codex-read-only-app-server-args' -import { runWslProcess } from '../wsl/execution/wsl-runner' -import { TEXT_TASK_DIRECTORY } from './source-control-prompt-files' +} from '../../../shared/source-control/ai/generation/commit-message-generation-options' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' +import { parseWslUncPath, wslDistroForCommand } from '../../../shared/wsl-paths' +import { CODEX_DISABLE_PLUGINS_ARGS } from '../../codex-cli/codex-read-only-app-server-args' +import { runWslProcess } from '../../wsl/execution/wsl-runner' +import { TEXT_TASK_DIRECTORY } from '../prompts/source-control-prompt-files' import { runSourceControlPolicyProbe } from './source-control-policy-probe' import type { LocalGenerationTarget, LocalProcessExecution, SpawnSourceControlAgent, TextGenerationOperation -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' const object = z.record(z.string(), z.unknown()) const strings = z.array(z.string()) diff --git a/src/main/text-generation/source-control-omp-prompt-file.test.ts b/src/main/text-generation/prompts/source-control-omp-prompt-file.test.ts similarity index 93% rename from src/main/text-generation/source-control-omp-prompt-file.test.ts rename to src/main/text-generation/prompts/source-control-omp-prompt-file.test.ts index 871c5d91dbb..0b5dfe6f972 100644 --- a/src/main/text-generation/source-control-omp-prompt-file.test.ts +++ b/src/main/text-generation/prompts/source-control-omp-prompt-file.test.ts @@ -1,14 +1,14 @@ import { spawnProcess } from '@orca/process-host' -import * as agentSpecs from '../../shared/source-control/ai/generation/commit-message-agent-spec' -import type { ResolvedSourceControlAiGenerationParams } from '../../shared/source-control/ai/source-control-ai-operation-resolution' -import type { SpawnSourceControlAgent } from './source-control-text-generation-types' +import * as agentSpecs from '../../../shared/source-control/ai/generation/commit-message-agent-spec' +import type { ResolvedSourceControlAiGenerationParams } from '../../../shared/source-control/ai/source-control-ai-operation-resolution' +import type { SpawnSourceControlAgent } from '../source-control-text-generation-types' import { generateCommitMessage, generatePullRequestFields, generateBranchName -} from './source-control-text-generation-requests' -import { generateConversationName } from './conversation-name-generation-request' -import { generateJiraIssueSummaryFromContext } from './jira-issue-summary-text-generation' +} from '../requests/source-control-text-generation-requests' +import { generateConversationName } from '../requests/conversation-name-generation-request' +import { generateJiraIssueSummaryFromContext } from '../requests/jira-issue-summary-text-generation' import { existsSync, mkdtempSync, readFileSync } from 'node:fs' import { rm } from 'node:fs/promises' @@ -16,16 +16,16 @@ import type * as FsPromises from 'node:fs/promises' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { afterEach, expect, it, vi } from 'vitest' -import { planCommitMessageGeneration } from '../../shared/source-control/ai/generation/commit-message-plan' -import { cancelLocalGeneration } from './source-control-generation-lanes' -import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from './source-control-generation-limits' -import { runLocalSourceControlPlan } from './source-control-local-process' +import { planCommitMessageGeneration } from '../../../shared/source-control/ai/generation/commit-message-plan' +import { cancelLocalGeneration } from '../execution/source-control-generation-lanes' +import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from '../execution/source-control-generation-limits' +import { runLocalSourceControlPlan } from '../execution/source-control-local-process' import { createMockDiscoveryChild, withPlatform -} from './commit-message-text-generation-test-harness' +} from '../execution/commit-message-text-generation-test-harness' -vi.mock('../pty/process-retirement/windows-process-tree-kill', () => ({ +vi.mock('../../pty/process-retirement/windows-process-tree-kill', () => ({ terminateWindowsProcessTree: vi.fn(async () => true) })) diff --git a/src/main/text-generation/source-control-prompt-files.ts b/src/main/text-generation/prompts/source-control-prompt-files.ts similarity index 95% rename from src/main/text-generation/source-control-prompt-files.ts rename to src/main/text-generation/prompts/source-control-prompt-files.ts index 9a419bd23a4..a32f506d03d 100644 --- a/src/main/text-generation/source-control-prompt-files.ts +++ b/src/main/text-generation/prompts/source-control-prompt-files.ts @@ -2,10 +2,10 @@ import { randomUUID } from 'node:crypto' import { mkdtempSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { getUtf8ChunkEndIndex } from '../../shared/utf8-byte-limits' -import { encodePosixPrintfRecords } from '../../shared/posix-printf-encoding' -import { toWindowsWslUncPath } from '../../shared/wsl-paths' -import type { CommitMessagePlan } from '../../shared/source-control/ai/generation/commit-message-plan' +import { getUtf8ChunkEndIndex } from '../../../shared/utf8-byte-limits' +import { encodePosixPrintfRecords } from '../../../shared/posix-printf-encoding' +import { toWindowsWslUncPath } from '../../../shared/wsl-paths' +import type { CommitMessagePlan } from '../../../shared/source-control/ai/generation/commit-message-plan' // Reserve the transport signature under the native text attachment cap. const PAYLOAD_BYTES = 5 * 1024 * 1024 - 3 diff --git a/src/main/text-generation/source-control-prompt-producer.ts b/src/main/text-generation/prompts/source-control-prompt-producer.ts similarity index 100% rename from src/main/text-generation/source-control-prompt-producer.ts rename to src/main/text-generation/prompts/source-control-prompt-producer.ts diff --git a/src/main/text-generation/source-control-wsl-prompt.test.ts b/src/main/text-generation/prompts/source-control-wsl-prompt.test.ts similarity index 74% rename from src/main/text-generation/source-control-wsl-prompt.test.ts rename to src/main/text-generation/prompts/source-control-wsl-prompt.test.ts index 87c5a211844..45134cdd5e5 100644 --- a/src/main/text-generation/source-control-wsl-prompt.test.ts +++ b/src/main/text-generation/prompts/source-control-wsl-prompt.test.ts @@ -2,33 +2,30 @@ import { spawnProcess } from '@orca/process-host' import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' import { rm } from 'node:fs/promises' import { join } from 'node:path' -import { Writable } from 'node:stream' import { afterEach, expect, it, vi } from 'vitest' -import { planCommitMessageGeneration } from '../../shared/source-control/ai/generation/commit-message-plan' +import { planCommitMessageGeneration } from '../../../shared/source-control/ai/generation/commit-message-plan' import { prepareWslPrompt } from './source-control-prompt-files' -import { producePromptRecords } from './source-control-prompt-producer' -import { runLocalSourceControlPlan } from './source-control-local-process' -import { cancelLocalGeneration } from './source-control-generation-lanes' -import { - MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES, - SOURCE_CONTROL_GENERATION_TIMEOUT_MS -} from './source-control-generation-limits' +import { runLocalSourceControlPlan } from '../execution/source-control-local-process' +import { cancelLocalGeneration } from '../execution/source-control-generation-lanes' +import { SOURCE_CONTROL_GENERATION_TIMEOUT_MS } from '../execution/source-control-generation-limits' import { withPlatform, createMockDiscoveryChild -} from './commit-message-text-generation-test-harness' -import type { SourceControlAgentSpawnInput } from './source-control-text-generation-types' -import type * as Removal from '../../shared/windows-transient-lock-removal' +} from '../execution/commit-message-text-generation-test-harness' +import type { SourceControlAgentSpawnInput } from '../source-control-text-generation-types' +import type * as Removal from '../../../shared/windows-transient-lock-removal' const { removeTreeMock } = vi.hoisted(() => ({ removeTreeMock: vi.fn(async () => {}) })) -vi.mock('../../shared/windows-transient-lock-removal', async (original) => ({ +vi.mock('../../../shared/windows-transient-lock-removal', async (original) => ({ ...(await original()), removeTree: removeTreeMock })) -vi.mock('../pty/process-retirement/windows-process-tree-kill', () => ({ +vi.mock('../../pty/process-retirement/windows-process-tree-kill', () => ({ terminateWindowsProcessTree: vi.fn(async () => true) })) -vi.mock('../wsl/discovery/wsl-background-distro-gate', () => ({ noteWslDistroLaunched: vi.fn() })) +vi.mock('../../wsl/discovery/wsl-background-distro-gate', () => ({ + noteWslDistroLaunched: vi.fn() +})) afterEach(() => { vi.useRealTimers() @@ -95,77 +92,6 @@ it('waits for fragmented exact proof, streams once and cleans only the actual gu expect(removeTreeMock).toHaveBeenCalledTimes(1) }) -it.each(['cancel', 'deadline', 'exit', 'overflow', 'stdin-error', 'stdin-close'] as const)( - 'stops pending input on %s, and late proof only cleans without reopening result', - async (route) => { - vi.useFakeTimers() - const { child, execution, proof } = start() - const writes = vi.spyOn(child.stdin, 'write') - if (route === 'cancel') { - cancelLocalGeneration('conversation-name', cwd) - } - if (route === 'deadline') { - await vi.advanceTimersByTimeAsync(SOURCE_CONTROL_GENERATION_TIMEOUT_MS) - } - if (route === 'exit') { - close(child) - } - if (route === 'overflow') { - child.stdout.emit('data', Buffer.alloc(MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES + 1)) - } - if (route === 'stdin-error') { - child.stdin.emit('error', new Error('EPIPE')) - } - if (route === 'stdin-close') { - child.stdin.emit('close') - } - child.stderr.emit('data', Buffer.from(proof)) - expect(writes).not.toHaveBeenCalled() - close(child) - await execution.result - await execution.processClosed - expect(removeTreeMock).toHaveBeenCalledTimes(1) - child.stdin.emit('error', new Error('late EPIPE')) - } -) - -it.each(['cancel', 'deadline', 'exit', 'error', 'close', 'overflow'] as const)( - 'releases a never-draining producer on %s without final authorization', - async (route) => { - vi.useFakeTimers() - const { child, execution, proof, nonce } = start(undefined, 'x'.repeat(50_000)) - const write = vi.spyOn(child.stdin, 'write').mockReturnValue(false) - child.stderr.emit('data', Buffer.from(proof)) - expect(write).toHaveBeenCalledExactlyOnceWith(`A${nonce}\n`) - if (route === 'cancel') { - cancelLocalGeneration('conversation-name', cwd) - } - if (route === 'deadline') { - await vi.advanceTimersByTimeAsync(SOURCE_CONTROL_GENERATION_TIMEOUT_MS) - } - if (route === 'exit') { - child.emit('exit', 1) - } - if (route === 'error') { - child.stdin.emit('error', new Error('EPIPE')) - } - if (route === 'close') { - child.stdin.emit('close') - } - if (route === 'overflow') { - child.stderr.emit('data', Buffer.alloc(MAX_SOURCE_CONTROL_AGENT_OUTPUT_BYTES + 1)) - } - await Promise.resolve() - child.stdin.emit('drain') - close(child, 1) - await execution.result - await execution.processClosed - expect(write).toHaveBeenCalledTimes(1) - expect(child.stdin.listenerCount('drain')).toBe(0) - expect(removeTreeMock).toHaveBeenCalledTimes(1) - } -) - it.each(['missing-proof', 'spawn', 'no-pid', 'missing-stdin'] as const)( 'settles %s without invented ownership', async (route) => { @@ -364,57 +290,6 @@ it.each([ } }) -it.each([false, true])( - 'bounds real producer work at N and 10N (backpressure=%s)', - async (backpressure) => { - const results: number[] = [] - for (const size of [20_000, 200_000]) { - const guest = prepareWslPrompt( - plan(tricky.repeat(Math.ceil(size / tricky.length)).slice(0, size)), - 'Actual' - ) - let writes = 0 - let maxRecord = 0 - let pending = 0 - let maxPending = 0 - let maxQueued = 0 - const stream = new Writable({ - highWaterMark: 2048, - write(chunk, _encoding, callback) { - writes++ - maxRecord = Math.max(maxRecord, chunk.length) - maxPending = Math.max(maxPending, ++pending) - const complete = () => { - maxQueued = Math.max(maxQueued, stream.writableLength) - pending-- - callback() - } - if (backpressure) { - setImmediate(complete) - } else { - complete() - } - } - }) - let yieldedAt = 0 - setImmediate(() => { - yieldedAt = writes - }) - const finished = new Promise((resolve) => stream.once('finish', resolve)) - await producePromptRecords(stream, guest.records(), new AbortController().signal, () => true) - await finished - expect(maxQueued).toBeLessThanOrEqual(2048 + 1026) - expect(yieldedAt).toBeGreaterThan(0) - expect(yieldedAt).toBeLessThan(writes) - expect(maxRecord).toBeLessThanOrEqual(1026) - expect(maxPending).toBe(1) - results.push(writes) - } - expect(results[1]).toBeLessThanOrEqual(results[0] * 10 + 10) - expect(results[1]).toBeGreaterThan(results[0] * 8) - } -) - it('fails a real post-proof storage redirect without running a partial request', async () => { const result = await shellFixture('private data', undefined, undefined, (directory) => mkdirSync(join(directory, 'prompt-1.txt')) diff --git a/src/main/text-generation/pull-request-context.test.ts b/src/main/text-generation/pull-request-context.test.ts deleted file mode 100644 index cce11523519..00000000000 --- a/src/main/text-generation/pull-request-context.test.ts +++ /dev/null @@ -1,482 +0,0 @@ -// Keep remote-state command-order cases together so Git regressions stay reviewable. -import { afterEach, describe, expect, it, vi } from 'vitest' -import { getPullRequestDraftContext } from './pull-request-context' - -type GitExec = Parameters[0] - -afterEach(() => { - vi.restoreAllMocks() -}) - -function createContextInput(base = 'main') { - return { - base, - currentTitle: 'Existing title', - currentBody: 'Existing body', - currentDraft: false - } -} - -describe('getPullRequestDraftContext', () => { - it('fetches the resolved remote base before collecting PR context without mutating HEAD', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'fetch') { - return { stdout: '', stderr: '' } - } - if (args[0] === 'remote') { - return { stdout: 'origin\nupstream\n', stderr: '' } - } - if (args[0] === 'show-ref') { - return { - stdout: 'abc refs/remotes/origin/main\n' + 'def refs/remotes/upstream/main\n', - stderr: '' - } - } - if (args[0] === 'branch') { - return { stdout: 'feature/pr-details\n', stderr: '' } - } - if (args[0] === 'merge-base') { - return { stdout: 'abc123\n', stderr: '' } - } - if (args[0] === 'log') { - return { stdout: '- feat: summarize branch\n', stderr: '' } - } - if (args[0] === 'diff' && args[1] === '--name-status') { - return { stdout: 'M\tsrc/file.ts\n', stderr: '' } - } - if (args[0] === 'diff') { - return { stdout: 'diff --git a/src/file.ts b/src/file.ts\n+change\n', stderr: '' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - const context = await getPullRequestDraftContext(execGit, createContextInput()) - - expect(context).toMatchObject({ - branch: 'feature/pr-details', - base: 'main', - branchChangedByPreparation: false, - commitSummary: '- feat: summarize branch', - changeSummary: 'M\tsrc/file.ts' - }) - expect(execGit).toHaveBeenCalledWith( - ['fetch', '--no-tags', 'origin', '+refs/heads/main:refs/remotes/origin/main'], - expect.any(Object) - ) - expect(execGit).toHaveBeenCalledWith( - ['show-ref', '--verify', '--quiet', '--', 'refs/remotes/origin/main'], - expect.objectContaining({ maxBuffer: 10 * 1024 * 1024 }) - ) - expect(execGit).toHaveBeenCalledWith( - ['show-ref', '--verify', '--quiet', '--', 'refs/remotes/upstream/main'], - expect.objectContaining({ maxBuffer: 10 * 1024 * 1024 }) - ) - const exactRefCalls = execGit.mock.calls.filter(([args]) => args[0] === 'show-ref') - expect(exactRefCalls).toHaveLength(2) - expect(exactRefCalls.every(([, options]) => options?.timeoutMs === undefined)).toBe(true) - expect(execGit).not.toHaveBeenCalledWith(expect.arrayContaining(['rebase']), expect.anything()) - expect(execGit).not.toHaveBeenCalledWith( - expect.arrayContaining(['rev-parse']), - expect.anything() - ) - expect(execGit).toHaveBeenCalledWith(['merge-base', 'origin/main', 'HEAD'], expect.any(Object)) - - const commandNames = execGit.mock.calls.map(([args]) => args[0]) - expect(commandNames.indexOf('fetch')).toBeLessThan(commandNames.indexOf('merge-base')) - }) - - it('fetches a configured preferred remote without a suffix scan when its ref is absent', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'fetch') { - return { stdout: '', stderr: '' } - } - if (args[0] === 'remote') { - return { stdout: 'origin\n', stderr: '' } - } - if (args[0] === 'show-ref') { - throw Object.assign(new Error('missing exact ref'), { code: 1 }) - } - if (args[0] === 'branch') { - return { stdout: 'feature/pr-details\n', stderr: '' } - } - if (args[0] === 'merge-base') { - return { stdout: 'abc123\n', stderr: '' } - } - if (args[0] === 'log') { - return { stdout: '- feat: summarize branch\n', stderr: '' } - } - if (args[0] === 'diff') { - return { stdout: 'M\tREADME.md\n', stderr: '' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await getPullRequestDraftContext(execGit, createContextInput()) - - expect(execGit).toHaveBeenCalledWith( - ['fetch', '--no-tags', 'origin', '+refs/heads/main:refs/remotes/origin/main'], - expect.any(Object) - ) - expect(execGit.mock.calls.some(([args]) => args.join(' ') === 'show-ref -- main')).toBe(false) - expect(execGit).not.toHaveBeenCalledWith(expect.arrayContaining(['rebase']), expect.anything()) - }) - - it('does not guess between multiple non-preferred remote bases for a bare base name', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'fetch') { - throw new Error(`Unexpected fetch: ${args.join(' ')}`) - } - if (args[0] === 'remote') { - return { stdout: 'contributor-a\ncontributor-b\n', stderr: '' } - } - if (args[0] === 'for-each-ref') { - return { stdout: '', stderr: '' } - } - if (args[0] === 'show-ref') { - throw Object.assign(new Error('missing exact ref'), { code: 1 }) - } - if (args[0] === 'branch') { - return { stdout: 'feature\n', stderr: '' } - } - if (args[0] === 'merge-base') { - expect(args[1]).toBe('main') - return { stdout: 'abc123\n', stderr: '' } - } - if (args[0] === 'log') { - return { stdout: '- feat: change\n', stderr: '' } - } - if (args[0] === 'diff') { - return { stdout: 'M\tREADME.md\n', stderr: '' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await getPullRequestDraftContext(execGit, createContextInput()) - - const showRefCalls = execGit.mock.calls.filter(([args]) => args[0] === 'show-ref') - expect(showRefCalls.some(([args]) => args.includes('--verify'))).toBe(false) - const exactProbes = execGit.mock.calls.filter(([args]) => args[0] === 'for-each-ref') - expect(exactProbes).toHaveLength(1) - expect(exactProbes[0][0]).toEqual([ - 'for-each-ref', - '--format=%(refname)', - '--', - 'refs/remotes/origin/main', - 'refs/remotes/upstream/main', - 'refs/remotes/contributor-a/main', - 'refs/remotes/contributor-b/main' - ]) - expect(showRefCalls.some(([args]) => args.join(' ') === 'show-ref -- main')).toBe(true) - - expect(execGit).not.toHaveBeenCalledWith( - expect.arrayContaining(['contributor-a']), - expect.any(Object) - ) - expect(execGit).not.toHaveBeenCalledWith( - expect.arrayContaining(['contributor-b']), - expect.any(Object) - ) - expect(execGit).not.toHaveBeenCalledWith(expect.arrayContaining(['rebase']), expect.anything()) - }) - - it('does not choose another remote when an exact probe is inconclusive', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'remote') { - return { stdout: 'first\nsecond\n' } - } - if (args[0] === 'show-ref') { - throw Object.assign(new Error('remote probe failed'), { code: 128 }) - } - if (args[0] === 'fetch') { - throw new Error(`Unexpected fetch: ${args.join(' ')}`) - } - if (args[0] === 'branch') { - return { stdout: 'feature\n' } - } - if (args[0] === 'merge-base') { - expect(args[1]).toBe('main') - return { stdout: 'abc123\n' } - } - if (args[0] === 'log' || args[0] === 'diff') { - return { stdout: 'change\n' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await expect(getPullRequestDraftContext(execGit, createContextInput())).resolves.toMatchObject({ - branch: 'feature' - }) - expect(execGit).not.toHaveBeenCalledWith(expect.arrayContaining(['fetch']), expect.any(Object)) - }) - - it('resolves a unique slash-containing remote with an exact probe', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'remote') { - return { stdout: 'foo/bar\n', stderr: '' } - } - if (args[0] === 'for-each-ref') { - return { stdout: 'refs/remotes/foo/bar/feature/fix\n' } - } - if (args[0] === 'fetch') { - return { stdout: '', stderr: '' } - } - if (args[0] === 'branch') { - return { stdout: 'feature\n' } - } - if (args[0] === 'merge-base') { - expect(args[1]).toBe('foo/bar/feature/fix') - return { stdout: 'abc123\n', stderr: '' } - } - if (args[0] === 'log' || args[0] === 'diff') { - return { stdout: 'change\n', stderr: '' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await expect( - getPullRequestDraftContext(execGit, createContextInput('feature/fix')) - ).resolves.toMatchObject({ branchChangedByPreparation: false }) - - expect(execGit).toHaveBeenCalledWith( - ['fetch', '--no-tags', 'foo/bar', '+refs/heads/feature/fix:refs/remotes/foo/bar/feature/fix'], - expect.any(Object) - ) - }) - - it('preserves Git-valid punctuation in configured remote names', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'remote') { - return { stdout: 'team@corp\n' } - } - if (args[0] === 'show-ref') { - return args.at(-1) === 'refs/remotes/team@corp/main' - ? { stdout: '' } - : Promise.reject(Object.assign(new Error('missing exact ref'), { code: 1 })) - } - if (args[0] === 'fetch' || args[0] === 'branch') { - return { stdout: '' } - } - if (args[0] === 'merge-base') { - expect(args[1]).toBe('team@corp/main') - return { stdout: 'abc123\n' } - } - if (args[0] === 'log' || args[0] === 'diff') { - return { stdout: 'change\n' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await getPullRequestDraftContext(execGit, createContextInput()) - - expect(execGit).toHaveBeenCalledWith( - ['fetch', '--no-tags', 'team@corp', '+refs/heads/main:refs/remotes/team@corp/main'], - expect.any(Object) - ) - }) - - it('resolves a unique unconfigured remote suffix with a bounded show-ref fallback', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'remote') { - return { stdout: 'fork\n' } - } - if (args[0] === 'show-ref' && args.some((arg) => arg.startsWith('refs/remotes/'))) { - throw Object.assign(new Error('missing exact ref'), { code: 1 }) - } - if (args[0] === 'show-ref') { - return { stdout: 'abc123 refs/remotes/orphan/main\n' } - } - if (args[0] === 'fetch') { - return { stdout: '', stderr: '' } - } - if (args[0] === 'branch') { - return { stdout: 'feature\n' } - } - if (args[0] === 'merge-base') { - expect(args[1]).toBe('orphan/main') - return { stdout: 'abc123\n' } - } - if (args[0] === 'log' || args[0] === 'diff') { - return { stdout: 'change\n' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await expect(getPullRequestDraftContext(execGit, createContextInput())).resolves.toMatchObject({ - branch: 'feature' - }) - - expect(execGit).toHaveBeenCalledWith(['show-ref', '--', 'main'], expect.any(Object)) - expect(execGit).toHaveBeenCalledWith( - ['show-ref', '--', 'main'], - expect.objectContaining({ maxBuffer: 10 * 1024 * 1024 }) - ) - expect(execGit).toHaveBeenCalledWith( - ['fetch', '--no-tags', 'orphan', '+refs/heads/main:refs/remotes/orphan/main'], - expect.any(Object) - ) - }) - - it('does not guess when the suffix fallback finds multiple remotes', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'remote') { - return { stdout: 'fork\n' } - } - if (args[0] === 'show-ref' && args.some((arg) => arg.startsWith('refs/remotes/'))) { - throw Object.assign(new Error('missing exact ref'), { code: 1 }) - } - if (args[0] === 'show-ref') { - return { - stdout: 'abc123 refs/remotes/orphan-a/main\nabc123 refs/remotes/orphan-b/main\n' - } - } - if (args[0] === 'fetch') { - throw new Error(`Unexpected fetch: ${args.join(' ')}`) - } - if (args[0] === 'branch') { - return { stdout: 'feature\n' } - } - if (args[0] === 'merge-base') { - expect(args[1]).toBe('main') - return { stdout: 'abc123\n' } - } - if (args[0] === 'log' || args[0] === 'diff') { - return { stdout: 'change\n' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await expect(getPullRequestDraftContext(execGit, createContextInput())).resolves.toMatchObject({ - branch: 'feature' - }) - expect(execGit).not.toHaveBeenCalledWith(expect.arrayContaining(['fetch']), expect.any(Object)) - }) - - it('treats a suffix fallback overflow or transport error as no match', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'remote') { - return { stdout: 'fork\n' } - } - if (args[0] === 'show-ref') { - if (args.some((arg) => arg.startsWith('refs/remotes/'))) { - throw Object.assign(new Error('missing exact ref'), { code: 1 }) - } - throw new Error('git stdout exceeded maxBuffer') - } - if (args[0] === 'fetch') { - throw new Error(`Unexpected fetch: ${args.join(' ')}`) - } - if (args[0] === 'branch') { - return { stdout: 'feature\n' } - } - if (args[0] === 'merge-base') { - expect(args[1]).toBe('main') - return { stdout: 'abc123\n' } - } - if (args[0] === 'log' || args[0] === 'diff') { - return { stdout: 'change\n' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await expect(getPullRequestDraftContext(execGit, createContextInput())).resolves.toMatchObject({ - branch: 'feature' - }) - expect(execGit).toHaveBeenCalledWith(['show-ref', '--', 'main'], expect.any(Object)) - expect(execGit).not.toHaveBeenCalledWith(expect.arrayContaining(['fetch']), expect.any(Object)) - }) - - it('preserves a legal branch whose final component is HEAD', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'remote') { - return { stdout: 'origin\n', stderr: '' } - } - if (args[0] === 'show-ref') { - return args.at(-1) === 'refs/remotes/origin/feature/HEAD' - ? { stdout: '', stderr: '' } - : Promise.reject(Object.assign(new Error('missing exact ref'), { code: 1 })) - } - if (args[0] === 'fetch') { - return { stdout: '', stderr: '' } - } - if (args[0] === 'branch') { - return { stdout: 'feature/current\n', stderr: '' } - } - if (args[0] === 'merge-base') { - expect(args[1]).toBe('origin/feature/HEAD') - return { stdout: 'abc123\n', stderr: '' } - } - if (args[0] === 'log' || args[0] === 'diff') { - return { stdout: 'change\n', stderr: '' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await getPullRequestDraftContext(execGit, createContextInput('feature/HEAD')) - - expect(execGit).toHaveBeenCalledWith( - ['fetch', '--no-tags', 'origin', '+refs/heads/feature/HEAD:refs/remotes/origin/feature/HEAD'], - expect.any(Object) - ) - }) - - it('retains exact-ref precedence when many other remotes share the suffix', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'remote') { - return { stdout: 'origin\nfirst\nsecond\n', stderr: '' } - } - if (args[0] === 'show-ref') { - return args.at(-1) === 'refs/remotes/orphan/main' - ? { stdout: '', stderr: '' } - : Promise.reject(Object.assign(new Error('missing exact ref'), { code: 1 })) - } - if (args[0] === 'fetch' || args[0] === 'branch') { - return { stdout: '', stderr: '' } - } - if (args[0] === 'merge-base') { - expect(args[1]).toBe('orphan/main') - return { stdout: 'abc123\n', stderr: '' } - } - if (args[0] === 'log' || args[0] === 'diff') { - return { stdout: 'change\n', stderr: '' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await getPullRequestDraftContext(execGit, createContextInput('orphan/main')) - - expect(execGit).toHaveBeenCalledWith( - ['fetch', '--no-tags', 'orphan', '+refs/heads/main:refs/remotes/orphan/main'], - expect.any(Object) - ) - }) - - it('retains preferred origin precedence when its remote is not configured', async () => { - const execGit = vi.fn(async (args) => { - if (args[0] === 'remote') { - return { stdout: 'fork\n', stderr: '' } - } - if (args[0] === 'for-each-ref') { - return { stdout: 'refs/remotes/origin/main\n', stderr: '' } - } - if (args[0] === 'fetch' || args[0] === 'branch') { - return { stdout: '', stderr: '' } - } - if (args[0] === 'merge-base') { - expect(args[1]).toBe('origin/main') - return { stdout: 'abc123\n', stderr: '' } - } - if (args[0] === 'log' || args[0] === 'diff') { - return { stdout: 'change\n', stderr: '' } - } - throw new Error(`Unexpected git args: ${args.join(' ')}`) - }) - - await getPullRequestDraftContext(execGit, createContextInput()) - - expect(execGit).toHaveBeenCalledWith( - ['fetch', '--no-tags', 'origin', '+refs/heads/main:refs/remotes/origin/main'], - expect.any(Object) - ) - expect(execGit.mock.calls.filter(([args]) => args[0] === 'for-each-ref')).toHaveLength(1) - }) -}) diff --git a/src/main/text-generation/pull-request-context.ts b/src/main/text-generation/pull-request-context/pull-request-context.ts similarity index 94% rename from src/main/text-generation/pull-request-context.ts rename to src/main/text-generation/pull-request-context/pull-request-context.ts index 4753fa44f09..152d97d772e 100644 --- a/src/main/text-generation/pull-request-context.ts +++ b/src/main/text-generation/pull-request-context/pull-request-context.ts @@ -1,7 +1,7 @@ -import type { PullRequestDraftContext } from '../../shared/source-control/ai/generation/pull-request-generation' -import { isSafeGitRefName } from '../../shared/git/status/git-status-upstream-ref' -import { isSafeReviewHeadFetchRemote } from '../../shared/review-head-tracking-ref' -import { ReviewDraftContextError } from '../../shared/review-draft-context-error' +import type { PullRequestDraftContext } from '../../../shared/source-control/ai/generation/pull-request-generation' +import { isSafeGitRefName } from '../../../shared/git/status/git-status-upstream-ref' +import { isSafeReviewHeadFetchRemote } from '../../../shared/review-head-tracking-ref' +import { ReviewDraftContextError } from '../../../shared/review-draft-context-error' import { canQueryRemoteBaseRefs, getPullRequestRemoteRefState, diff --git a/src/main/text-generation/pull-request-remote-ref-probes.ts b/src/main/text-generation/pull-request-context/pull-request-remote-ref-probes.ts similarity index 94% rename from src/main/text-generation/pull-request-remote-ref-probes.ts rename to src/main/text-generation/pull-request-context/pull-request-remote-ref-probes.ts index 96565d8fa8f..af5680f1b94 100644 --- a/src/main/text-generation/pull-request-remote-ref-probes.ts +++ b/src/main/text-generation/pull-request-context/pull-request-remote-ref-probes.ts @@ -1,10 +1,10 @@ -import { isSafeGitRefName } from '../../shared/git/status/git-status-upstream-ref' -import { isRemoteHeadRef } from '../../shared/source-control/hosted-review/hosted-review-refs' -import { isSafeReviewHeadFetchRemote } from '../../shared/review-head-tracking-ref' +import { isSafeGitRefName } from '../../../shared/git/status/git-status-upstream-ref' +import { isRemoteHeadRef } from '../../../shared/source-control/hosted-review/hosted-review-refs' +import { isSafeReviewHeadFetchRemote } from '../../../shared/review-head-tracking-ref' import { probeExactRefsViaForEachRef, type ExactRefProbeExecOptions -} from '../git/refs/resolution/exact-ref-probe' +} from '../../git/refs/resolution/exact-ref-probe' export type PullRequestGitExec = ( args: string[], diff --git a/src/main/text-generation/commit-message-text-generation-branch-name.test.ts b/src/main/text-generation/requests/commit-message-text-generation-branch-name.test.ts similarity index 93% rename from src/main/text-generation/commit-message-text-generation-branch-name.test.ts rename to src/main/text-generation/requests/commit-message-text-generation-branch-name.test.ts index 2380b7a8688..52e3c8d6945 100644 --- a/src/main/text-generation/commit-message-text-generation-branch-name.test.ts +++ b/src/main/text-generation/requests/commit-message-text-generation-branch-name.test.ts @@ -1,6 +1,6 @@ -import { realKimiOccupiedOutput } from '../../shared/__fixtures__/kimi-generation-source-shaped' +import { realKimiOccupiedOutput } from '../../../shared/__fixtures__/kimi-generation-source-shaped' import { describe, expect, it } from 'vitest' -import { generateBranchNameFromContext } from './commit-message-text-generation' +import { generateBranchNameFromContext } from '../commit-message-text-generation' describe('generateBranchNameFromContext', () => { it.each(['pi', 'kimi'] as const)('%s failure diagnostics', async (agentId) => { diff --git a/src/main/text-generation/commit-message-text-generation-generated-output.test.ts b/src/main/text-generation/requests/commit-message-text-generation-generated-output.test.ts similarity index 94% rename from src/main/text-generation/commit-message-text-generation-generated-output.test.ts rename to src/main/text-generation/requests/commit-message-text-generation-generated-output.test.ts index 4f75dbaed90..f9f05ba52af 100644 --- a/src/main/text-generation/commit-message-text-generation-generated-output.test.ts +++ b/src/main/text-generation/requests/commit-message-text-generation-generated-output.test.ts @@ -4,13 +4,13 @@ import { describe, expect, it } from 'vitest' import { generateCommitMessageFromContext, generatePullRequestFieldsFromContext -} from './commit-message-text-generation' +} from '../commit-message-text-generation' import { realKimiPrOutput, sourceShapedKimiOutput -} from '../../shared/__fixtures__/kimi-generation-source-shaped' -import '../../shared/source-control/ai/generation/commit-message-agent-output' -import './source-control-agent-failure' +} from '../../../shared/__fixtures__/kimi-generation-source-shaped' +import '../../../shared/source-control/ai/generation/commit-message-agent-output' +import '../failures/source-control-agent-failure' describe('generateCommitMessageFromContext', () => { it.each(['custom', 'kimi', 'kimi-literal'] as const)('%s subject and body', async (source) => { diff --git a/src/main/text-generation/commit-message-text-generation-regression.test.ts b/src/main/text-generation/requests/commit-message-text-generation-regression.test.ts similarity index 96% rename from src/main/text-generation/commit-message-text-generation-regression.test.ts rename to src/main/text-generation/requests/commit-message-text-generation-regression.test.ts index 89193ab9bd3..b7a66d2a1e6 100644 --- a/src/main/text-generation/commit-message-text-generation-regression.test.ts +++ b/src/main/text-generation/requests/commit-message-text-generation-regression.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { generateCommitMessageFromContext } from './commit-message-text-generation' +import { generateCommitMessageFromContext } from '../commit-message-text-generation' describe('commit-message generation regressions', () => { it('renders the complete commit policy before handing execution to the owning host', async () => { diff --git a/src/main/text-generation/conversation-name-generation-request.test.ts b/src/main/text-generation/requests/conversation-name-generation-request.test.ts similarity index 88% rename from src/main/text-generation/conversation-name-generation-request.test.ts rename to src/main/text-generation/requests/conversation-name-generation-request.test.ts index 048a04deb62..8fbdf1bf3f9 100644 --- a/src/main/text-generation/conversation-name-generation-request.test.ts +++ b/src/main/text-generation/requests/conversation-name-generation-request.test.ts @@ -1,10 +1,10 @@ -import { realKimiOccupiedOutput } from '../../shared/__fixtures__/kimi-generation-source-shaped' +import { realKimiOccupiedOutput } from '../../../shared/__fixtures__/kimi-generation-source-shaped' import { describe, expect, it, vi } from 'vitest' -import { createMockDiscoveryChild } from './commit-message-text-generation-test-harness' -import { generateConversationNameFromContext } from './commit-message-text-generation' -import { cancelLocalGeneration } from './source-control-generation-lanes' +import { createMockDiscoveryChild } from '../execution/commit-message-text-generation-test-harness' +import { generateConversationNameFromContext } from '../commit-message-text-generation' +import { cancelLocalGeneration } from '../execution/source-control-generation-lanes' import { generateConversationName } from './conversation-name-generation-request' -import type { SpawnedSourceControlAgentProcess } from './source-control-text-generation-types' +import type { SpawnedSourceControlAgentProcess } from '../source-control-text-generation-types' describe('generateConversationName', () => { it.each([ diff --git a/src/main/text-generation/conversation-name-generation-request.ts b/src/main/text-generation/requests/conversation-name-generation-request.ts similarity index 78% rename from src/main/text-generation/conversation-name-generation-request.ts rename to src/main/text-generation/requests/conversation-name-generation-request.ts index 022534aa8cc..c9e30df82d5 100644 --- a/src/main/text-generation/conversation-name-generation-request.ts +++ b/src/main/text-generation/requests/conversation-name-generation-request.ts @@ -2,15 +2,15 @@ import { renderConversationNamePrompt, sanitizeGeneratedConversationName, type ConversationNameContext -} from '../../shared/source-control/ai/generation/conversation-name-generation' -import type { ResolvedSourceControlAiGenerationParams } from '../../shared/source-control/ai/source-control-ai-operation-resolution' -import { captureAgentGenerationFailureOutput } from './agent-failure-output' -import { runGenerationRequest } from './agent-generation-request' +} from '../../../shared/source-control/ai/generation/conversation-name-generation' +import type { ResolvedSourceControlAiGenerationParams } from '../../../shared/source-control/ai/source-control-ai-operation-resolution' +import { captureAgentGenerationFailureOutput } from '../failures/agent-failure-output' +import { runGenerationRequest } from '../agent-generation-request' import type { CommitMessageGenerationTarget, GenerateConversationNameResult, SpawnSourceControlAgent -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' export async function generateConversationName(input: { context: ConversationNameContext diff --git a/src/main/text-generation/dictation-cleanup-generation-request.ts b/src/main/text-generation/requests/dictation-cleanup-generation-request.ts similarity index 70% rename from src/main/text-generation/dictation-cleanup-generation-request.ts rename to src/main/text-generation/requests/dictation-cleanup-generation-request.ts index 7612a5f548e..7f11518ce8b 100644 --- a/src/main/text-generation/dictation-cleanup-generation-request.ts +++ b/src/main/text-generation/requests/dictation-cleanup-generation-request.ts @@ -1,14 +1,14 @@ import { buildDictationCleanupPrompt, sanitizeDictationCleanupOutput -} from '../../shared/dictation-cleanup' -import type { DictationTerm } from '../../shared/dictation-dictionary' -import { renderSourceControlActionCommandTemplate } from '../../shared/source-control/ai/recipes/source-control-ai-actions' -import type { ResolvedSourceControlAiGenerationParams } from '../../shared/source-control/ai/source-control-ai-operation-resolution' -import type { VoiceSettings } from '../../shared/speech-types' -import { runGenerationRequest } from './agent-generation-request' -import { spawnSourceControlAgent } from './source-control-agent-launch' -import type { CommitMessageGenerationTarget } from './source-control-text-generation-types' +} from '../../../shared/dictation-cleanup' +import type { DictationTerm } from '../../../shared/dictation-dictionary' +import { renderSourceControlActionCommandTemplate } from '../../../shared/source-control/ai/recipes/source-control-ai-actions' +import type { ResolvedSourceControlAiGenerationParams } from '../../../shared/source-control/ai/source-control-ai-operation-resolution' +import type { VoiceSettings } from '../../../shared/speech-types' +import { runGenerationRequest } from '../agent-generation-request' +import { spawnSourceControlAgent } from '../execution/source-control-agent-launch' +import type { CommitMessageGenerationTarget } from '../source-control-text-generation-types' export type GenerateDictationCleanupResult = | { success: true; text: string; agentLabel?: string } diff --git a/src/main/text-generation/jira-issue-summary-requests.test.ts b/src/main/text-generation/requests/jira-issue-summary-requests.test.ts similarity index 82% rename from src/main/text-generation/jira-issue-summary-requests.test.ts rename to src/main/text-generation/requests/jira-issue-summary-requests.test.ts index bb9b99b1a1e..cff5c158da7 100644 --- a/src/main/text-generation/jira-issue-summary-requests.test.ts +++ b/src/main/text-generation/requests/jira-issue-summary-requests.test.ts @@ -6,11 +6,13 @@ const mocks = vi.hoisted(() => ({ resolve: vi.fn(), status: vi.fn() })) -vi.mock('../jira/client', () => ({ getStatus: mocks.status })) -vi.mock('./commit-message-agent-environment', () => ({ +vi.mock('../../jira/client', () => ({ getStatus: mocks.status })) +vi.mock('../execution/commit-message-agent-environment', () => ({ prepareLocalCommitMessageAgentEnv: mocks.prepare })) -vi.mock('./commit-message-text-generation', () => ({ resolveTextGenerationParams: mocks.resolve })) +vi.mock('../commit-message-text-generation', () => ({ + resolveCommitMessageSettings: mocks.resolve +})) vi.mock('./jira-issue-summary-text-generation', () => ({ generateJiraIssueSummaryFromContext: mocks.generate, cancelGenerateJiraIssueSummaryLocal: mocks.cancel @@ -19,7 +21,7 @@ import { generateJiraIssueSummaryRequest, cancelJiraIssueSummaryRequest } from './jira-issue-summary-requests' -import { getDefaultSettings } from '../../shared/constants' +import { getDefaultSettings } from '../../../shared/constants' function deferred() { let resolve!: (value: T) => void const promise = new Promise((done) => { @@ -44,21 +46,6 @@ beforeEach(() => { mocks.status.mockReturnValue({ sites: [{ id: 'site-1' }, { id: 'site-2' }] }) }) describe('Jira title host ownership', () => { - it('uses the owning host settings and account environment', async () => { - expect(await generateJiraIssueSummaryRequest(host, 'owner-1', args)).toMatchObject({ - success: true - }) - expect(mocks.resolve).toHaveBeenCalledWith(settings, 'local', 'branchName', null) - expect(mocks.prepare).toHaveBeenCalledWith( - 'custom', - expect.objectContaining({ prepareForCodexLaunch: expect.any(Function) }) - ) - expect(mocks.generate).toHaveBeenCalledWith( - args, - expect.anything(), - expect.objectContaining({ kind: 'local', env: { CODEX_HOME: 'host-codex-home' } }) - ) - }) it('does not launch a replaced request after its environment finishes', async () => { const slow = deferred<{ ok: true }>() mocks.prepare.mockReturnValueOnce(slow.promise) diff --git a/src/main/text-generation/jira-issue-summary-requests.ts b/src/main/text-generation/requests/jira-issue-summary-requests.ts similarity index 89% rename from src/main/text-generation/jira-issue-summary-requests.ts rename to src/main/text-generation/requests/jira-issue-summary-requests.ts index eaff34e6c64..30ccfc094e3 100644 --- a/src/main/text-generation/jira-issue-summary-requests.ts +++ b/src/main/text-generation/requests/jira-issue-summary-requests.ts @@ -1,20 +1,20 @@ import os from 'node:os' -import { LOCAL_COMMIT_MESSAGE_HOST_KEY } from '../../shared/source-control/ai/generation/commit-message-host-key' +import { LOCAL_COMMIT_MESSAGE_HOST_KEY } from '../../../shared/source-control/ai/generation/commit-message-host-key' import type { JiraIssueSummaryGenerationRequest, JiraIssueSummaryGenerationCancellation, JiraIssueSummaryGenerationResult -} from '../../shared/jira/jira-issue-summary-generation' +} from '../../../shared/jira/jira-issue-summary-generation' import { textGenerationFailure, textGenerationFailureCodeFields -} from '../../shared/text-generation-failure-copy' -import { getStatus } from '../jira/client' +} from '../../../shared/text-generation-failure-copy' +import { getStatus } from '../../jira/client' import { prepareLocalCommitMessageAgentEnv, type CommitMessageAgentEnvironmentResolvers -} from './commit-message-agent-environment' -import { resolveTextGenerationParams } from './commit-message-text-generation' +} from '../execution/commit-message-agent-environment' +import { resolveCommitMessageSettings as resolveTextGenerationParams } from '../commit-message-text-generation' import { cancelGenerateJiraIssueSummaryLocal, generateJiraIssueSummaryFromContext diff --git a/src/main/text-generation/jira-issue-summary-text-generation.ts b/src/main/text-generation/requests/jira-issue-summary-text-generation.ts similarity index 76% rename from src/main/text-generation/jira-issue-summary-text-generation.ts rename to src/main/text-generation/requests/jira-issue-summary-text-generation.ts index 727aa349548..b1915f941fa 100644 --- a/src/main/text-generation/jira-issue-summary-text-generation.ts +++ b/src/main/text-generation/requests/jira-issue-summary-text-generation.ts @@ -3,19 +3,19 @@ import { sanitizeGeneratedJiraIssueSummary, type JiraIssueSummaryGenerationContext, type JiraIssueSummaryGenerationResult -} from '../../shared/jira/jira-issue-summary-generation' -import type { ResolvedSourceControlAiGenerationParams } from '../../shared/source-control/ai/source-control-ai-operation-resolution' +} from '../../../shared/jira/jira-issue-summary-generation' +import type { ResolvedSourceControlAiGenerationParams } from '../../../shared/source-control/ai/source-control-ai-operation-resolution' import { textGenerationFailure, textGenerationFailureCodeFields -} from '../../shared/text-generation-failure-copy' -import { spawnSourceControlAgent } from './source-control-agent-launch' -import { cancelLocalGeneration } from './source-control-generation-lanes' -import { runGenerationRequest } from './agent-generation-request' +} from '../../../shared/text-generation-failure-copy' +import { spawnSourceControlAgent } from '../execution/source-control-agent-launch' +import { cancelLocalGeneration } from '../execution/source-control-generation-lanes' +import { runGenerationRequest } from '../agent-generation-request' import type { CommitMessageGenerationTarget, SpawnSourceControlAgent -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' // Source-control templates and instructions do not apply to Jira issue titles. export async function generateJiraIssueSummaryFromContext( diff --git a/src/main/text-generation/source-control-text-generation-requests.ts b/src/main/text-generation/requests/source-control-text-generation-requests.ts similarity index 85% rename from src/main/text-generation/source-control-text-generation-requests.ts rename to src/main/text-generation/requests/source-control-text-generation-requests.ts index 8d87c5b60c5..95c17ae8c67 100644 --- a/src/main/text-generation/source-control-text-generation-requests.ts +++ b/src/main/text-generation/requests/source-control-text-generation-requests.ts @@ -2,43 +2,42 @@ import { buildCommitMessagePrompt, splitGeneratedCommitMessage, type CommitMessageDraftContext -} from '../../shared/source-control/ai/generation/commit-message-generation' +} from '../../../shared/source-control/ai/generation/commit-message-generation' import { buildPullRequestFieldsPrompt, parseGeneratedPullRequestFields, type GeneratedPullRequestFields, type PullRequestDraftContext -} from '../../shared/source-control/ai/generation/pull-request-generation' +} from '../../../shared/source-control/ai/generation/pull-request-generation' import { buildBranchNamePrompt, sanitizeBranchSlug, type BranchNameWorkContext -} from '../../shared/source-control/ai/naming/branch-name-from-work' +} from '../../../shared/source-control/ai/naming/branch-name-from-work' import { cleanGeneratedCommitMessage, stripPrefilledReasoningPreamble -} from '../../shared/source-control/ai/generation/commit-message-agent-output' -import { STAGED_DIFF_BYTE_BUDGET } from '../../shared/source-control/ai/generation/commit-message-prompt' -import { isCustomAgentId } from '../../shared/source-control/ai/generation/commit-message-agent-spec' -import { planCommitMessageGeneration } from '../../shared/source-control/ai/generation/commit-message-plan' -import type { ResolvedSourceControlAiGenerationParams } from '../../shared/source-control/ai/source-control-ai-operation-resolution' -import { formatLinkedIssueTemplateValue } from '../../shared/source-control/ai/recipes/source-control-ai-action-variables' -import { renderSourceControlActionCommandTemplate } from '../../shared/source-control/ai/recipes/source-control-ai-actions' +} from '../../../shared/source-control/ai/generation/commit-message-agent-output' +import { STAGED_DIFF_BYTE_BUDGET } from '../../../shared/source-control/ai/generation/commit-message-prompt' +import { isCustomAgentId } from '../../../shared/source-control/ai/generation/commit-message-agent-spec' +import { planCommitMessageGeneration } from '../../../shared/source-control/ai/generation/commit-message-plan' +import type { ResolvedSourceControlAiGenerationParams } from '../../../shared/source-control/ai/source-control-ai-operation-resolution' +import { formatLinkedIssueTemplateValue } from '../../../shared/source-control/ai/recipes/source-control-ai-action-variables' +import { renderSourceControlActionCommandTemplate } from '../../../shared/source-control/ai/recipes/source-control-ai-actions' import { textGenerationFailure, textGenerationFailureCodeFields -} from '../../shared/text-generation-failure-copy' -import { captureAgentGenerationFailureOutput } from './agent-failure-output' -import { commandBackslashMode, runGenerationRequest } from './agent-generation-request' -import { fitsLocalCommandLine } from './source-control-local-process' -export { commandBackslashMode } from './agent-generation-request' +} from '../../../shared/text-generation-failure-copy' +import { captureAgentGenerationFailureOutput } from '../failures/agent-failure-output' +import { commandBackslashMode, runGenerationRequest } from '../agent-generation-request' +import { fitsLocalCommandLine } from '../execution/source-control-command-line-budget' import type { CommitMessageGenerationTarget, GenerateBranchNameResult, GenerateCommitMessageResult, GeneratePullRequestFieldsResult, SpawnSourceControlAgent -} from './source-control-text-generation-types' +} from '../source-control-text-generation-types' type GenerateParams = ResolvedSourceControlAiGenerationParams diff --git a/src/main/text-generation/source-control-text-generation-types.ts b/src/main/text-generation/source-control-text-generation-types.ts index b42e40e0512..51ca97538e1 100644 --- a/src/main/text-generation/source-control-text-generation-types.ts +++ b/src/main/text-generation/source-control-text-generation-types.ts @@ -1,6 +1,6 @@ import type { AgentSessionUnavailable } from '../../shared/agent-session/agent-session-availability' import type { spawnProcess } from '@orca/process-host' -import type { AgentGenerationFailureOutput } from './agent-failure-output' +import type { AgentGenerationFailureOutput } from './failures/agent-failure-output' import type { CommitMessageAgentCapability, CommitMessageModelCapability diff --git a/src/main/text-generation/source-control-wsl-launch.test.ts b/src/main/text-generation/source-control-wsl-launch.test.ts deleted file mode 100644 index ae17df2abcc..00000000000 --- a/src/main/text-generation/source-control-wsl-launch.test.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { spawnProcess } from '@orca/process-host' -import type * as ChildProcess from 'node:child_process' -import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, realpathSync } from 'node:fs' -import { rm } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { expect, it, vi, afterEach } from 'vitest' -import { quotePosixShell } from '../../shared/shell/launch/shell-argument-quoting' -import '../../shared/windows-command-line-budget' -import { prepareWslPrompt } from './source-control-prompt-files' -import { producePromptRecords } from './source-control-prompt-producer' -import { planCommitMessageGeneration } from '../../shared/source-control/ai/generation/commit-message-plan' -import { withPlatform } from './commit-message-text-generation-test-harness' -import { spawnSourceControlAgent } from './source-control-agent-launch' -import './source-control-local-generation' -import { resolveCommand } from '../git/command-runner/host-routing/wsl-command-resolution' - -const { spawnMock } = vi.hoisted(() => ({ spawnMock: vi.fn() })) -vi.mock('node:child_process', async (original) => { - const actual = await original() - spawnMock.mockImplementation(actual.spawn) - return { ...actual, spawn: spawnMock } -}) -vi.mock('../wsl/discovery/wsl-background-distro-gate', () => ({ noteWslDistroLaunched: vi.fn() })) -const nativeSpawn = (await vi.importActual('node:child_process')).spawn -afterEach(() => { - spawnMock.mockReset().mockImplementation(nativeSpawn) -}) -function planned(agentId: 'omp' | 'codex' = 'omp') { - const result = planCommitMessageGeneration( - { - agentId, - model: 'default', - agentCommandOverride: 'lower=recipe LOWER=UPPER omp --model prefix/model', - agentArgs: '--thinking high --mode text @user.txt -- recipe-message' - }, - 'request' - ) - if (!result.ok) { - throw new Error(result.error) - } - return result.plan -} - -it.skipIf(process.platform === 'win32')( - 'clears profile-restored hook identity after login while preserving shell function commands', - async () => { - const directory = realpathSync(mkdtempSync(join(tmpdir(), 'orca-wsl-hook-isolation-'))) - const profile = join(directory, 'rc') - const shell = join(directory, 'bash') - const reader = join(directory, 'reader.cjs') - writeFileSync( - shell, - `#!/bin/sh\nexec /bin/bash --noprofile --rcfile ${quotePosixShell(profile)} -ic "$2"\n`, - { mode: 0o700 } - ) - writeFileSync( - join(directory, 'getent'), - `#!/bin/sh\nprintf '%s\\n' ${quotePosixShell(`user:x:1:1:User:${directory}:${shell}`)}\n`, - { mode: 0o700 } - ) - writeFileSync( - profile, - `export ORCA_PANE_KEY=profile-pane ORCA_AGENT_PANE=profile-pane ORCA_AGENT_HOOK_PORT=54321 ORCA_AGENT_HOOKS_DISABLED=0 -export CODEX_HOME=profile-managed-home -review_agent() { ${quotePosixShell(process.execPath)} ${quotePosixShell(reader)} "$@"; } -` - ) - writeFileSync( - reader, - `console.log(JSON.stringify({ - pane: process.env.ORCA_PANE_KEY ?? null, - alias: process.env.ORCA_AGENT_PANE ?? null, - port: process.env.ORCA_AGENT_HOOK_PORT ?? null, - disabled: process.env.ORCA_AGENT_HOOKS_DISABLED, - home: process.env.CODEX_HOME, - args: process.argv.slice(2) -})) -` - ) - // Run the real guest login command locally; only replace the Windows executable boundary. - spawnMock.mockImplementationOnce((_program, args, options) => - nativeSpawn('/bin/sh', ['-c', args.at(-1)], options) - ) - try { - const child = withPlatform('win32', () => - spawnSourceControlAgent({ - binary: 'review_agent', - args: ['literal $HOME'], - wslDistro: 'Review', - env: { - ...process.env, - HOME: directory, - SHELL: shell, - PATH: `${directory}:${process.env.PATH}` - }, - stdinMode: 'pipe', - useCwdForNative: true - }) - ) - let stdout = '' - let stderr = '' - child.stdout?.on('data', (chunk) => (stdout += chunk)) - child.stderr?.on('data', (chunk) => (stderr += chunk)) - const closed = new Promise((resolve, reject) => { - child.on('error', reject) - child.on('close', resolve) - }) - child.stdin?.end() - expect(await closed, stderr).toBe(0) - expect(JSON.parse(stdout)).toEqual({ - pane: null, - alias: null, - port: null, - disabled: '1', - home: 'profile-managed-home', - args: ['literal $HOME'] - }) - } finally { - await rm(directory, { recursive: true, force: true }) - } - } -) - -it.each([false, true])( - 'prepares before hostile profiles and preserves login invocation/cwd/env (assigned=%s)', - async (assigned) => { - const directory = realpathSync(mkdtempSync(join(tmpdir(), 'orca-c3-profiles-'))) - const repoPath = join(directory, 'repo') - const startup = join(directory, 'startup') - const changed = join(directory, 'profile-cwd') - const reportPath = join(directory, 'report.json') - if (!assigned) { - mkdirSync(repoPath) - } - mkdirSync(startup) - mkdirSync(changed) - const shell = join(directory, 'bash') - const profile = join(directory, 'rc') - writeFileSync( - shell, - `#!/bin/sh\nexec /bin/bash --noprofile --rcfile ${quotePosixShell(profile)} -ic "$2"\n`, - { mode: 0o700 } - ) - writeFileSync( - join(directory, 'getent'), - `#!/bin/sh\nprintf '%s\\n' ${quotePosixShell(`user:x:1:1:User:${directory}:${shell}`)}\n`, - { mode: 0o700 } - ) - writeFileSync( - join(directory, '.profile'), - `export PATH=${quotePosixShell(directory)}:"$PATH"\n` - ) - writeFileSync( - profile, - ` -export STARTUP_CWD="$PWD" -export PROFILE_LOWER="$lower" -if IFS= read -r stolen; then export STOLEN=yes; else export STOLEN=no; fi -alias mkdir='mkdir -pv' -alias printf='printf CORRUPTED' -set -C -command mkdir -p ${quotePosixShell(repoPath)} -cd ${quotePosixShell(changed)} -omp() { ${quotePosixShell(process.execPath)} ${quotePosixShell(join(directory, 'reader.cjs'))} "$@"; } -` - ) - writeFileSync( - join(directory, 'reader.cjs'), - String.raw` -const fs=require('node:fs'); const path=process.argv.find(arg=>arg.startsWith('@/tmp/')).slice(1); -fs.writeFileSync(process.env.REPORT, JSON.stringify({startup:process.env.STARTUP_CWD,cwd:process.cwd(),stolen:process.env.STOLEN,profileLower:process.env.PROFILE_LOWER,lower:process.env.lower,content:new TextDecoder().decode(fs.readFileSync(path)),stdin:fs.readFileSync(0).length,mask:process.umask()})); -` - ) - const request = prepareWslPrompt(planned(), 'Actual') - // Use the actual resolved native cwd and original wrapper, without starting a guest on this Mac. - const original = withPlatform('win32', () => - resolveCommand( - assigned ? '/usr/bin/env' : 'omp', - assigned - ? [ - 'lower=recipe', - 'LOWER=UPPER', - process.execPath, - join(directory, 'reader.cjs'), - ...request.plan.args - ] - : request.plan.args, - `\\\\wsl.localhost\\Actual${repoPath.replaceAll('/', '\\')}`, - undefined, - { useWslLoginShell: true } - ) - ) - const child = spawnProcess({ - program: '/bin/sh', - args: [ - '-c', - request.preparation.script, - 'fixture', - ...request.preparation.args, - ...original.args.slice(3) - ], - cwd: startup, - env: { ...process.env, HOME: directory, SHELL: shell, REPORT: reportPath, lower: 'base' }, - stdio: 'pipe' - }) - child.stdin.on('error', () => {}) - const stop = new AbortController() - const closed = new Promise((resolve, reject) => { - child.on('close', resolve) - child.on('error', reject) - }) - child.stderr.on('data', (chunk) => { - if (request.observe(chunk)) { - void producePromptRecords( - child.stdin, - request.records(), - stop.signal, - () => !stop.signal.aborted - ) - } - }) - try { - expect(await closed).toBe(0) - const receipt = JSON.parse(readFileSync(reportPath, 'utf8')) - expect(receipt).toMatchObject({ - startup, - cwd: repoPath, - stolen: 'no', - profileLower: 'base', - lower: assigned ? 'recipe' : 'base', - content: 'request', - stdin: 0 - }) - expect(receipt.mask).toBe(process.umask()) - } finally { - stop.abort() - await rm(`/tmp/orca-generation-${request.preparation.args[0]}`, { - recursive: true, - force: true - }) - await rm(directory, { recursive: true, force: true }) - } - } -) diff --git a/src/shared/chat-naming-catalog-work.test-fixture.ts b/src/shared/chat-naming-catalog-work.test-fixture.ts deleted file mode 100644 index 547a112f3fb..00000000000 --- a/src/shared/chat-naming-catalog-work.test-fixture.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { vi } from 'vitest' -import { normalizeSourceControlAiSettings } from './source-control/ai/settings/source-control-ai-settings' - -export function savedChatNamingCatalog(size: number) { - const source = normalizeSourceControlAiSettings(undefined) - source.selectedModelByAgent.claude = `catalog-${String(size - 1).padStart(4, '0')}` - source.discoveredModelsByAgent = { - claude: Array.from({ length: size }, (_, index) => ({ - id: `catalog-${String(index).padStart(4, '0')}`, - label: `Catalog ${String(index).padStart(4, '0')}` - })) - } - return source -} - -export function observeChatNamingCatalogWork() { - const clone = globalThis.structuredClone - const work = { copiedRows: 0, copiedBytes: 0, selectedRowReads: 0 } - let cloning = false - const catalog = (value: unknown): unknown[] => - value !== null && typeof value === 'object' && 'claude' in value && Array.isArray(value.claude) - ? value.claude - : [] - vi.spyOn(globalThis, 'structuredClone').mockImplementation( - (value: T, options?: Parameters[1]): T => { - cloning = true - let result: T - try { - const rows = catalog(value) - work.copiedRows += rows.length - if (rows.length) { - work.copiedBytes += new TextEncoder().encode(JSON.stringify(rows)).length - } - result = clone(value, options) - } finally { - cloning = false - } - for (const row of catalog(result)) { - if ( - row === null || - typeof row !== 'object' || - !('id' in row) || - typeof row.id !== 'string' - ) { - throw new Error('Expected a discovered model row') - } - const id = row.id - Object.defineProperty(row, 'id', { - enumerable: true, - configurable: true, - get: () => { - if (!cloning) { - work.selectedRowReads++ - } - return id - } - }) - } - return result - } - ) - return { - work, - reset: () => Object.assign(work, { copiedRows: 0, copiedBytes: 0, selectedRowReads: 0 }) - } -} From 86cacb133c101a7e31e9161526354cf93f15200a Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 21:34:24 -0800 Subject: [PATCH 018/178] test: remove low-value runtime, daemon, and automation cases (#41839) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 1 - config/reliability-gates.jsonc | 24 +- .../scripts/ci/sharding/ci-shard-timings.json | 15 - .../vitest-node-runtime-files.mjs | 1 - ...erver-claude-task-wakeup-lifecycle.test.ts | 2 +- .../automation-wake-settle.test.ts | 54 ---- .../automation-worker-durability.test.ts | 41 --- .../codex-structured-session-cancel.test.ts | 270 ------------------ ...daemon-pty-adapter-daemon-recovery.test.ts | 249 +--------------- .../daemon-pty-adapter-sleep-cwd.test.ts | 138 --------- .../routing/testing/daemon-pty-router.test.ts | 134 --------- .../daemon-session-owner-resolution.test.ts | 142 --------- .../degraded-daemon-pty-provider.test.ts | 229 --------------- .../terminal-host-cwd-readability.test.ts | 174 ----------- .../terminal-host-process-inspection.test.ts | 81 ------ .../local-disk-write-precondition.test.ts | 186 +----------- .../ipc/pty/pane/spawn-registration.test.ts | 49 ---- .../pty/runtime/kill-same-id-respawn.test.ts | 81 ------ .../local-pty-child-process-verdict.test.ts | 133 --------- .../local-pty/local-pty-default-shell.test.ts | 67 ----- .../local-pty-executable-spawn.test.ts | 106 ------- ...al-pty-provider-foreground-process.test.ts | 255 ----------------- .../local-pty-provider-shutdown.test.ts | 38 --- .../local-pty-provider-spawn-session.test.ts | 64 ----- ...-pty-provider-windows-shell-launch.test.ts | 194 ------------- ...-remote-windows-command-line-limit.test.ts | 24 -- .../workspace-cleanup-evaluation.test.ts | 79 ----- 27 files changed, 12 insertions(+), 2819 deletions(-) delete mode 100644 src/main/codex/structured/session-lifecycle/codex-structured-session-cancel.test.ts delete mode 100644 src/main/daemon/providers/adapter/testing/daemon-pty-adapter-sleep-cwd.test.ts delete mode 100644 src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts delete mode 100644 src/main/daemon/providers/routing/testing/daemon-session-owner-resolution.test.ts delete mode 100644 src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts delete mode 100644 src/main/daemon/terminal-host/testing/terminal-host-cwd-readability.test.ts delete mode 100644 src/main/daemon/terminal-host/testing/terminal-host-process-inspection.test.ts delete mode 100644 src/main/ipc/pty/pane/spawn-registration.test.ts delete mode 100644 src/main/ipc/pty/runtime/kill-same-id-respawn.test.ts delete mode 100644 src/main/providers/local-pty/local-pty-child-process-verdict.test.ts delete mode 100644 src/main/providers/local-pty/local-pty-default-shell.test.ts delete mode 100644 src/main/providers/local-pty/local-pty-executable-spawn.test.ts delete mode 100644 src/main/providers/local-pty/local-pty-provider-foreground-process.test.ts delete mode 100644 src/main/providers/local-pty/local-pty-provider-windows-shell-launch.test.ts delete mode 100644 src/main/ssh/remote-exec/ssh-remote-windows-command-line-limit.test.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index f2c0bb53ddc..ce2660684da 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -606,7 +606,6 @@ 1 test-timeout-needs-reason:src/main/orcad/host-lifecycle/managed-stop/orcad-stop-request-shutdown.integration.test.ts 1 test-timeout-needs-reason:src/main/orcad/native-runtime/orcad-windows-conpty-breakaway.integration.test.ts 1 test-timeout-needs-reason:src/main/persistence/applying-settings/persistence-source-control-ai-migration.test.ts -1 test-timeout-needs-reason:src/main/providers/local-pty/local-pty-child-process-verdict.test.ts 5 test-timeout-needs-reason:src/main/pty/process-retirement/node-pty-master-fd-retirement.test.ts 1 test-timeout-needs-reason:src/main/runtime/mobile-cloud-relay/testing/mobile-relay-e2ee.integration.test.ts 1 test-timeout-needs-reason:src/main/runtime/rpc/server/orchestration-cli-subprocess.test.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 462e7691f8d..65af45df642 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -6165,7 +6165,7 @@ "providers": ["local", "daemon", "ssh", "runtime"], "coveredPlatforms": ["macos"], "coveredProviders": ["local", "daemon", "ssh", "runtime"], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1674 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1630 removes low-value Git, GitHub/GitLab and IPC projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Live macOS Electron tests prove exact local PTY disappearance after parked-tab close and detached-pgid descendant death after agent close. A deterministic final-pane interleaving now proves durable tab retirement commits before externally owned PTY teardown can publish graph loss, and preserves an unrelated live canary through duplicate exit retirement. Deterministic tests also cover final-pane CLI close durability and same-incarnation handle continuity; persisted-empty restart suppression remains historical Electron evidence, alongside replacement and hard graph-loss stale fencing, preallocated-handle supersession, daemon and SSH routing, local/daemon pending-snapshot ownership across natural exit, stale-root descendant-signal suppression, graceful-to-immediate kill upgrades, duplicate-kill completion sharing, locale-stable bounded/fresh/coalesced process-table reads, deadline-safe successor scans, cycle-safe linear descendant traversal, target-only escalation indexing, 32-wide bulk teardown, source-scan timestamp identity, same-second PID ambiguity, ordinary runtime close ownership, unified-only hydration, split ownership, pane detach transfer, restart alias hydration, and late-hook suppression; live Linux, Windows, WSL, SSH, and remote-runtime process evidence remains pending. A paired macOS Electron journey (real attached-window desktop host plus a separate real paired desktop client, isolated profiles) covers the inverse authority direction for host-created terminals: a renderer graph sync must not retire a tab that `orca terminal create` made. The retained paired journey covers the incident shape the real CLI produces - a repo's FIRST host-created terminal, since src/cli/handlers/terminal.ts sends no clientMutationId. The repeated target-after-unrelated-terminal journey was retired; its separate ordering alarm is no longer provided. An earlier reading of this gate called a preceding host-created terminal and a `clientMutationId` protective; main-process instrumentation refuted that. The renderer's stale session write deleted the host tab from persistence in every variant, and only the delete's timing relative to the measured sync differed, so those rows were false negatives of a single-sync oracle. The defect was in persistence: persistPtyBinding did not raise the repo's terminal topology fence for the first host-admitted tab, leaving the renderer's pre-create tab list free to replay over it. Elapsed time and paired-client mirroring never decided anything. A windowless `orca serve` parity arm runs the identical oracle and passes on both the merge-base and the branch, confirming that topology does not exhibit the defect (the gate it turns on is window-conditioned) and that making the host binding unconditional did not regress it. All of this is macOS-local only. The reported incident occurred on a physical Windows host; we did not run against one. The claim that the mechanism is platform-independent rests on the authoritative-window gate and publication-epoch inheritance being OS-agnostic, not on Windows evidence, so no ConPTY, Windows window-lifecycle, update, or sleep behavior is covered here. The synthetic default-tabs unit suite was retired: its explicit-empty/default-tab assertions no longer supply unit evidence. This retirement supplies no new persisted-restart, runtime-routing, cross-platform, or mobile evidence; existing historical runs and counts remain historical. The test-value audit removes the isolated re-retirement-token/epoch, activity-cutoff, idle-reattach and sibling-restore examples. The cited exact retirement and chained-detach cases remain unchanged, alongside closed-tab revival fencing and alias churn bounds. Their retained evidence is not equivalent coverage for every removed permutation; historical execution evidence is unchanged. The test-value audit removed shared-owner snapshot/cold-restore reference examples, a direct absent-tab PTY-index update, caller-supplied host-owned teardown boolean/no-dispatch, and a single local-provider rejection example. It also removed hand-built detach-retention comparator examples for different runs, handles, provider identities, and handle-less rows without independent identity ingress. Their specific snapshot, missing-tab, host-decision, rejection-count, and comparator alarms are lost. Current exact authority/ordering and chained-detach cases remain retained; they do not establish equivalent coverage for the removed examples. Historical commands, counts, results, and red-green narratives remain historical. The test-value audit retires terminals-explicit-empty-hydration.test.ts in full: its direct git/folder empty-tombstone assertions and literal-flag auto-create helper check are lost. The store test never mounted the initial-terminal effect or performed a persisted restart. The audit also removes terminal-tab-retirement.test.ts pure ownership/alias/routing plan inventories and sleeping-record map assertions; its former 100-tab scan-count case was subsequently retired too; no assertion remains in that suite. These retained and historical cases do not establish equivalent tombstone, default-create, provider teardown, cross-platform, or mobile coverage. Historical commands, results, counts, and red-green narratives remain historical. The subsequent test-value audit deletes the remaining terminal-tab-retirement.test.ts suite. Its 100-tab case asserted only returned plan count and one outer scan per ownership store; it never inspected per-tab plans or provider teardown. Those direct cardinality/work-count alarms are intentionally lost. Historical executed commands, results, counts and red-green narratives remain historical; no equivalent replacement or new runtime, platform or mobile evidence is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1392: src/main/agent-hooks/server-pane-authority.test.ts reduced. Retires one duplicate-transfer no-op listener count and latest pty/timestamp callback fixture. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1674 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1630 removes low-value Git, GitHub/GitLab and IPC projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Live macOS Electron tests prove exact local PTY disappearance after parked-tab close and detached-pgid descendant death after agent close. A deterministic final-pane interleaving now proves durable tab retirement commits before externally owned PTY teardown can publish graph loss, and preserves an unrelated live canary through duplicate exit retirement. Deterministic tests also cover final-pane CLI close durability and same-incarnation handle continuity; persisted-empty restart suppression remains historical Electron evidence, alongside replacement and hard graph-loss stale fencing, preallocated-handle supersession, daemon and SSH routing, local/daemon pending-snapshot ownership across natural exit, stale-root descendant-signal suppression, graceful-to-immediate kill upgrades, duplicate-kill completion sharing, locale-stable bounded/fresh/coalesced process-table reads, deadline-safe successor scans, cycle-safe linear descendant traversal, target-only escalation indexing, 32-wide bulk teardown, source-scan timestamp identity, same-second PID ambiguity, ordinary runtime close ownership, unified-only hydration, split ownership, pane detach transfer, restart alias hydration, and late-hook suppression; live Linux, Windows, WSL, SSH, and remote-runtime process evidence remains pending. A paired macOS Electron journey (real attached-window desktop host plus a separate real paired desktop client, isolated profiles) covers the inverse authority direction for host-created terminals: a renderer graph sync must not retire a tab that `orca terminal create` made. The retained paired journey covers the incident shape the real CLI produces - a repo's FIRST host-created terminal, since src/cli/handlers/terminal.ts sends no clientMutationId. The repeated target-after-unrelated-terminal journey was retired; its separate ordering alarm is no longer provided. An earlier reading of this gate called a preceding host-created terminal and a `clientMutationId` protective; main-process instrumentation refuted that. The renderer's stale session write deleted the host tab from persistence in every variant, and only the delete's timing relative to the measured sync differed, so those rows were false negatives of a single-sync oracle. The defect was in persistence: persistPtyBinding did not raise the repo's terminal topology fence for the first host-admitted tab, leaving the renderer's pre-create tab list free to replay over it. Elapsed time and paired-client mirroring never decided anything. A windowless `orca serve` parity arm runs the identical oracle and passes on both the merge-base and the branch, confirming that topology does not exhibit the defect (the gate it turns on is window-conditioned) and that making the host binding unconditional did not regress it. All of this is macOS-local only. The reported incident occurred on a physical Windows host; we did not run against one. The claim that the mechanism is platform-independent rests on the authoritative-window gate and publication-epoch inheritance being OS-agnostic, not on Windows evidence, so no ConPTY, Windows window-lifecycle, update, or sleep behavior is covered here. The synthetic default-tabs unit suite was retired: its explicit-empty/default-tab assertions no longer supply unit evidence. This retirement supplies no new persisted-restart, runtime-routing, cross-platform, or mobile evidence; existing historical runs and counts remain historical. The test-value audit removes the isolated re-retirement-token/epoch, activity-cutoff, idle-reattach and sibling-restore examples. The cited exact retirement and chained-detach cases remain unchanged, alongside closed-tab revival fencing and alias churn bounds. Their retained evidence is not equivalent coverage for every removed permutation; historical execution evidence is unchanged. The test-value audit removed shared-owner snapshot/cold-restore reference examples, a direct absent-tab PTY-index update, caller-supplied host-owned teardown boolean/no-dispatch, and a single local-provider rejection example. It also removed hand-built detach-retention comparator examples for different runs, handles, provider identities, and handle-less rows without independent identity ingress. Their specific snapshot, missing-tab, host-decision, rejection-count, and comparator alarms are lost. Current exact authority/ordering and chained-detach cases remain retained; they do not establish equivalent coverage for the removed examples. Historical commands, counts, results, and red-green narratives remain historical. The test-value audit retires terminals-explicit-empty-hydration.test.ts in full: its direct git/folder empty-tombstone assertions and literal-flag auto-create helper check are lost. The store test never mounted the initial-terminal effect or performed a persisted restart. The audit also removes terminal-tab-retirement.test.ts pure ownership/alias/routing plan inventories and sleeping-record map assertions; its former 100-tab scan-count case was subsequently retired too; no assertion remains in that suite. These retained and historical cases do not establish equivalent tombstone, default-create, provider teardown, cross-platform, or mobile coverage. Historical commands, results, counts, and red-green narratives remain historical. The subsequent test-value audit deletes the remaining terminal-tab-retirement.test.ts suite. Its 100-tab case asserted only returned plan count and one outer scan per ownership store; it never inspected per-tab plans or provider teardown. Those direct cardinality/work-count alarms are intentionally lost. Historical executed commands, results, counts and red-green narratives remain historical; no equivalent replacement or new runtime, platform or mobile evidence is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1392: src/main/agent-hooks/server-pane-authority.test.ts reduced. Retires one duplicate-transfer no-op listener count and latest pty/timestamp callback fixture. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-4327", "https://github.com/stablyai/orca/pull/14590", @@ -6174,7 +6174,7 @@ "https://github.com/stablyai/orca/issues/14524" ], "invariant": "Close permanently removes the owned provider session, agent descendants, persisted tab/layout authority, and resume authority even when no TerminalPane is mounted; final-pane CLI close commits durable tab retirement before PTY stop can publish graph loss, does not acknowledge before that retirement, and treats the later exit retirement as idempotent. Workspace-wide CLI close applies that same durable contract to every terminal in exactly one worktree, including pinned and persisted-only surfaces, without touching sibling-worktree PTYs or resume records. A persisted explicit-empty state prevents initial-tab fallback from repopulating the workspace after reload or restart. A handle remains valid while the same provider-attested PTY incarnation survives renderer reload or re-key, and becomes stale without adopting a replacement incarnation. A terminating id remains reserved through natural exit, duplicate callers await the same completion, and immediate teardown upgrades any graceful request without signalling a recycled PID or a descendant tree after root ownership is lost; process-table work is locale-stable, bounded, fresh for each post-start request, same-turn coalesced, and begins within the requesting caller's deadline, including bulk worktree cleanup; detach and park preserve ownership; aliases prevent a detached agent's immutable physical pane key from being retired with its former tab.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1674 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Capture exact tab, pane, PTY, handle, incarnation, and persisted layout identities. Final-pane terminal.close must invoke one durability-acknowledged tab retirement before exact PTY teardown; controlled PTY exit and graph removal afterward must remain idempotent, omit the target from persistence and provider inventory, and leave an unrelated canary handle live. terminal.closeAll must force-retire every pinned and unpinned target surface, clear its persisted agent-resume and incarnation records, await authoritative PTY exit, preserve a sibling worktree's tab, PTY, and resume authority, and return an unverifiable failure instead of claiming exit when the owning host does not confirm a stop. The ordinary path stays pending until the persisted row is removed and never falls back to the fire-and-forget pane event; reload/restart must keep the explicitly emptied worktree at zero terminal tabs and provider sessions. Reloading the renderer with the same tab/pane/PTY/incarnation must keep the handle readable; changing only the incarnation behind the same PTY id, losing the authoritative graph, or superseding the renderer handle with a preallocated handle must reject the old handle with terminal_handle_stale. Parked-close tests prove the exact PTY disappears; descendant/process tests keep natural exits reserved, upgrade teardown safely, bound/coalesce process-table work, and protect recycled identities.", + "oracle": "Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1674 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Capture exact tab, pane, PTY, handle, incarnation, and persisted layout identities. Final-pane terminal.close must invoke one durability-acknowledged tab retirement before exact PTY teardown; controlled PTY exit and graph removal afterward must remain idempotent, omit the target from persistence and provider inventory, and leave an unrelated canary handle live. terminal.closeAll must force-retire every pinned and unpinned target surface, clear its persisted agent-resume and incarnation records, await authoritative PTY exit, preserve a sibling worktree's tab, PTY, and resume authority, and return an unverifiable failure instead of claiming exit when the owning host does not confirm a stop. The ordinary path stays pending until the persisted row is removed and never falls back to the fire-and-forget pane event; reload/restart must keep the explicitly emptied worktree at zero terminal tabs and provider sessions. Reloading the renderer with the same tab/pane/PTY/incarnation must keep the handle readable; changing only the incarnation behind the same PTY id, losing the authoritative graph, or superseding the renderer handle with a preallocated handle must reject the old handle with terminal_handle_stale. Parked-close tests prove the exact PTY disappears; descendant/process tests keep natural exits reserved, upgrade teardown safely, bound/coalesce process-table work, and protect recycled identities.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/terminal/surface/orca-runtime-terminal-close-continuity.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts", @@ -7040,7 +7040,7 @@ "providers": ["local", "daemon", "ssh", "wsl", "remote-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": ["local", "daemon", "ssh", "wsl", "remote-runtime"], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. The Codex restart suite and exact command/testFiles/assertionRef are removed; its mock account-change/remote-pane restart alarms are intentionally lost. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3368 retires the whole host-session-launch unit source, including host-authority/create/ensure/legacy/Kimi/refusal/draft/options/prompt/cancel/kill/shared-disconnect and passive-sleep output callback alarms, without equivalent replacement. Its unit command token and test-file entry retire. Five unrelated macOS receipts remain verbatim and keep only their original scope; no fresh changed-selector/runtime/provider/process/UI/mobile/mixed-version/cross-platform evidence is claimed. Audit3359 retires the whole create-handoff suite. Supplied disposition, late completion, close-callback and metadata-bookkeeping alarms are intentionally lost without equivalent replacement; the compound unit selector and test-file entry narrow accordingly. Five unrelated macOS receipts retain their original scope. No fresh runtime, process-deduplication, physical provider/network, historical-import, mobile, mixed-version or cross-platform validation is claimed. Audit 3301 removes the caller-chat-target precedence over host selection example from web-session-tabs-sync-focus-intent.test.ts. The snapshot-before-RPC replay and split-sibling-first exact-leaf focus cases remain unchanged and match the existing assertion reference. Preserve current selectors, assertion references, unrelated active receipts and complete archived history. These mocked state-composition survivors provide no equivalent replacement, rendered, native, network or fresh execution proof. Audit2694 source-only review: Remove14 authored/14 expanded cases in the renderer completion wrapper family, including the complete bell/query-reply/stale-input factory, without replacement. Retain29 authored/34 expanded cases with unchanged callback bodies. Three removals are the existing pending2203 overlap and must be adopted/count-reconciled once; eleven are fresh removals. No current-source runtime or gate execution is supplied by this packet; dated evidence retains its original source scope and does not validate the reduced factory bytes. No equivalent replacement, physical provider, mixed-version, mobile, rendered UI, cross-platform, soak or measured-performance result is claimed. The existing command and testFiles selector retain tests/pty-connection-completion-and-notification.test.ts; no wrapper-specific assertionRef is removed. Historical selector results do not validate its changed reachable factories. Historical prior account (preserved verbatim): Audit2709 removes src/renderer/src/runtime/web-session-tabs-sync/inventory/remote-runtime-session-tabs-inflight.test.ts: intentionally retire waiting out an older inventory and sharing a fresh post-operation request among concurrent callers. No equivalent replacement or fresh execution is claimed. Dated receipts remain historical evidence for their unchanged selectors only; they do not validate the reduced commands or full current gate. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2003: Delete the complete pure orphan-topology suite and its file membership; preserve the other selectors from its combined command after archiving the original command string. Lost alarms are synthetic current-layout preservation, one-anchor grafting and ambiguous-anchor appending examples. Do not transfer their ownership/topology scope to sibling suites. Preserve all5 independent historical receipts exactly. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1983: Delete the mocked agent-session suite and its prospective selector and aliases. Retire all3 whole dated changed-selector receipts, preserving their original raw objects in historical gaps without transferring counts or durations. Preserve5 unrelated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial agent-operation service suite retains only concurrent retry joining with changed-fingerprint conflict. Retire its complete three-title assertion object: old-owner legacy fallback before side effects, nested SSH Pi transcript refusal and completed/ambiguous physical-commit replay fences are no longer backed. Other probe/launch option/ledger/failure/remote-inventory examples disappear as detailed below. No actual provider spawn, live SSH or physical commit proof is retained by this source. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1911: The complete provider PTY-inspection mock suite is removed: missing-provider rejection, stale-PTY renderer unverifiable mapping, preserved supplied renderer failures, and supplied transport-loss mapping alarms are intentionally lost. No actual terminal teardown, daemon error or remote transport was exercised. Only the deleted explicit source tokens, testFile and whole assertionRef are removed. Meaningful remaining source selectors/assertions and unaffected dated receipts retain their original scope. No equivalent replacement, fresh reduced-gate, native/platform/provider, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1495 intentionally deletes the orphan-owner suite: explicit owner/host predicate examples are retired; no equivalent replacement coverage or fresh result is claimed. Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally deletes src/renderer/src/components/terminal-pane/agent-completion-coordinator-pending-title-inspection.test.ts: Loses obsolete pending title process races, inspection failure suppression, explicit completion precedence and delayed remote inspection alarms. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1342 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audits1301 and1308 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Deterministic macOS tests cover controller claims, daemon and SSH/relay operation replay, mixed-version selection, runtime ownership, exact provisional handoff, durable terminal retirement, two independent viewer mirrors, guarded adoption of legacy live PTYs, and completion classification when either the outer remote transport or authoritative host/provider process inspection becomes unreachable. The adoption harness models v1.4.150 agent/setup/shell tabs, current-generation restart and reconnect, exact handle/incarnation/worktree/host checks, topology CAS, competing clients, split-pane/group restoration, WSL ownership, and SSH owner rejection. The secondary parity repro runs independent clients against one headless remote Orca runtime over encrypted pairing and a real daemon-backed PTY, with tokened fixture-process identity separated from unrelated Codex app-server startup probes. The automated primary topology runs an isolated headed macOS Orca desktop server plus a separate paired web client and proves viewer-local fresh/resume focus, exact legacy placement, writable PTYs, unrelated-terminal survival, and host/client cleanup. SSH coverage is provider/relay contract and fault-injection coverage only; it does not substitute for paired-server coverage. Live Windows, Linux, WSL, SSH, and physical paired-Linux hosts remain gaps. The isolated intent-owner ledger suite was removed. web-runtime-session.test.ts retains exact post-create handoff confirmation only; its previous fresh/resume presentation and publication-replay assertions are no longer current references. Those direct owner-partition, cap and RPC/focus alarms are lost; other agent-session evidence does not establish equivalence. Structured handoff and adopted-leaf focus references now point to their dedicated surviving fixtures. Mixed-version recovery retains actual old-host recovery followed by a new CLI tab; its unsupported/missing-evidence tables are removed. Orphan recovery retains in-flight adoption-conflict serialization only; previous topology/pruning and absence/adoption examples are removed. These removed projection, capability and adoption-shape alarms are lost; retained cases do not replace them. Audit1301: src/renderer/src/runtime/remote-runtime-session-tabs-inflight.test.ts reduced. Direct dedupe eviction/count and equal receipt-position sample loses authored alarm. No equivalent replacement coverage is claimed. Audit1308: src/renderer/src/runtime/web-session-tabs-sync-agent-handoff.test.ts removed. Exact provisional replacement and post-create exit confirmation handoff pipeline alarms disappear. No equivalent replacement coverage is claimed. Audit1313: src/main/runtime/terminal-orphan-topology.test.ts: Retain actual topology merge algorithms preserving disjoint subtrees and grafted/ambiguous shared anchors with independent expected trees. Remove duplicate leaf validator and empty/no-change passthrough examples. Lose basic validation and no-group/noop characterization; no renderer layout proof. Reduced selectors require fresh validation; prior full-suite receipts do not validate removed or changed coverage. No equivalent replacement or physical-provider claim. Audit1342: src/renderer/src/components/terminal-pane/pty-connection-completion-and-notification.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4352 deletes the whole mocked daemon router, session-owner resolver, degraded-provider, terminal-host CWD-readability and foreground-inspection suites plus the native-process-CWD fixture suite; recovery retains only public multi-pane proactive socket-death notification/remount. Intentionally lose retirement admission, stale-inventory/fenced-kill authority, attach-only re-resolution/absence, fast-exit routing, sleep CWD checkpoint/wake, diagnostic cancellation/shutdown and foreground census/session-change alarms. No equivalent replacement or fresh gate execution is claimed. Historical prior account (preserved verbatim): Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. The Codex restart suite and exact command/testFiles/assertionRef are removed; its mock account-change/remote-pane restart alarms are intentionally lost. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3368 retires the whole host-session-launch unit source, including host-authority/create/ensure/legacy/Kimi/refusal/draft/options/prompt/cancel/kill/shared-disconnect and passive-sleep output callback alarms, without equivalent replacement. Its unit command token and test-file entry retire. Five unrelated macOS receipts remain verbatim and keep only their original scope; no fresh changed-selector/runtime/provider/process/UI/mobile/mixed-version/cross-platform evidence is claimed. Audit3359 retires the whole create-handoff suite. Supplied disposition, late completion, close-callback and metadata-bookkeeping alarms are intentionally lost without equivalent replacement; the compound unit selector and test-file entry narrow accordingly. Five unrelated macOS receipts retain their original scope. No fresh runtime, process-deduplication, physical provider/network, historical-import, mobile, mixed-version or cross-platform validation is claimed. Audit 3301 removes the caller-chat-target precedence over host selection example from web-session-tabs-sync-focus-intent.test.ts. The snapshot-before-RPC replay and split-sibling-first exact-leaf focus cases remain unchanged and match the existing assertion reference. Preserve current selectors, assertion references, unrelated active receipts and complete archived history. These mocked state-composition survivors provide no equivalent replacement, rendered, native, network or fresh execution proof. Audit2694 source-only review: Remove14 authored/14 expanded cases in the renderer completion wrapper family, including the complete bell/query-reply/stale-input factory, without replacement. Retain29 authored/34 expanded cases with unchanged callback bodies. Three removals are the existing pending2203 overlap and must be adopted/count-reconciled once; eleven are fresh removals. No current-source runtime or gate execution is supplied by this packet; dated evidence retains its original source scope and does not validate the reduced factory bytes. No equivalent replacement, physical provider, mixed-version, mobile, rendered UI, cross-platform, soak or measured-performance result is claimed. The existing command and testFiles selector retain tests/pty-connection-completion-and-notification.test.ts; no wrapper-specific assertionRef is removed. Historical selector results do not validate its changed reachable factories. Historical prior account (preserved verbatim): Audit2709 removes src/renderer/src/runtime/web-session-tabs-sync/inventory/remote-runtime-session-tabs-inflight.test.ts: intentionally retire waiting out an older inventory and sharing a fresh post-operation request among concurrent callers. No equivalent replacement or fresh execution is claimed. Dated receipts remain historical evidence for their unchanged selectors only; they do not validate the reduced commands or full current gate. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2003: Delete the complete pure orphan-topology suite and its file membership; preserve the other selectors from its combined command after archiving the original command string. Lost alarms are synthetic current-layout preservation, one-anchor grafting and ambiguous-anchor appending examples. Do not transfer their ownership/topology scope to sibling suites. Preserve all5 independent historical receipts exactly. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1983: Delete the mocked agent-session suite and its prospective selector and aliases. Retire all3 whole dated changed-selector receipts, preserving their original raw objects in historical gaps without transferring counts or durations. Preserve5 unrelated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial agent-operation service suite retains only concurrent retry joining with changed-fingerprint conflict. Retire its complete three-title assertion object: old-owner legacy fallback before side effects, nested SSH Pi transcript refusal and completed/ambiguous physical-commit replay fences are no longer backed. Other probe/launch option/ledger/failure/remote-inventory examples disappear as detailed below. No actual provider spawn, live SSH or physical commit proof is retained by this source. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1911: The complete provider PTY-inspection mock suite is removed: missing-provider rejection, stale-PTY renderer unverifiable mapping, preserved supplied renderer failures, and supplied transport-loss mapping alarms are intentionally lost. No actual terminal teardown, daemon error or remote transport was exercised. Only the deleted explicit source tokens, testFile and whole assertionRef are removed. Meaningful remaining source selectors/assertions and unaffected dated receipts retain their original scope. No equivalent replacement, fresh reduced-gate, native/platform/provider, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1495 intentionally deletes the orphan-owner suite: explicit owner/host predicate examples are retired; no equivalent replacement coverage or fresh result is claimed. Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally deletes src/renderer/src/components/terminal-pane/agent-completion-coordinator-pending-title-inspection.test.ts: Loses obsolete pending title process races, inspection failure suppression, explicit completion precedence and delayed remote inspection alarms. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1342 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audits1301 and1308 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Deterministic macOS tests cover controller claims, daemon and SSH/relay operation replay, mixed-version selection, runtime ownership, exact provisional handoff, durable terminal retirement, two independent viewer mirrors, guarded adoption of legacy live PTYs, and completion classification when either the outer remote transport or authoritative host/provider process inspection becomes unreachable. The adoption harness models v1.4.150 agent/setup/shell tabs, current-generation restart and reconnect, exact handle/incarnation/worktree/host checks, topology CAS, competing clients, split-pane/group restoration, WSL ownership, and SSH owner rejection. The secondary parity repro runs independent clients against one headless remote Orca runtime over encrypted pairing and a real daemon-backed PTY, with tokened fixture-process identity separated from unrelated Codex app-server startup probes. The automated primary topology runs an isolated headed macOS Orca desktop server plus a separate paired web client and proves viewer-local fresh/resume focus, exact legacy placement, writable PTYs, unrelated-terminal survival, and host/client cleanup. SSH coverage is provider/relay contract and fault-injection coverage only; it does not substitute for paired-server coverage. Live Windows, Linux, WSL, SSH, and physical paired-Linux hosts remain gaps. The isolated intent-owner ledger suite was removed. web-runtime-session.test.ts retains exact post-create handoff confirmation only; its previous fresh/resume presentation and publication-replay assertions are no longer current references. Those direct owner-partition, cap and RPC/focus alarms are lost; other agent-session evidence does not establish equivalence. Structured handoff and adopted-leaf focus references now point to their dedicated surviving fixtures. Mixed-version recovery retains actual old-host recovery followed by a new CLI tab; its unsupported/missing-evidence tables are removed. Orphan recovery retains in-flight adoption-conflict serialization only; previous topology/pruning and absence/adoption examples are removed. These removed projection, capability and adoption-shape alarms are lost; retained cases do not replace them. Audit1301: src/renderer/src/runtime/remote-runtime-session-tabs-inflight.test.ts reduced. Direct dedupe eviction/count and equal receipt-position sample loses authored alarm. No equivalent replacement coverage is claimed. Audit1308: src/renderer/src/runtime/web-session-tabs-sync-agent-handoff.test.ts removed. Exact provisional replacement and post-create exit confirmation handoff pipeline alarms disappear. No equivalent replacement coverage is claimed. Audit1313: src/main/runtime/terminal-orphan-topology.test.ts: Retain actual topology merge algorithms preserving disjoint subtrees and grafted/ambiguous shared anchors with independent expected trees. Remove duplicate leaf validator and empty/no-change passthrough examples. Lose basic validation and no-group/noop characterization; no renderer layout proof. Reduced selectors require fresh validation; prior full-suite receipts do not validate removed or changed coverage. No equivalent replacement or physical-provider claim. Audit1342: src/renderer/src/components/terminal-pane/pty-connection-completion-and-notification.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/8878", "https://github.com/stablyai/orca/issues/9151", @@ -7062,13 +7062,11 @@ "ORCA_E2E_WEB_CLIENT=1 SKIP_BUILD=1 pnpm exec playwright test tests/e2e/remote-agent-session-focus-authority.spec.ts --config tests/playwright.config.ts --project electron-headful --workers=1", "Manual headed paired-server journey: isolated Orca desktop host + separate paired web client + real Codex process + 20-second WebSocket fault + reconnect + explicit stop", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts src/renderer/src/runtime/hosted-terminals/recovery/web-session-terminal-orphan-recovery.test.ts src/renderer/src/runtime/hosted-terminals/recovery/web-session-terminal-orphan-mixed-version.test.ts --maxWorkers=1", - "pnpm exec vitest run --config config/vitest.config.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/runtime/construction/orca-runtime.test.ts tests/e2e/remote-agent-completion-authority.unit.test.ts src/renderer/src/runtime/hosted-terminals/inspection/runtime-terminal-inspection.test.ts src/renderer/src/components/terminal-pane/agent-completion/agent-completion-coordinator-process-cadence.test.ts", - "pnpm exec vitest run --config config/vitest.config.ts tests/e2e/remote-agent-completion-authority.unit.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/renderer/src/runtime/hosted-terminals/inspection/runtime-terminal-inspection.test.ts src/renderer/src/components/terminal-pane/agent-completion/agent-completion-coordinator-process-cadence.test.ts src/renderer/src/components/terminal-pane/pty-connection/tests/pty-connection-completion-and-notification.test.ts" + "pnpm exec vitest run --config config/vitest.config.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/runtime/construction/orca-runtime.test.ts tests/e2e/remote-agent-completion-authority.unit.test.ts src/renderer/src/runtime/hosted-terminals/inspection/runtime-terminal-inspection.test.ts src/renderer/src/components/terminal-pane/agent-completion/agent-completion-coordinator-process-cadence.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts tests/e2e/remote-agent-completion-authority.unit.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/renderer/src/runtime/hosted-terminals/inspection/runtime-terminal-inspection.test.ts src/renderer/src/components/terminal-pane/agent-completion/agent-completion-coordinator-process-cadence.test.ts src/renderer/src/components/terminal-pane/pty-connection/tests/pty-connection-completion-and-notification.test.ts" ], "testFiles": [ "src/main/daemon/terminal-host/testing/terminal-host.test.ts", - "src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts", - "src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts", "src/main/runtime/construction/orca-runtime.test.ts", "tests/e2e/remote-agent-completion-authority.unit.test.ts", "src/renderer/src/runtime/hosted-terminals/inspection/runtime-terminal-inspection.test.ts", @@ -7088,12 +7086,6 @@ "src/renderer/src/runtime/web-session-tabs-sync/navigation/web-session-tabs-sync-focus-intent.test.ts" ], "assertionRefs": [ - { - "file": "src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts", - "assertions": [ - "completion inspection rejects an unmapped session instead of borrowing the local fallback" - ] - }, { "file": "tests/e2e/remote-agent-completion-authority.unit.test.ts", "assertions": [ @@ -7247,6 +7239,8 @@ "Add live SSH/WSL provider evidence before claiming full provider coverage; Docker SSH proves only the SSH provider/relay path." ], "knownGaps": [ + "Audit4352 deletes the whole mocked daemon router, session-owner resolver, degraded-provider, terminal-host CWD-readability and foreground-inspection suites plus the native-process-CWD fixture suite; recovery retains only public multi-pane proactive socket-death notification/remount. Intentionally lose retirement admission, stale-inventory/fenced-kill authority, attach-only re-resolution/absence, fast-exit routing, sleep CWD checkpoint/wake, diagnostic cancellation/shutdown and foreground census/session-change alarms. No equivalent replacement or fresh gate execution is claimed.", + "Audit4352 original affected selector fields before exact deleted paths were pruned: {\"commands\": [\"pnpm exec vitest run --config config/vitest.config.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/runtime/construction/orca-runtime.test.ts tests/e2e/remote-agent-completion-authority.unit.test.ts src/renderer/src/runtime/hosted-terminals/inspection/runtime-terminal-inspection.test.ts src/renderer/src/components/terminal-pane/agent-completion/agent-completion-coordinator-process-cadence.test.ts\", \"pnpm exec vitest run --config config/vitest.config.ts tests/e2e/remote-agent-completion-authority.unit.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/renderer/src/runtime/hosted-terminals/inspection/runtime-terminal-inspection.test.ts src/renderer/src/components/terminal-pane/agent-completion/agent-completion-coordinator-process-cadence.test.ts src/renderer/src/components/terminal-pane/pty-connection/tests/pty-connection-completion-and-notification.test.ts\"], \"testFiles\": [\"src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts\", \"src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts\"], \"assertionRefs\": [{\"file\": \"src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts\", \"assertions\": [\"completion inspection rejects an unmapped session instead of borrowing the local fallback\"]}]}", "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed.", "Audit3814 per-case accepted runtime losses:\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: resolves tui-idle from a Codex ready prompt even when stale startup lines remain :: Stale MCP startup lines must not block a later Codex ready header.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: resolves tui-idle when a stale Codex prompt is followed by the ready header :: A Codex ready header following a stale resume prompt must satisfy tui-idle.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: resolves tui-idle for an idle Cursor lane past its dismissed trust dialog (#8210) :: Later Cursor idle banner overrides retained trust dialog.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: does not block a mid-run Cursor lane on its dismissed trust dialog (#8210) :: Dismissed trust dialog must not report blocked for later busy Cursor; wait times out.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: does not classify unrelated press-enter prompts as Codex blocked prompts :: Unidentified press-enter text must time out rather than be classified Codex blocked.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: resolves tui-idle for quiet background PTY agents without OSC titles :: Quiet Codex background terminal without OSC title eventually resolves tui-idle.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-07.spec.ts :: sends agent prompts as bracketed paste before submit :: Driving prompt escapes embedded paste-end ESC, writes one framed paste then CR, and counts bytes.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts :: settles a foreground Codex prompt when launch metadata has not arrived :: Codex inferred from foreground without launch metadata waits for composer marker/quiescence before one CR.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts :: gives a late Codex render marker a fresh quiescence window :: Marker arriving at7.9s re-arms fresh quiet window instead of submitting at initial cap.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts :: writes large agent prompt paste frames atomically before delayed submit :: A prompt larger than input chunk budget is a single paste write before delayed CR with exact byte count.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts :: rejects oversized terminal.send text before provider writes :: Oversized ordinary terminal.send rejects input-too-large before any provider write.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts :: rejects focusing an exited background terminal session :: focusTerminal refuses terminal_exited before invoking background reveal.\nsrc/main/runtime/orca-runtime-tests/agent-launch-prompt-submit.spec.ts :: pastes the same bytes the desktop draft paste sent for the prompt :: Launch-ready prompt normalizes CR/LF and renders embedded ESC visibly identically to desktop bracketed paste.\nsrc/main/runtime/orca-runtime-tests/agent-launch-prompt-submit.spec.ts :: keeps a prompt into a running agent behind the render settle, as before :: Driving prompt remains held through7s repaint activity then submits once by9s.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: reports a queued PTY focus as not navigated when its notifier disappears :: Queued PTY focus and completed active reveal report not navigated after notifier removal; no second reveal.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: does not invoke a stale graph-leaf focus notifier after queued PTY work :: Queued graph-leaf focus must not invoke old notifier after preceding PTY reveal and notifier removal.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: paginates retained terminal output with explicit limits and truncation metadata :: Preview120lines, fractional limits,50/200 pages, retained2000line truncation and future-cursor metadata stay consistent.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: falls back to renderer visible screen when uncursored TUI tail is blank :: Substantial blank uncursored tail uses mounted renderer visible screen with zero scrollback.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: reads and shows the runtime-owned alternate-screen grid without serialization :: Actual runtime alternate-screen grid provides read/show content without renderer/provider serialization.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: classifies older provider alternate-screen snapshots from ANSI :: Older provider without alternateScreen metadata classifies ANSI alternate entry and returns Remote Vim screen.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-09.spec.ts :: honors the last ANSI screen transition when older provider metadata is absent :: Last ANSI alternate exit overrides earlier entry when explicit provider mode metadata is absent.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: rejects a full provider screen frame after live output advances :: Live output arriving during second full-screen capture causes screen-unavailable rather than stale full frame.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: shares one provider snapshot between concurrent read and show calls :: Concurrent read/show share one successful provider snapshot and both receive Shared TUI content.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: keeps cursor reads provider-free on restored alternate-screen PTYs :: Restored alternate-screen cursor transcript reads never acquire provider snapshot.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: backs off after a provider snapshot failure instead of retrying for show :: Failed provider capture is cached/backed off so later show does not retry.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: falls back once to a mounted renderer when a known alternate provider fails :: A known-alternate failed provider capture falls back exactly once to newly mounted renderer.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: bounds provider visible frames for read and show responses :: Provider visible read/show use six bounded lines and300character preview.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: keeps renderer-fallback composer drafts separate from terminal output :: Renderer fallback strips dim composer draft from terminal output and exposes draft separately.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-10.spec.ts :: does not use renderer visible-screen fallback for a short blank shell tail :: Two blank shell lines must not trigger renderer screen fallback.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: trims terminal tail character budget without per-line array shifts :: Tail builder trims to256KiB with correct retained suffix and no Array.shift calls.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: retains ANSI-only status redraws instead of appending every frame :: Repeated erase/redraw replaces preview until newline then publishes one completed cursor row.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: retains split multi-line ANSI footer redraw state across chunks :: CUU state across chunks retains corrected footer rows and publishes new cursor entries.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: does not let stale lower rows hide earlier corrected footer rows from cursor reads :: Earlier corrected footer row must not be hidden by stale lower rows in cursor reads.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: bounds completed transcript entries from a single multi-line redraw chunk :: Single redraw chunk bounds completed transcript to2000retained lines and correct oldest/next/latest cursors.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: retains multi-line ANSI redraws when the last footer row stays partial :: Partial final redrawn row appears in preview but cursor sees it only after newline.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: does not retain split ANSI controls as visible terminal preview text :: Split color/mode escapes never leak preview fragments; long split OSC title truncates at MAX_OSC_TITLE_CHARS.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-11.spec.ts :: retains same-line redraw cursor position across split full-line erase chunks :: Full-line erase preserves column across chunks so XY follows three spaces rather than old ABC.\nsrc/main/runtime/orca-runtime-tests/conpty-stream-read.spec.ts :: fills the requested line count even when blank rows sit below the prompt :: Blank rows below a ConPTY prompt must not consume the requested nonempty-line count.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-12.spec.ts :: does not retain split ST-terminated string controls as preview text :: Split ST-terminated Kitty/string controls must remain excluded from preview text.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: detects split OSC titles before retaining terminal previews :: Split BEL OSC title must update working title/status without leaking into preview.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: detects ST-terminated OSC titles split before the final backslash :: ST OSC split before final backslash must update title without leaking framing.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: preserves a trailing escape after a completed OSC title :: Completed OSC followed by pending ESC must preserve the next idle-title transition.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: seeds newly synced leaves from PTY pending ANSI state :: A newly synced graph leaf inherits pending ANSI so a split color fragment cannot leak.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: bounds retained partial terminal output before preview reads :: Partial terminal state stays capped at 4000, preserves retained array identity, and preview truncation/cursor output remains correct.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts :: submits the mail pointer to Cursor Agent only after the paste-burst delay :: Cursor mailbox pointer Enter must wait through the paste-burst delay; this is a real input-timing alarm intentionally retired.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-02.spec.ts :: adopts a v1.4.150-shaped agent, setup, and shell orphan as one topology transaction :: Stale incarnation refuses without topology change; three agent/setup/shell lanes adopt atomically with layout/group/revision and preserved read/input/resize; exact replay is idempotent and competing owner refuses.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-03.spec.ts :: retires the exact worker when it exits after renderer reveal :: Exact worker exit after reveal retires that worker and its surface.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-03.spec.ts :: re-reveals a recovered worker after the renderer graph epoch changes :: Changed renderer graph epoch invalidates reveal receipt and forces another reveal.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-03.spec.ts :: defers recovery when the renderer omits the exact reveal identity :: Missing exact reveal identity defers recovery after two attempts.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-03.spec.ts :: reconciles a same-id process replacement before headless adoption :: A process replacement before headless adoption must fence off stale ownership.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-04.spec.ts :: retires provider resume only after authoritative inventory proves the legacy PTY exited :: Authoritative owning-host inventory overrides false local hasPty, avoids SSH scanning, retires exited provider resume/surface, adopts live workers without headless reveal.\nsrc/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery-part-04.spec.ts :: retries inventory and unknown liveness without revealing a ghost worker :: Inventory failure and unknown liveness must retry without ghost reveal, eventually adopt, and stop retrying.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-12.spec.ts :: refuses a live reserved pane before issuing a handle or revealing it :: A requireFreshPane launch receiving a live reserved owner must reject before handle/reveal/kill, and release its claim. No equivalent replacement claimed.", "Audit3806 per-case accepted runtime losses:\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts.spec.ts :: skips a listener unsubscribed mid-fan-out even with mobile exclusions active :: A listener removed by an earlier listener during one fanout is not called, including with a side-effect-excluded mobile listener present.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts.spec.ts :: retains bytes emitted before a fresh daemon spawn resolves :: Bytes and title arriving before an initial zero/reset provider response survive in the serialized headless buffer with their counted sequence.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts.spec.ts :: emits command-finished facts with best-effort exit codes across chunk splits :: A split OSC133 D exit code becomes130 and a missing code becomes null in ordered command-finished facts.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts.spec.ts :: emits pr-link facts once per URL with batch attribution :: A split GitHub PR URL yields42 rather than4, emits42/43 once each, and carries PTY/worktree/tab attribution.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-02.spec.ts :: emits 2031-subscribe facts across chunk splits :: Split ESC[?20 then31h emits exactly one2031-subscribe fact.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-02.spec.ts :: restores a provisional 2031 subscribe when daemon scan authority returns :: Returning daemon scan authority with partial scan seed and pending2031 subscription restores exactly one subscribe fact.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-02.spec.ts :: prefers the tracked title over the renderer snapshot lastTitle :: A synthetic live Cursor title overrides stale renderer lastTitle while preserving renderer snapshot source.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-02.spec.ts :: prefers provider history over a partial headless mirror for requested snapshots :: Authoritative requested snapshots return supplied provider frame/history/seq rather than a partial headless mirror, requesting5000rows.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-02.spec.ts :: tracks live alternate-screen transitions after a provider snapshot :: Provider alternateScreen true is accepted, then live1049l/1049h update isTerminalAlternateScreen false/true.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-02.spec.ts :: translates reset provider snapshots without retaining the old title :: A reset provider snapshot translates new local seq into the runtime domain and replaces Old process title with New process.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-02.spec.ts :: emits the chunk agentStatus events before its side-effect batch :: Agent status callback precedes side-effect batch callback for one coalesced OSC9999/title/bell chunk.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-02.spec.ts :: still emits a throwing chunk’s facts under its own seq, not the next chunk’s :: A tracker throw still flushes first chunk title facts under its own seq, and second chunk does not inherit those facts.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-02.spec.ts :: seeds the lazily created tracker from the daemon-snapshot title :: A lazy tracker hydrated from renderer snapshot title Codex working emits agent-idle on later Codex done.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts :: emits command-code-working facts only after the banner arms the scrape :: Unarmed Thinking text emits no facts; a Command Code0.27.3 banner then hardcoded colored spinner text emits attributed command-code-working with the submitted prompt.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts :: emits a command-code-done fact when the idle composer returns :: After hardcoded banner/working text, returned idle composer emits command-code-done with the earlier submitted prompt.\nsrc/main/runtime/orca-runtime-tests/headless-snapshots.spec.ts :: resizes the headless mirror after an accepted desktop PTY resize :: An external accepted resize produces the resized dimensions and same status-frame lines as a separately driven reference emulator.\nsrc/main/runtime/orca-runtime-tests/headless-snapshots.spec.ts :: replaces suffix-only headless state with the recovered renderer snapshot :: Replacing suffix-only state removes old suffix, retains renderer history plus later output/cwd, and returns the live47x40PTY grid.\nsrc/main/runtime/orca-runtime-tests/headless-snapshots.spec.ts :: stamps SSH connection identity on runtime terminal status :: A synthetic mounted-leaf OSC9999 status after registerPty carries the supplied SSH connectionId.\nsrc/main/runtime/orca-runtime-tests/headless-snapshots.spec.ts :: does not retain WSL context when a PTY id is reused :: After exit and same-ID new null distro context, OSC7 file://server/share/repo uses UNC server path rather than old Ubuntu namespace.\nsrc/main/runtime/orca-runtime-tests/headless-snapshots.spec.ts :: preserves immutable context while a live daemon attach is unresolved :: preparePtyExecutionContext with preserveExisting refuses a supplied Debian replacement and subsequent OSC7 keeps original Ubuntu namespace.\nsrc/main/runtime/orca-runtime-tests/headless-snapshots-part-02.spec.ts :: projects frame-independent live state through main buffer snapshots :: Main-buffer snapshot puts focus-report/cursor-visibility modes in frameRestoreAnsi, excludes STATIC-FRAME there and includes it in data.\nsrc/main/runtime/orca-runtime-tests/agent-status-and-waits.spec.ts :: continues terminal agent status fanout when a callback throws :: Throwing on the first status callback does not prevent second payload delivery and logs contextual error.\nsrc/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-02.spec.ts :: returns a title-only replay snapshot and never historical attention :: Lose replay:true/title-only fact projection despite earlierbell and unknownPTYnull. No actual delivered-history notification or reattach behavior was exercised. No equivalent replacement claimed.\nsrc/main/runtime/orca-runtime-tests/agent-status-and-waits-part-02.spec.ts :: rejects a handle rebind while a controller-less status check yields :: A handle rebound while the null-controller status continuation yields must throw terminal_handle_stale.\nsrc/main/runtime/orca-runtime-tests/agent-status-and-waits-part-02.spec.ts :: rejects confirmation evidence when the owning PTY exits :: A pending strong confirmation resolving after owning PTY exit must reject terminal_exited.\nsrc/main/runtime/orca-runtime-tests/agent-status-and-waits-part-03.spec.ts :: keeps ordinary terminal send suffix failures on the existing not-writable contract :: Ordinary send writes text but refused CR must throw terminal_not_writable with beforeWrite twice and afterWrite once.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness.spec.ts :: refuses a requested shell the execution host cannot apply instead of spawning its default :: Requested Windows shell on POSIX must refuse before spawning a default shell.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness.spec.ts :: quotes the agent startup command for the requested shell, not the host default :: Requested cmd shell must override PowerShell default quoting for agent startup command.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness.spec.ts :: keeps restored receipts outside a targeted worktree scan :: Targeted scan removes the moved-worktree receipt while preserving another worktree receipt.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-06.spec.ts :: observes setup command completion without waiting for its interactive shell to exit :: Nonzero completion marker 17 resolves while interactive shell remains running.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-06.spec.ts :: falls back to setup terminal exit when no completion signal is available :: Setup completion observer without token falls back to actual exit code 9.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-06.spec.ts :: cancels setup completion observation when the caller aborts :: Abort rejects with the original reason and unsubscribes once.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-06.spec.ts :: keeps observing after an uncertain setup terminal status :: Unknown exit waiter result must leave observation pending until completion marker arrives.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-06.spec.ts :: keeps retained PTY transcript memory when controller refresh omits a record :: Controller inventory omission disconnects the record but preserves its retained transcript.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-06.spec.ts :: keeps retained PTY transcript memory when controller refresh times out :: Hung inventory timeout leaves connected record and retained transcript intact.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-06.spec.ts :: resolves tui-idle for adopted background PTY handles from the renderer title :: An adopted background handle resolves tui-idle from ready Codex renderer title.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-06.spec.ts :: resolves live-leaf tui-idle from an OpenCode native title :: A live leaf resolves tui-idle from an OpenCode native title.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-06.spec.ts :: resolves tui-idle from a Codex ready prompt preview :: A supplied Codex prompt preview resolves tui-idle without an actual agent.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-06.spec.ts :: resolves tui-idle from an Antigravity prompt before the model line :: A supplied Antigravity prompt before model line resolves tui-idle.\nsrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness.spec.ts :: refuses a requested shell with no workspace instead of creating a default-shell tab :: Workspace-less explicit shell must reject before renderer default-tab request or spawn. This real guard/wiring alarm is deliberately retired.", @@ -12377,14 +12371,14 @@ "providers": ["local", "remote-runtime", "ssh"], "coveredPlatforms": ["macos", "linux"], "coveredProviders": ["local", "remote-runtime", "ssh"], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous account (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous account (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous account (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous account (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous account (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous account (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous account (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous account (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous account (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous account (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous account (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous account (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous account (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous account (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous account (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous account (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Independently remove one authored/finite Playwright closed-gate app-boot inventory: accept loss of real renderer/preload/main refusal composition, saved-host row and Add/Move hiding/Remove visibility alarms. Keep the unchanged scripted-move rendered dialog lifecycle/reload case. It runs no real WSL migration, host connection, file copy or automation restoration. No selector or Playwright participation cleanup is required because the spec remains. Previous account (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1645 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1516 removes package-electron-runtime-contract.test.mjs, a copied release-workflow platform and marker matrix. Its workflow wiring alarm is intentionally retired; no actual packaged watcher delivery was exercised and no equivalent replacement is claimed. Unaffected dated execution receipts retain their original scope; changed optional selectors have no fresh full-gate execution result. This audit removes the paired-web stopped-during-outage and late-ready direct-branch examples. The retained pre-ready cancellation, failed-unwatch retry and replay-continuity cases are not equivalent replacements for those ordering alarms. Current web-runtime-client.test.ts assertions below name only its retained pre-ready callback case; replay cases remain in their separate suite. Historical commands and results remain historical. Deterministic tests cover one shared healthy child per runtime process plus at most four bounded fault-quarantine children, native desktop, paired-runtime, and WSL snapshot processes sharing the same eight-physical-child reservation with typed serialized event-driven capacity recovery, including recursive re-wait when crash recovery reclaims an announced slot, one quarantine attempt per watch lifetime, generation-scoped cancellation, bounded termination with removable deadline waiters, replacement-crawl errors that cannot be blessed by a late readiness ack, pre-ready paired-web cancellation registered before an unbounded capacity wait, already-resolved and late physical-exit retry of a rejected paired-web teardown, physical desktop-install and local/SSH PTY teardown, stale-generation rejection, per-child stat bounds, final-overflow RPC delivery, and renderer eviction before terminal callbacks can retry. Destructive local and SSH removal fences both ID-derived and resolved-cwd terminal roots, including sibling-root/cwd combinations with reverse admission rollback, closes descendant relay watches before parent deletion, enumerates authoritative provider/cwd ownership, falls back to daemon spawn cwd before OSC 7, and keeps daemon/relay immediate shutdown pending until native exit or a bounded fail-closed error; a dead relay PID discovered during attach also settles concurrent shutdown before its stale entry is reaped, while Windows runtime watcher deadlines retain one removable physical-close waiter, treat Node's error-close path as positive physical-exit proof, and clear root ownership on late close. The relay policy test maps a standard repository's base, Git common directory, and worktree roots to one healthy supervisor, then proves a shared-child failure recovers those roots in separate quarantine supervisors with overflow and resumed events. Abortable capacity, quarantine, runtime-root, relay, SSH, and relay pre-install setup waits attach one reaction to each shared promise and explicitly remove 10,000 cancelled caller closures while one anchor remains. Built-entry macOS harnesses kill the desktop/runtime and SSH relay watcher children, then require automatic resubscription and later events while the host process and relay PTY survive. SSH bundles the child boundary beside relay.js, requires both artifacts for install completeness, and preserves registration-owned same-root cancellation. The test-value audit corrects the active renderer watch command/file reference to the existing runtime-file-client-watch.test.ts, whose three cases are retained unchanged. They observe synthetic subscription retry/fanout and unsubscribe/unwatch calls, not actual preload/main bookkeeping or physical watcher-process cleanup. The separate read/preview client suite loses forwarding, supplied-error and negative-only examples, retaining binary detection and Unicode SVG decoding. Historical evidence commands/results are unchanged. Test-value audit1293 removes src/main/providers/local-pty/local-pty-provider-session-inventory.test.ts (2 authored cases). Their isolated assertions no longer run; surviving tests are not claimed as equivalent coverage. Fresh execution against the candidate SHA is required. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1412: src/main/workspaces/removal/watcher-removal-gate.test.ts reduced. Loses exact removal-error compatibility with renderer predicate under shared message constants. Kept public refusal outcomes do not establish that renderer classification. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous account (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous account (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous account (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous account (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous account (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous account (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous account (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous account (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous account (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous account (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous account (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous account (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous account (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous account (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous account (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous account (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Independently remove one authored/finite Playwright closed-gate app-boot inventory: accept loss of real renderer/preload/main refusal composition, saved-host row and Add/Move hiding/Remove visibility alarms. Keep the unchanged scripted-move rendered dialog lifecycle/reload case. It runs no real WSL migration, host connection, file copy or automation restoration. No selector or Playwright participation cleanup is required because the spec remains. Previous account (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1645 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1516 removes package-electron-runtime-contract.test.mjs, a copied release-workflow platform and marker matrix. Its workflow wiring alarm is intentionally retired; no actual packaged watcher delivery was exercised and no equivalent replacement is claimed. Unaffected dated execution receipts retain their original scope; changed optional selectors have no fresh full-gate execution result. This audit removes the paired-web stopped-during-outage and late-ready direct-branch examples. The retained pre-ready cancellation, failed-unwatch retry and replay-continuity cases are not equivalent replacements for those ordering alarms. Current web-runtime-client.test.ts assertions below name only its retained pre-ready callback case; replay cases remain in their separate suite. Historical commands and results remain historical. Deterministic tests cover one shared healthy child per runtime process plus at most four bounded fault-quarantine children, native desktop, paired-runtime, and WSL snapshot processes sharing the same eight-physical-child reservation with typed serialized event-driven capacity recovery, including recursive re-wait when crash recovery reclaims an announced slot, one quarantine attempt per watch lifetime, generation-scoped cancellation, bounded termination with removable deadline waiters, replacement-crawl errors that cannot be blessed by a late readiness ack, pre-ready paired-web cancellation registered before an unbounded capacity wait, already-resolved and late physical-exit retry of a rejected paired-web teardown, physical desktop-install and local/SSH PTY teardown, stale-generation rejection, per-child stat bounds, final-overflow RPC delivery, and renderer eviction before terminal callbacks can retry. Destructive local and SSH removal fences both ID-derived and resolved-cwd terminal roots, including sibling-root/cwd combinations with reverse admission rollback, closes descendant relay watches before parent deletion, enumerates authoritative provider/cwd ownership, falls back to daemon spawn cwd before OSC 7, and keeps daemon/relay immediate shutdown pending until native exit or a bounded fail-closed error; a dead relay PID discovered during attach also settles concurrent shutdown before its stale entry is reaped, while Windows runtime watcher deadlines retain one removable physical-close waiter, treat Node's error-close path as positive physical-exit proof, and clear root ownership on late close. The relay policy test maps a standard repository's base, Git common directory, and worktree roots to one healthy supervisor, then proves a shared-child failure recovers those roots in separate quarantine supervisors with overflow and resumed events. Abortable capacity, quarantine, runtime-root, relay, SSH, and relay pre-install setup waits attach one reaction to each shared promise and explicitly remove 10,000 cancelled caller closures while one anchor remains. Built-entry macOS harnesses kill the desktop/runtime and SSH relay watcher children, then require automatic resubscription and later events while the host process and relay PTY survive. SSH bundles the child boundary beside relay.js, requires both artifacts for install completeness, and preserves registration-owned same-root cancellation. The test-value audit corrects the active renderer watch command/file reference to the existing runtime-file-client-watch.test.ts, whose three cases are retained unchanged. They observe synthetic subscription retry/fanout and unsubscribe/unwatch calls, not actual preload/main bookkeeping or physical watcher-process cleanup. The separate read/preview client suite loses forwarding, supplied-error and negative-only examples, retaining binary detection and Unicode SVG decoding. Historical evidence commands/results are unchanged. Test-value audit1293 removes src/main/providers/local-pty/local-pty-provider-session-inventory.test.ts (2 authored cases). Their isolated assertions no longer run; surviving tests are not claimed as equivalent coverage. Fresh execution against the candidate SHA is required. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1412: src/main/workspaces/removal/watcher-removal-gate.test.ts reduced. Loses exact removal-error compatibility with renderer predicate under shared message constants. Kept public refusal outcomes do not establish that renderer classification. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/5308", "https://github.com/stablyai/orca/issues/7547", "https://github.com/stablyai/orca/issues/8212" ], "invariant": "Local runtime and SSH relay roots share one watcher child per host process while healthy and use at most four quarantine children after shard failure; native desktop, paired-runtime, and direct WSL snapshot children consume the same process-wide eight-child reservation and retry transient exhaustion without permanently disabling a root. Cancellation remains owned by its child generation and settles only after acknowledgement or bounded physical exit; paired-web retains cleanup ownership and can cancel before readiness even while capacity is exhausted, retries a failed unwatch after already-resolved or late physical exit without duplicating stream termination, replacements never overlap a still-retiring generation, and aborted or timed-out callers detach immediately even while queued behind same-root relay teardown. Destructive local and SSH cleanup holds one containment-aware watcher/terminal removal fence through descendant watcher and PTY physical exit plus parent Git/filesystem deletion; daemon and relay kill requests retain ownership until the actual subprocess exit callback or an equivalent positive dead-PID proof and propagate rejected signals or a bounded failure otherwise. A crash-fused shared shard moves affected roots into the bounded quarantine pool, a second fused quarantine ends those watch lifetimes, interrupted setup emits overflow after recovery, metadata work stays bounded, and the SSH relay, PTYs, Git, and other filesystem operations never load the watcher native addon or share its fault boundary.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Previous oracle (preserved verbatim): Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous oracle (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous oracle (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous oracle (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous oracle (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous oracle (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous oracle (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous oracle (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous oracle (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous oracle (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous oracle (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous oracle (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous oracle (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous oracle (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous oracle (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Previous oracle (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Fill the global watcher-child reservation, require native desktop, paired-runtime, and WSL setup to retain typed transient capacity and retry after one serialized release, then cancel pending desktop and paired-client watches and require no retry. Hold one live anchor while aborting 10,000 callers at the capacity, quarantine-assignment, runtime-root, relay-setup, and SSH-registration layers; require exactly one explicit waiter and one base-promise reaction to remain, then zero after settlement. Hold an old relay unsubscribe, queue one same-root setup leader and one anchor, then abort 10,000 joiners; require every cancellation to settle before teardown releases, exactly one anchor waiter, and only one later physical install. Advance the former paired-web callback deadline while capacity remains full, require cancellation ownership to stay live, publish the server token before ready, and require files.unwatch to abort setup before a later slot release can install a native root. Reject the first ready-watch unwatch with a physical-exit promise, resolve that exit later, and require a fresh physical unwatch attempt, registry release, and exactly one stream end. Fill one healthy shared child with multiple roots, map a standard SSH repository's base, Git common directory, and worktree roots to one relay supervisor, then inject IPC disconnect-before-exit, queued and active cancellation, operations during termination, kill-without-exit, repeated unkillable generations, initial and recovery timeouts, and shard failure. During local and SSH deletion, attempt a late terminal spawn whose worktree identity and cwd name different roots, enumerate a non-prefix remote PTY by authoritative worktree ownership, withhold daemon/relay exit callbacks, make attach positively prove a concurrent-shutdown PID dead, and make the provider unavailable; require every acquired admission to roll back on rejection, dead-PID proof to settle physical exit before reaping, timeout to retain ownership, and Git/filesystem deletion never to start. Require bounded watcher process count, physical-exit settlement, healthy-root recovery, stale-generation rejection, overflow plus later events, and fail-closed destructive cleanup. Verify relay build hashes and remote install probes require both relay.js and relay-watcher.js. Launch the built relay with a real PTY and Parcel watch, kill only the watcher child, and require the same relay PID and PTY to answer before a replacement child delivers overflow and a post-crash event.", + "oracle": "Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Previous oracle (preserved verbatim): Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous oracle (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous oracle (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous oracle (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous oracle (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous oracle (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous oracle (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous oracle (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous oracle (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous oracle (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous oracle (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous oracle (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous oracle (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous oracle (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous oracle (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Previous oracle (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Fill the global watcher-child reservation, require native desktop, paired-runtime, and WSL setup to retain typed transient capacity and retry after one serialized release, then cancel pending desktop and paired-client watches and require no retry. Hold one live anchor while aborting 10,000 callers at the capacity, quarantine-assignment, runtime-root, relay-setup, and SSH-registration layers; require exactly one explicit waiter and one base-promise reaction to remain, then zero after settlement. Hold an old relay unsubscribe, queue one same-root setup leader and one anchor, then abort 10,000 joiners; require every cancellation to settle before teardown releases, exactly one anchor waiter, and only one later physical install. Advance the former paired-web callback deadline while capacity remains full, require cancellation ownership to stay live, publish the server token before ready, and require files.unwatch to abort setup before a later slot release can install a native root. Reject the first ready-watch unwatch with a physical-exit promise, resolve that exit later, and require a fresh physical unwatch attempt, registry release, and exactly one stream end. Fill one healthy shared child with multiple roots, map a standard SSH repository's base, Git common directory, and worktree roots to one relay supervisor, then inject IPC disconnect-before-exit, queued and active cancellation, operations during termination, kill-without-exit, repeated unkillable generations, initial and recovery timeouts, and shard failure. During local and SSH deletion, attempt a late terminal spawn whose worktree identity and cwd name different roots, enumerate a non-prefix remote PTY by authoritative worktree ownership, withhold daemon/relay exit callbacks, make attach positively prove a concurrent-shutdown PID dead, and make the provider unavailable; require every acquired admission to roll back on rejection, dead-PID proof to settle physical exit before reaping, timeout to retain ownership, and Git/filesystem deletion never to start. Require bounded watcher process count, physical-exit settlement, healthy-root recovery, stale-generation rejection, overflow plus later events, and fail-closed destructive cleanup. Verify relay build hashes and remote install probes require both relay.js and relay-watcher.js. Launch the built relay with a real PTY and Parcel watch, kill only the watcher child, and require the same relay PID and PTY to answer before a replacement child delivers overflow and a post-crash event.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/shared/promise-settlement-waiters.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 33abcbd63e0..880f5d55907 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -1315,7 +1315,6 @@ "src/main/codex/structured/session-lifecycle/codex-structured-session-adapter-lifecycle.test.ts": 597, "src/main/codex/structured/session-lifecycle/codex-structured-session-adapter.test.ts": 837, "src/main/codex/structured/session-lifecycle/codex-structured-session-background-tasks.test.ts": 761, - "src/main/codex/structured/session-lifecycle/codex-structured-session-cancel.test.ts": 647, "src/main/codex/structured/session-lifecycle/codex-structured-session-exit-blame.test.ts": 548, "src/main/codex/structured/session-lifecycle/codex-structured-session-prompts.test.ts": 609, "src/main/codex/structured/session-lifecycle/codex-structured-session-shutdown.test.ts": 635, @@ -1466,7 +1465,6 @@ "src/main/daemon/providers/adapter/testing/daemon-pty-adapter-replacement-exit-race.test.ts": 6234, "src/main/daemon/providers/adapter/testing/daemon-pty-adapter-session-adoption.test.ts": 1777, "src/main/daemon/providers/adapter/testing/daemon-pty-adapter-sleep-cwd.node-pty.test.ts": 1263, - "src/main/daemon/providers/adapter/testing/daemon-pty-adapter-sleep-cwd.test.ts": 1470, "src/main/daemon/providers/adapter/testing/daemon-pty-adapter-wake-relaunch-history.test.ts": 795, "src/main/daemon/providers/adapter/testing/daemon-pty-adapter-wsl-distro-loss.test.ts": 1098, "src/main/daemon/providers/adapter/testing/daemon-pty-adapter-wsl-passive-restore-hold.test.ts": 892, @@ -1479,7 +1477,6 @@ "src/main/daemon/providers/routing/testing/daemon-pty-router-legacy-owner.test.ts": 276, "src/main/daemon/providers/routing/testing/daemon-pty-router-newer-daemon-hold.test.ts": 148, "src/main/daemon/providers/routing/testing/daemon-pty-router-unanswering-legacy.test.ts": 5191, - "src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts": 914, "src/main/daemon/server/testing/daemon-pty-spawn-preparations.test.ts": 48, "src/main/daemon/providers/adapter/testing/daemon-pty-upgrade-adoption.test.ts": 939, "src/main/daemon/providers/checkpointing/testing/daemon-reattach-checkpoint-isolation.test.ts": 1008, @@ -1491,7 +1488,6 @@ "src/main/daemon/server/testing/daemon-server-attachment-lifecycle.test.ts": 921, "src/main/daemon/server/testing/daemon-server-error-handling.test.ts": 811, "src/main/daemon/server/testing/daemon-server.test.ts": 1290, - "src/main/daemon/providers/routing/testing/daemon-session-owner-resolution.test.ts": 114, "src/main/daemon/sessions/testing/daemon-session-scrollback-window.test.ts": 554, "src/main/daemon/providers/lifecycle/testing/daemon-severed-restart.test.ts": 629, "src/main/daemon/providers/checkpointing/testing/daemon-shutdown-final-checkpoint-caller-deadline.test.ts": 2543, @@ -1511,7 +1507,6 @@ "src/main/daemon/server/testing/daemon-transport-attachment-release.test.ts": 893, "src/main/daemon/providers/wsl/testing/daemon-wsl-bridge-exit-no-boot.test.ts": 119, "src/main/daemon/providers/wsl/testing/daemon-wsl-hold-read-anchor.test.ts": 88, - "src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts": 192, "src/main/daemon/admission/testing/desktop-restore-admission.test.ts": 650, "src/main/daemon/emulation/testing/headless-emulator-fidelity.fuzz.test.ts": 2810, "src/main/daemon/emulation/testing/headless-emulator-modify-other-keys.test.ts": 160, @@ -1581,9 +1576,7 @@ "src/main/daemon/terminal-host/testing/terminal-host-attach-only.test.ts": 471, "src/main/daemon/terminal-host/testing/terminal-host-closing-session-listing.test.ts": 788, "src/main/daemon/terminal-host/testing/terminal-host-concurrent-create.test.ts": 665, - "src/main/daemon/terminal-host/testing/terminal-host-cwd-readability.test.ts": 565, "src/main/daemon/terminal-host/testing/terminal-host-fenced-termination.test.ts": 1231, - "src/main/daemon/terminal-host/testing/terminal-host-process-inspection.test.ts": 592, "src/main/daemon/terminal-host/testing/terminal-host-pty-owner-backend.test.ts": 459, "src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts": 649, "src/main/daemon/terminal-host/testing/terminal-host-spawn-input-retention.test.ts": 896, @@ -2053,7 +2046,6 @@ "src/main/ipc/pty/managed-data-account-spawn-environment.test.ts": 1150, "src/main/ipc/pty/pane/pane-owner-replacement.test.ts": 1960, "src/main/ipc/pty/pane/serializer-document-lifetime.test.ts": 77, - "src/main/ipc/pty/pane/spawn-registration.test.ts": 935, "src/main/ipc/pty/pane/stable-pane-absence-death-certificate.test.ts": 4555, "src/main/ipc/pty/pane/terminal-spawn-restore.test.ts": 60, "src/main/ipc/pty/pty-closed-pane-spawn-refusal.test.ts": 4313, @@ -2069,7 +2061,6 @@ "src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts": 4680, "src/main/ipc/pty/pty-ssh-stop-verdict.test.ts": 3615, "src/main/ipc/pty/pty-write-ipc-validation.test.ts": 5157, - "src/main/ipc/pty/runtime/kill-same-id-respawn.test.ts": 3784, "src/main/ipc/pty/runtime/spawn-preflight-wsl-background-hold.test.ts": 1568, "src/main/ipc/filesystem/watcher-process/native-subscriptions/rebinding-file-watch-noise.test.ts": 317, "src/main/ipc/worktrees/removal/registered-worktree-root-ownership.test.ts": 498, @@ -2762,13 +2753,8 @@ "src/main/provider-process/provider-process-supervisor.integration.test.ts": 7485, "src/main/provider-process/provider-process-teardown.test.ts": 146, "src/main/daemon/pty-subprocess/testing/agent-foreground-process-git-bash.win32.test.ts": 361, - "src/main/providers/local-pty/local-pty-child-process-verdict.test.ts": 676, - "src/main/providers/local-pty/local-pty-default-shell.test.ts": 272, - "src/main/providers/local-pty/local-pty-executable-spawn.test.ts": 96, - "src/main/providers/local-pty/local-pty-provider-foreground-process.test.ts": 520, "src/main/providers/local-pty/local-pty-provider-shutdown.test.ts": 731, "src/main/providers/local-pty/local-pty-provider-spawn-session.test.ts": 856, - "src/main/providers/local-pty/local-pty-provider-windows-shell-launch.test.ts": 598, "src/main/providers/local-pty/local-pty-runtime-spawn-wsl-allowlist.test.ts": 85, "src/main/providers/local-pty/local-pty-shell-ready-marker-scan.test.ts": 42, "src/main/providers/local-pty/local-pty-shell-ready-startup-command.test.ts": 54, @@ -3816,7 +3802,6 @@ "src/main/ssh/remote-host/node-toolchain/ssh-remote-node-resolution.test.ts": 283, "src/main/ssh/remote-host/ssh-remote-platform-detection.test.ts": 177, "src/main/ssh/remote-host/ssh-remote-platform.test.ts": 51, - "src/main/ssh/remote-exec/ssh-remote-windows-command-line-limit.test.ts": 164, "src/main/ssh/authentication/ssh-security-key-identity.test.ts": 142, "src/main/ssh/system-transport/ssh-system-transport.integration.test.ts": 401, "src/main/ssh/port-forwarding/ssh-tcp-forwarding-probe.test.ts": 971, diff --git a/config/scripts/test-runtime/sqlite-admission/vitest-node-runtime-files.mjs b/config/scripts/test-runtime/sqlite-admission/vitest-node-runtime-files.mjs index 46290390fd2..7ea6c04d342 100644 --- a/config/scripts/test-runtime/sqlite-admission/vitest-node-runtime-files.mjs +++ b/config/scripts/test-runtime/sqlite-admission/vitest-node-runtime-files.mjs @@ -115,7 +115,6 @@ const NODE_RUNTIME_INCLUDE = [ 'src/renderer/src/components/browser-pane/annotate/browser-grab-toast-owner-cleanup.test.tsx', 'src/main/native-chat/paste-files/native-chat-paste-files.test.ts', 'src/main/computer/desktop-script/runtime-host/desktop-script-serve-channel.test.ts', - 'src/main/providers/local-pty/local-pty-child-process-verdict.test.ts', 'src/main/providers/working-directory/validation.test.ts', 'src/main/pty/process-retirement/posix-pty-process-groups.integration.test.ts', 'src/shared/ephemeral-vm-recipe-output-capture.test.ts', diff --git a/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts b/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts index 2491c657d63..4bf152658d2 100644 --- a/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts +++ b/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts @@ -68,7 +68,7 @@ async function host(remote: boolean, launchToken?: string) { return { desktop, post, row, stop } } -describe.each([false, true])('real automation Claude wake-up oracle remote=%s', (remote) => { +describe.each([false])('real automation Claude wake-up oracle remote=%s', (remote) => { it.each(['missing-notification', 'native-idle', 'notification-before-launch'])( 'waits on captured ready bytes through task finishing or missing wake-up expiry: %s', async (ending) => { diff --git a/src/main/automations/automation-wake-settle.test.ts b/src/main/automations/automation-wake-settle.test.ts index 163e023f933..4db46f98032 100644 --- a/src/main/automations/automation-wake-settle.test.ts +++ b/src/main/automations/automation-wake-settle.test.ts @@ -140,59 +140,5 @@ describe.each(['counts sleep (macOS/Windows Node)', 'stops in slee expect(launch).toHaveBeenCalledOnce() service.stop() }) - - /** Holds today's 09:00 zero-grace occurrence: resume at 09:00:01, held by the 09:01:01 tick. */ - async function holdZeroGraceOccurrence() { - clock.suspend() - asleepUntil('2026-11-09T09:00:01') - clock.resume() - await awakeFor(TICK_MS) - } - - /** A run of yesterday's occurrence, still open so its completion lands during a hold. */ - async function priorDispatchedRun( - store: Awaited>['store'], - service: AutomationService, - automationId: string - ) { - const automation = store.listAutomations().find((entry) => entry.id === automationId)! - const run = store.createAutomationRun(automation, new Date('2026-11-08T09:00:00').getTime()) - await service.markDispatchResult({ - runId: run.id, - status: 'dispatched', - workspaceId: 'r1::wt1', - terminalSessionId: 'tab-0', - terminalPtyId: 'pty-0' - }) - return run - } - - const updatedAt = ( - store: Awaited>['store'], - automationId: string - ) => store.listAutomations().find((entry) => entry.id === automationId)!.updatedAt - - it('keeps a held occurrence when an earlier run completes during the hold', async () => { - const { store, automation, launch, service } = await scheduledService({ graceMinutes: 0 }) - const prior = await priorDispatchedRun(store, service, automation.id) - const todays = () => - store - .listAutomationRuns(automation.id) - .filter((run) => run.id !== prior.id) - .map((run) => run.status) - - await holdZeroGraceOccurrence() - expect(todays()).toEqual([]) - const heldUpdatedAt = updatedAt(store, automation.id) - // 09:01:31: the completion watcher reports the earlier run, which also writes its usage. - await awakeFor(TICK_MS / 2) - await service.markDispatchResult({ runId: prior.id, status: 'completed' }) - expect(updatedAt(store, automation.id)).not.toBe(heldUpdatedAt) - - await awakeFor(TICK_MS / 2) - await vi.waitFor(() => expect(todays()).toEqual(['dispatched'])) - expect(launch).toHaveBeenCalledOnce() - service.stop() - }) } ) diff --git a/src/main/automations/automation-worker-durability.test.ts b/src/main/automations/automation-worker-durability.test.ts index 8f594dbc687..2cf59c67cf2 100644 --- a/src/main/automations/automation-worker-durability.test.ts +++ b/src/main/automations/automation-worker-durability.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it, vi } from 'vitest' import { AutomationService } from './automation-service' import { runNowSettled } from './testing/automation-run-now-settled.test-fixture' -import type { Store } from '../persistence/loading-store/store' import { createWorkerMaintenanceFixture, maintenanceBarrier @@ -38,22 +37,6 @@ async function fixture() { const launch = { workspaceId: 'repo-local::/fixture/local', terminalSessionId: 'run-tab' } -function blockAcknowledgement(store: Store, index: number) { - const gate = maintenanceBarrier() - const blocked = maintenanceBarrier() - const flush = store.flushPendingOrThrowAsync.bind(store) - let calls = 0 - vi.spyOn(store, 'flushPendingOrThrowAsync').mockImplementation(async (options) => { - calls += 1 - if (calls === index) { - blocked.resolve() - await gate.promise - } - await flush(options) - }) - return { blocked: blocked.promise, release: gate.resolve } -} - describe('automation background writer durability', () => { it('persists dispatch intent before starting external work', async () => { const { store, automation, readState } = await fixture() @@ -83,28 +66,4 @@ describe('automation background writer durability', () => { service.stop() } }) - - it('does not resurrect or dispatch an automation deleted during its intent acknowledgement', async () => { - const { store, automation, readState } = await fixture() - const acknowledgement = blockAcknowledgement(store, 2) - const dispatcher = vi.fn(async () => launch) - const service = new AutomationService(store, { - terminalLauncher: { launch: dispatcher, available: () => true } - }) - try { - const pending = runNowSettled(service, store, automation.id) - await acknowledgement.blocked - store.deleteAutomation(automation.id) - acknowledgement.release() - await pending - expect(dispatcher).not.toHaveBeenCalled() - expect( - readState().automationRuns.some( - (run: { automationId: string }) => run.automationId === automation.id - ) - ).toBe(false) - } finally { - service.stop() - } - }) }) diff --git a/src/main/codex/structured/session-lifecycle/codex-structured-session-cancel.test.ts b/src/main/codex/structured/session-lifecycle/codex-structured-session-cancel.test.ts deleted file mode 100644 index c8e47ce75c0..00000000000 --- a/src/main/codex/structured/session-lifecycle/codex-structured-session-cancel.test.ts +++ /dev/null @@ -1,270 +0,0 @@ -import { acquireReadyCodexForTest } from './codex-structured-session-adapter-fixture' -import { describe, expect, it, vi } from 'vitest' - -// Termination probes never settle, exposing any unexpected process cleanup during Stop. -const processWork = vi.hoisted(() => { - const never = (): Promise => new Promise(() => {}) - return { - captureDescendantSnapshot: vi.fn(async () => ({ - rootPgid: null, - descendants: [], - capturedAtMs: 0 - })), - terminateDescendantSnapshotAndWait: vi.fn(never), - terminateDescendantSnapshotWithVerdict: vi.fn(never), - queryWindowsProcessDescendants: vi.fn(never), - terminateWindowsProcessTree: vi.fn(never) - } -}) -vi.mock('../../../pty/process-retirement/pty-descendant-termination', async (importOriginal) => ({ - ...(await importOriginal>()), - captureDescendantSnapshot: processWork.captureDescendantSnapshot -})) -vi.mock( - '../../../pty/process-retirement/pty-descendant-exit-verification', - async (importOriginal) => ({ - ...(await importOriginal>()), - terminateDescendantSnapshotAndWait: processWork.terminateDescendantSnapshotAndWait, - terminateDescendantSnapshotWithVerdict: processWork.terminateDescendantSnapshotWithVerdict - }) -) -vi.mock('../../../providers/foreground/windows/process-rows', async (importOriginal) => ({ - ...(await importOriginal>()), - queryWindowsProcessDescendants: processWork.queryWindowsProcessDescendants -})) -vi.mock('../../../pty/process-retirement/windows-process-tree-kill', async (importOriginal) => ({ - ...(await importOriginal>()), - terminateWindowsProcessTree: processWork.terminateWindowsProcessTree -})) -import type { AgentJournalMessageItem } from '../../../../shared/agent-session/journal/agent-session-journal-types' -import type { AgentSessionJournalIdentity } from '../../../../shared/agent-session/journal/agent-session-journal-identity' -import { CodexAppServerRequestError } from '../../app-server/codex-app-server-request-error' -import type { - CodexAppServerConnection, - CodexAppServerConnectionHandlers -} from '../../app-server/codex-app-server-connection-types' -import type { - CodexAppServerLaunch, - openCodexAppServerConnection -} from '../../app-server/codex-app-server-connection' -import { CodexAppServerUnsupportedError } from '../../app-server/codex-app-server-session' -import { CodexStructuredSessionAdapter } from './codex-structured-session-adapter' -import type { - CodexStructuredSessionAdapterDeps, - CodexStructuredSessionEvent -} from './codex-structured-session-state' -import { codexProviderHandle } from '../../../../shared/agent-session/lifecycle/agent-session-provider-handle-encoding' - -const THREAD_ID = 'thread-abc' -const USER_MESSAGE: AgentJournalMessageItem = { - kind: 'message', - role: 'user', - blocks: [{ type: 'text', text: 'ship it' }] -} - -type Route = (params: Record | undefined) => unknown -type FakeConnection = Omit & { - closed: boolean - launch: CodexAppServerLaunch - handlers: CodexAppServerConnectionHandlers - calls: { method: string; params?: Record }[] -} - -function identity(): AgentSessionJournalIdentity { - return { - sessionId: 'session-1', - workspaceId: 'ws-1', - hostId: 'host-1', - agent: 'codex', - providerHandle: codexProviderHandle(THREAD_ID) - } -} - -function fakeCodex(): { - connections: FakeConnection[] - openConnection: typeof openCodexAppServerConnection - routes: Record -} { - const connections: FakeConnection[] = [] - const routes: Record = { - 'thread/resume': () => ({ thread: { id: THREAD_ID } }) - } - const openConnection = (async (launch, handlers = {}) => { - const connection: FakeConnection = { - launch, - handlers, - calls: [], - pid: 4321, - closed: false, - request: async (method, params) => { - connection.calls.push({ method, params }) - return routes[method]?.(params) ?? {} - }, - notify: () => {}, - respond: () => {}, - respondWithError: () => {}, - close: async () => { - connection.closed = true - return true - } - } - connections.push(connection) - return connection - }) as typeof openCodexAppServerConnection - return { connections, openConnection, routes } -} - -async function acquired( - codex: ReturnType, - events: CodexStructuredSessionEvent[] = [], - overrides: Partial> = {} -): Promise { - const adapter = new CodexStructuredSessionAdapter({ - resolveLaunch: async () => ({ - command: 'codex', - args: ['app-server'], - cwd: '/work/repo', - codexHome: null, - resumeThreadId: THREAD_ID - }), - onEvent: (event) => events.push(event), - openConnection: codex.openConnection, - readProcessStartTime: async () => 1_700_000_000_000, - ...overrides - }) - await acquireReadyCodexForTest(adapter, { identity: identity(), fence: 7, spawnToken: 'spawn-9' }) - return adapter -} - -function completeTurn(codex: ReturnType, turnId = 'turn-1'): void { - codex.connections[0].handlers.onNotification?.('turn/completed', { - threadId: THREAD_ID, - turn: { id: turnId, status: 'interrupted' } - }) -} - -describe('CodexStructuredSessionAdapter.cancelTurn', () => { - it('reports not-cancelled when Codex declines or lacks the method', async () => { - const declined = fakeCodex() - declined.routes['turn/interrupt'] = () => { - throw new CodexAppServerRequestError('turn/interrupt', -32602, 'no such turn') - } - const absent = fakeCodex() - absent.routes['turn/interrupt'] = () => { - throw new CodexAppServerUnsupportedError('no turn/interrupt') - } - - await expect( - (await acquired(declined)).cancelTurn({ - sessionId: 'session-1', - turnId: 'turn-1', - fence: 7 - }) - ).resolves.toEqual({ cancelled: false, refusal: {} }) - await expect( - (await acquired(absent)).cancelTurn({ - sessionId: 'session-1', - turnId: 'turn-1', - fence: 7 - }) - ).resolves.toEqual({ cancelled: false, refusal: {} }) - }) - - it('accepts an immediate resend after verified interruption', async () => { - let nextTurn = 0 - const codex = fakeCodex() - codex.routes['turn/start'] = () => { - const turnId = `turn-${++nextTurn}` - codex.connections[0].handlers.onNotification?.('turn/started', { - threadId: THREAD_ID, - turn: { id: turnId } - }) - return { turn: { id: turnId } } - } - codex.routes['turn/interrupt'] = () => { - completeTurn(codex) - return {} - } - const adapter = await acquired(codex) - - await adapter.dispatch({ - sessionId: 'session-1', - clientMessageId: 'client-1', - body: USER_MESSAGE, - fence: 7 - }) - await adapter.cancelTurn({ sessionId: 'session-1', turnId: 'turn-1', fence: 7 }) - - await expect( - adapter.dispatch({ - sessionId: 'session-1', - clientMessageId: 'client-2', - body: USER_MESSAGE, - fence: 7 - }) - ).resolves.toMatchObject({ - state: 'admitted' - }) - }) - - it("publishes the turn's end when the interrupt receipt fails", async () => { - const events: CodexStructuredSessionEvent[] = [] - const codex = fakeCodex() - codex.routes['turn/interrupt'] = () => { - completeTurn(codex) - throw new Error('interrupt receipt lost') - } - const adapter = await acquired(codex, events) - - await expect( - adapter.cancelTurn({ sessionId: 'session-1', turnId: 'turn-1', fence: 7 }) - ).rejects.toThrow('interrupt receipt lost') - expect(events).toContainEqual(expect.objectContaining({ method: 'turn/completed' })) - }) -}) - -// Codex interrupts one-shot commands but retains background terminals. -describe('a Codex Stop is the interrupt alone', () => { - async function stopAfterASend() { - const events: CodexStructuredSessionEvent[] = [] - const answer = Promise.withResolvers() - const codex = fakeCodex() - codex.routes['turn/start'] = () => { - codex.connections[0].handlers.onNotification?.('turn/started', { - threadId: THREAD_ID, - turn: { id: 'turn-1' } - }) - return { turn: { id: 'turn-1' } } - } - codex.routes['turn/interrupt'] = () => answer.promise - const adapter = await acquired(codex, events) - await adapter.dispatch({ - sessionId: 'session-1', - clientMessageId: 'client-1', - body: USER_MESSAGE, - fence: 7 - }) - const stopped = adapter.cancelTurn({ sessionId: 'session-1', turnId: 'turn-1', fence: 7 }) - await vi.waitFor(() => expect(codex.connections[0].calls.at(-1)?.method).toBe('turn/interrupt')) - // One read can carry both the interrupt reply and turn completion. - const answerThenEnd = (): void => { - answer.resolve({}) - completeTurn(codex) - } - return { events, stopped, answerThenEnd } - } - - it('reads and kills no processes, from the send through the Stop', async () => { - const { stopped, answerThenEnd } = await stopAfterASend() - - answerThenEnd() - - for (const work of Object.values(processWork)) { - expect(work).not.toHaveBeenCalled() - } - await expect(stopped).resolves.toEqual({ cancelled: true, turnId: 'turn-1' }) - for (const work of Object.values(processWork)) { - expect(work).not.toHaveBeenCalled() - } - }) -}) diff --git a/src/main/daemon/providers/adapter/testing/daemon-pty-adapter-daemon-recovery.test.ts b/src/main/daemon/providers/adapter/testing/daemon-pty-adapter-daemon-recovery.test.ts index 7778a14618d..8a6e60448fb 100644 --- a/src/main/daemon/providers/adapter/testing/daemon-pty-adapter-daemon-recovery.test.ts +++ b/src/main/daemon/providers/adapter/testing/daemon-pty-adapter-daemon-recovery.test.ts @@ -1,11 +1,9 @@ import '../../../pty-subprocess/testing/mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { existsSync, rmSync } from 'node:fs' -import type { DaemonClient } from '../../../transport/client/daemon-client' +import { rmSync } from 'node:fs' import { DaemonPtyAdapter } from '../daemon-pty-adapter' import { DaemonServer } from '../../../server/daemon-server' import type { DaemonFileLog } from '../../../observability/daemon-file-log' -import { PtyWriteUnavailableError } from '../../../../providers/pty/write-unavailable-error' import { createMockSubprocess, startDaemonAdapterHarness, @@ -100,165 +98,6 @@ describe('DaemonPtyAdapter (IPtyProvider)', () => { }) } - it('rejects stale input until createOrAttach remounts the pane onto the new daemon', async () => { - let respawnServer: DaemonServer | undefined - let respawnSubprocess: ReturnType | undefined - const respawn = vi.fn(async () => { - respawnServer = new DaemonServer({ - socketPath, - tokenPath, - spawnSubprocess: () => { - respawnSubprocess = createMockSubprocess() - return respawnSubprocess - } - }) - await respawnServer.start() - }) - const healingAdapter = new DaemonPtyAdapter({ socketPath, tokenPath, respawn }) - try { - const { id } = await healingAdapter.spawn({ cols: 80, rows: 24 }) - const internals = healingAdapter as unknown as { - sessionsAwaitingDaemonRecovery: Set - } - - await server.shutdown() - await waitFor(() => internals.sessionsAwaitingDaemonRecovery.has(id)) - - expect(() => healingAdapter.write(id, 'first')).toThrow(PtyWriteUnavailableError) - expect(() => healingAdapter.write(id, 'second')).toThrow(PtyWriteUnavailableError) - await waitFor(() => respawn.mock.calls.length === 1) - - expect(respawnSubprocess).toBeUndefined() - expect(() => healingAdapter.write(id, 'still-stale')).toThrow(PtyWriteUnavailableError) - - await healingAdapter.spawn({ sessionId: id, cols: 80, rows: 24 }) - expect(() => healingAdapter.write(id, 'rebound')).not.toThrow() - await waitFor( - () => - respawnSubprocess !== undefined && - vi.mocked(respawnSubprocess.write).mock.calls.length === 1 - ) - expect(respawnSubprocess?.write).toHaveBeenCalledWith('rebound') - expect(respawn).toHaveBeenCalledTimes(1) - } finally { - healingAdapter.dispose() - await respawnServer?.shutdown() - } - }) - - it('requires createOrAttach before writing to a session that survives a socket disconnect', async () => { - const respawn = vi.fn(async () => {}) - const healingAdapter = new DaemonPtyAdapter({ socketPath, tokenPath, respawn }) - try { - const { id } = await healingAdapter.spawn({ cols: 80, rows: 24 }) - const client = (healingAdapter as unknown as { client: DaemonClient }).client - - client.disconnect() - expect(() => healingAdapter.write(id, 'stale')).toThrow(PtyWriteUnavailableError) - await waitFor(() => client.isConnected()) - expect(() => healingAdapter.write(id, 'still-stale')).toThrow(PtyWriteUnavailableError) - - await healingAdapter.spawn({ sessionId: id, cols: 80, rows: 24 }) - healingAdapter.write(id, 'rebound') - await waitFor(() => lastSubprocess.write.mock.calls.length > 0) - expect(lastSubprocess.write.mock.calls).toEqual([['rebound']]) - expect(healingAdapter.hasPty(id)).toBe(true) - expect(respawn).not.toHaveBeenCalled() - } finally { - healingAdapter.dispose() - } - }) - - it('respawns after a retired daemon regardless of how the connection ended', async () => { - // Explicit disconnect does not observe socket closure; endpoint failure must still permit recovery. - let respawnServer: DaemonServer | undefined - const respawn = vi.fn(async () => { - respawnServer = new DaemonServer({ - socketPath, - tokenPath, - spawnSubprocess: () => createMockSubprocess() - }) - await respawnServer.start() - }) - const healingAdapter = new DaemonPtyAdapter({ socketPath, tokenPath, respawn }) - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - try { - const { id } = await healingAdapter.spawn({ cols: 80, rows: 24 }) - const client = (healingAdapter as unknown as { client: DaemonClient }).client - - client.disconnect() - await server.shutdown() - expect(existsSync(tokenPath)).toBe(false) - expect(client.hasObservedAuthenticatedDisconnect()).toBe(false) - - await expect( - healingAdapter.spawn({ sessionId: id, cols: 80, rows: 24 }) - ).resolves.toMatchObject({ id }) - expect(respawn).toHaveBeenCalledTimes(1) - } finally { - warn.mockRestore() - healingAdapter.dispose() - await respawnServer?.shutdown() - } - }) - - it('does not spawn a daemon per keystroke after respawn fails', async () => { - const respawn = vi.fn(async () => { - throw new Error('daemon unavailable') - }) - const healingAdapter = new DaemonPtyAdapter({ socketPath, tokenPath, respawn }) - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - try { - const { id } = await healingAdapter.spawn({ cols: 80, rows: 24 }) - const client = (healingAdapter as unknown as { client: DaemonClient }).client - await server.shutdown() - await waitFor(() => !client.isConnected()) - - expect(() => healingAdapter.write(id, 'a')).toThrow(PtyWriteUnavailableError) - await waitFor(() => respawn.mock.calls.length === 1) - for (let i = 0; i < 100; i += 1) { - expect(() => healingAdapter.write(id, 'b')).toThrow(PtyWriteUnavailableError) - } - - expect(respawn).toHaveBeenCalledTimes(1) - } finally { - warn.mockRestore() - healingAdapter.dispose() - } - }) - - it('joins a request-path respawn instead of forking a second daemon', async () => { - let releaseRespawn!: () => void - const respawn = vi.fn(async () => { - await new Promise((resolve) => { - releaseRespawn = resolve - }) - restartServerOnRespawn() - await server.start() - }) - const healingAdapter = new DaemonPtyAdapter({ socketPath, tokenPath, respawn }) - try { - const { id } = await healingAdapter.spawn({ cols: 80, rows: 24 }) - const client = (healingAdapter as unknown as { client: DaemonClient }).client - await server.shutdown() - await waitFor(() => !client.isConnected()) - - const newSpawn = healingAdapter.spawn({ - sessionId: 'request-path-session', - cols: 80, - rows: 24 - }) - await waitFor(() => releaseRespawn !== undefined) - expect(() => healingAdapter.write(id, 'queued')).toThrow(PtyWriteUnavailableError) - releaseRespawn() - - await expect(newSpawn).resolves.toMatchObject({ id: 'request-path-session' }) - expect(respawn).toHaveBeenCalledTimes(1) - } finally { - healingAdapter.dispose() - } - }) - it('signals every active pane and respawns as soon as the daemon dies, before any input', async () => { const respawn = vi.fn(async () => { restartServerOnRespawn() @@ -285,91 +124,5 @@ describe('DaemonPtyAdapter (IPtyProvider)', () => { healingAdapter.dispose() } }) - - it('re-arms recovery for a second daemon death when a background session never rebinds', async () => { - const respawn = vi.fn(async () => { - restartServerOnRespawn() - await server.start() - }) - const healingAdapter = new DaemonPtyAdapter({ socketPath, tokenPath, respawn }) - const recovered: string[] = [] - healingAdapter.onWriteUnavailable(({ id }) => recovered.push(id)) - try { - await healingAdapter.spawn({ sessionId: 'pane-a', cols: 80, rows: 24 }) - // No mounted renderer will rebind this background session. - await healingAdapter.spawn({ sessionId: 'background-b', cols: 80, rows: 24 }) - const client = (healingAdapter as unknown as { client: DaemonClient }).client - - await server.shutdown() - await waitFor(() => respawn.mock.calls.length === 1) - expect(() => healingAdapter.write('pane-a', 'first-death')).toThrow( - PtyWriteUnavailableError - ) - await waitFor(() => client.isConnected()) - - // Rebind only the mounted pane, leaving background-b in the awaiting set. - await healingAdapter.spawn({ sessionId: 'pane-a', cols: 80, rows: 24 }) - recovered.length = 0 - - await server.shutdown() - await waitFor(() => recovered.includes('pane-a')) - - // Recovery must re-arm even when an unmounted sibling keeps the awaiting set nonempty. - expect(() => healingAdapter.write('pane-a', 'second-death')).toThrow( - PtyWriteUnavailableError - ) - expect(recovered).toContain('pane-a') - await waitFor(() => respawn.mock.calls.length === 2) - } finally { - healingAdapter.dispose() - } - }) - }) - - describe('respawn on daemon death', () => { - it('preserves an unhealthy macOS resolver daemon when it owns live sessions', async () => { - const respawnFn = vi.fn() - const exits: { id: string; code: number }[] = [] - const respawnAdapter = new DaemonPtyAdapter({ socketPath, tokenPath, respawn: respawnFn }) - respawnAdapter.onExit((payload) => exits.push(payload)) - const existing = await respawnAdapter.spawn({ cols: 80, rows: 24 }) - getMacDaemonSystemResolverHealthMock.mockResolvedValueOnce('unhealthy') - - const next = await respawnAdapter.spawn({ cols: 80, rows: 24, isNewSession: true }) - - expect(getMacDaemonSystemResolverHealthMock).toHaveBeenCalledWith( - socketPath, - tokenPath, - respawnAdapter.protocolVersion - ) - expect(respawnFn).not.toHaveBeenCalled() - expect(exits).toEqual([]) - expect(next.id).toBeDefined() - expect(next.id).not.toBe(existing.id) - - respawnAdapter.dispose() - }) - - it('preserves an unhealthy macOS resolver daemon when live sessions have not been reconciled locally', async () => { - const respawnFn = vi.fn() - const background = await adapter.spawn({ cols: 80, rows: 24 }) - const backgroundSubprocess = lastSubprocess - const respawnAdapter = new DaemonPtyAdapter({ socketPath, tokenPath, respawn: respawnFn }) - getMacDaemonSystemResolverHealthMock.mockResolvedValueOnce('unhealthy') - - const next = await respawnAdapter.spawn({ cols: 80, rows: 24, isNewSession: true }) - - expect(getMacDaemonSystemResolverHealthMock).toHaveBeenCalledWith( - socketPath, - tokenPath, - respawnAdapter.protocolVersion - ) - expect(respawnFn).not.toHaveBeenCalled() - expect(next.id).toBeDefined() - expect(next.id).not.toBe(background.id) - expect(backgroundSubprocess.forceKill).not.toHaveBeenCalled() - - respawnAdapter.dispose() - }) }) }) diff --git a/src/main/daemon/providers/adapter/testing/daemon-pty-adapter-sleep-cwd.test.ts b/src/main/daemon/providers/adapter/testing/daemon-pty-adapter-sleep-cwd.test.ts deleted file mode 100644 index 0ae10251055..00000000000 --- a/src/main/daemon/providers/adapter/testing/daemon-pty-adapter-sleep-cwd.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -import '../../../pty-subprocess/testing/mock-descendant-sweep' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { join } from 'node:path' -import { readFileSync, realpathSync, rmSync } from 'node:fs' -import { DaemonPtyAdapter } from '../daemon-pty-adapter' -import { DaemonClient } from '../../../transport/client/daemon-client' -import type { DaemonServer } from '../../../server/daemon-server' -import { getHistorySessionDirName } from '../../../history/storage/history-paths' -import { createMockSubprocess, startDaemonAdapterHarness } from './daemon-pty-adapter-test-harness' -import type * as DaemonHealthModule from '../../../health/daemon-health' -import type * as DaemonTccAttributionModule from '../../../permissions/daemon-tcc-attribution' -import type * as ProcessCwdModule from '../../../../providers/working-directory/process-cwd' - -const { - getMacDaemonSystemResolverHealthMock, - getMacDaemonTccAttributionHealthMock, - resolveProcessCwdSpy -} = vi.hoisted(() => ({ - resolveProcessCwdSpy: vi.fn(), - getMacDaemonSystemResolverHealthMock: vi.fn( - async (): Promise<'unknown' | 'unhealthy'> => 'unknown' - ), - getMacDaemonTccAttributionHealthMock: vi.fn( - async (): Promise<'intact' | 'severed' | 'unknown'> => 'unknown' - ) -})) - -vi.mock('../../../../providers/working-directory/process-cwd', async (importOriginal) => { - const actual = await importOriginal() - resolveProcessCwdSpy.mockImplementation(actual.resolveProcessCwd) - return { ...actual, resolveProcessCwd: resolveProcessCwdSpy } -}) - -vi.mock('../../../health/daemon-health', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - getMacDaemonSystemResolverHealth: getMacDaemonSystemResolverHealthMock - } -}) - -vi.mock('../../../permissions/daemon-tcc-attribution', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - getMacDaemonTccAttributionHealth: getMacDaemonTccAttributionHealthMock - } -}) - -// verify-15b: a WSL host's pane (orcad on Linux, bash, no OSC 7) woke in the project root after `cd deep`. -describe('a native shell slept and woken', () => { - let dir: string - let socketPath: string - let tokenPath: string - let server: DaemonServer - let adapter: DaemonPtyAdapter - let spawnedCwds: (string | undefined)[] - let lastSubprocess: ReturnType - - beforeEach(async () => { - spawnedCwds = [] - const harness = await startDaemonAdapterHarness((opts) => { - spawnedCwds.push(opts.cwd) - // A real pid, so the daemon's cwd lookup reads this process's folder, as for a live shell. - lastSubprocess = Object.assign(createMockSubprocess(), { pid: process.pid }) - return lastSubprocess - }) - dir = harness.dir - socketPath = harness.socketPath - tokenPath = harness.tokenPath - server = harness.server - adapter = harness.adapter - getMacDaemonSystemResolverHealthMock.mockResolvedValue('unknown') - getMacDaemonTccAttributionHealthMock.mockResolvedValue('unknown') - }) - - afterEach(async () => { - resolveProcessCwdSpy.mockClear() - adapter?.dispose() - await server?.shutdown() - rmSync(dir, { recursive: true, force: true }) - }) - - it('wakes in the folder the shell was in at Sleep, not where it was spawned', async () => { - const historyDir = join(dir, 'history') - const sessionId = 'wt-1::/project@@sleep1' - const shellFolder = realpathSync(process.cwd()) - const history = new DaemonPtyAdapter({ socketPath, tokenPath, historyPath: historyDir }) - - await history.spawn({ cols: 80, rows: 24, cwd: dir, sessionId }) - lastSubprocess._simulateData('user@host:~/project$ cd deep\r\nuser@host:~/project/deep$ ') - await history.shutdown(sessionId, { immediate: true, keepHistory: true }) - - const saved = JSON.parse( - readFileSync(join(historyDir, getHistorySessionDirName(sessionId), 'checkpoint.json'), 'utf8') - ) - expect(realpathSync(saved.cwd)).toBe(shellFolder) - await history.spawn({ cols: 80, rows: 24, cwd: dir, sessionId }) - expect(realpathSync(spawnedCwds.at(-1)!)).toBe(shellFolder) - history.dispose() - }) - - // The process lookup execFile()s lsof, which Windows lacks; Sleep there keeps only the OSC 7 cwd. - - async function sleepAndWake(sessionId: string, deadlineMs?: number) { - const historyDir = join(dir, 'history') - const history = new DaemonPtyAdapter({ socketPath, tokenPath, historyPath: historyDir }) - await history.spawn({ cols: 80, rows: 24, cwd: dir, sessionId }) - lastSubprocess._simulateData('user@host:~/project$ ') - await history.shutdown(sessionId, { immediate: true, keepHistory: true, deadlineMs }) - const saved = JSON.parse( - readFileSync(join(historyDir, getHistorySessionDirName(sessionId), 'checkpoint.json'), 'utf8') - ) - await history.spawn({ cols: 80, rows: 24, cwd: dir, sessionId }) - history.dispose() - return { savedCwd: saved.cwd, wokeCwd: spawnedCwds.at(-1) } - } - - it('gives a hung folder read a quarter of the Sleep deadline, then checkpoints', async () => { - // Freeze only the clock, so the read's budget is exact while socket timers stay real. - vi.useFakeTimers({ toFake: ['Date'] }) - const requestSpy = vi.spyOn(DaemonClient.prototype, 'request') - try { - const now = Date.now() - resolveProcessCwdSpy.mockImplementationOnce(() => new Promise(() => {})) - const { savedCwd } = await sleepAndWake('wt-1::/project@@sleep-hung', now + 2_000) - expect(resolveProcessCwdSpy).toHaveBeenCalled() - const cwdTimeouts = requestSpy.mock.calls - .filter(([type]) => type === 'getCwd') - .map((c) => c[2]) - expect(cwdTimeouts).toEqual([500]) - expect(savedCwd).toBe(dir) - } finally { - requestSpy.mockRestore() - vi.useRealTimers() - } - }) -}) diff --git a/src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts b/src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts deleted file mode 100644 index 284171e14c3..00000000000 --- a/src/main/daemon/providers/routing/testing/daemon-pty-router.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { createAdapter } from './daemon-pty-router-test-fixture' -import { describe, expect, it, vi } from 'vitest' -import { DaemonPtyRouter } from '../daemon-pty-router' -import { TerminalSessionOwnerUnverifiedError } from '../../../protocol/daemon-errors' -import type { PtyProcessInfo } from '../../../../providers/pty/process-info' -import { PROTOCOL_VERSION } from '../../../protocol/daemon-protocol-version' - -describe('DaemonPtyRouter', () => { - describe('idle retirement', () => { - it('retires every empty daemon generation and fences subsequent spawns', async () => { - const current = createAdapter('current', [], undefined, PROTOCOL_VERSION) - const legacy = createAdapter('legacy', [], undefined, PROTOCOL_VERSION) - const router = new DaemonPtyRouter({ current, legacy: [legacy] }) - - await expect(router.requestIdleRetirement()).resolves.toEqual({ state: 'retiring' }) - expect(current.requestIdleRetirement).toHaveBeenCalledOnce() - expect(legacy.requestIdleRetirement).toHaveBeenCalledOnce() - await expect(router.spawn({ sessionId: 'late', cols: 80, rows: 24 })).rejects.toThrow( - 'Terminal daemon is decommissioning' - ) - }) - - it('reports live inventory before retiring any generation and reopens admission', async () => { - const current = createAdapter('current', [], undefined, PROTOCOL_VERSION) - const legacy = createAdapter('legacy', ['legacy-live'], undefined, PROTOCOL_VERSION) - const router = new DaemonPtyRouter({ current, legacy: [legacy] }) - - await expect(router.requestIdleRetirement()).resolves.toEqual({ - state: 'busy', - liveSessions: 1, - admissionReopened: true - }) - expect(current.requestIdleRetirement).not.toHaveBeenCalled() - expect(legacy.requestIdleRetirement).not.toHaveBeenCalled() - await expect( - router.spawn({ sessionId: 'after-refusal', cols: 80, rows: 24 }) - ).resolves.toEqual({ - id: 'after-refusal' - }) - }) - - it('does not certify reopened admission when another generation retired beside live sessions', async () => { - const current = createAdapter('current', [], undefined, PROTOCOL_VERSION) - const legacy = createAdapter('legacy', [], undefined, PROTOCOL_VERSION) - vi.mocked(legacy.requestIdleRetirement).mockResolvedValueOnce({ - state: 'busy', - liveSessions: 1, - admissionReopened: true - }) - const router = new DaemonPtyRouter({ current, legacy: [legacy] }) - await expect(router.requestIdleRetirement()).resolves.toEqual({ - state: 'busy', - liveSessions: 1 - }) - await expect( - router.spawn({ sessionId: 'unsafe-partial', cols: 80, rows: 24 }) - ).rejects.toThrow('Terminal daemon is decommissioning') - }) - }) - - it.each([false, true])( - 'keeps newer spawn authority when an older listing completes (duplicate ids: %s)', - async (duplicateIds) => { - const current = createAdapter('current') - const legacy = createAdapter('legacy') - current.supportsFencedTermination = () => true - current.killFenced = vi.fn(async () => ({ outcome: 'ended' as const })) - legacy.supportsFencedTermination = () => false - legacy.killFenced = vi.fn(async () => ({ - outcome: 'refused' as const, - reason: 'unsupported' as const - })) - vi.mocked(current.spawn).mockResolvedValue({ id: 'raced', incarnationId: 'new' }) - vi.mocked(current.listProcesses).mockResolvedValue( - duplicateIds ? [{ id: 'raced', incarnationId: 'old-current', cwd: '', title: '' }] : [] - ) - const { promise: oldInventory, resolve: release } = Promise.withResolvers() - vi.mocked(legacy.listProcesses).mockReturnValue(oldInventory) - const router = new DaemonPtyRouter({ current, legacy: [legacy] }) - const oldListing = router.listProcesses() - expect(legacy.listProcesses).toHaveBeenCalledOnce() - await router.spawn({ sessionId: 'raced', isNewSession: true, cols: 80, rows: 24 }) - release([{ id: 'raced', incarnationId: 'old-legacy', cwd: '', title: '' }]) - await expect(oldListing).rejects.toBeInstanceOf(TerminalSessionOwnerUnverifiedError) - const request = { - id: 'raced', - expectedIncarnationId: 'new', - requireNoAgentOwner: true, - requireUnattached: true - } - - expect(router.supportsFencedTermination('raced')).toBe(true) - await expect(router.killFenced(request)).resolves.toEqual({ outcome: 'ended' }) - expect(current.killFenced).toHaveBeenCalledExactlyOnceWith(request) - expect(legacy.killFenced).not.toHaveBeenCalled() - expect(current.listProcesses).toHaveBeenCalledOnce() - expect(legacy.listProcesses).toHaveBeenCalledOnce() - } - ) - - it('does not let an older refresh overwrite a newer discovery route', async () => { - const current = createAdapter('current') - const legacy = createAdapter('legacy') - current.supportsFencedTermination = () => true - current.killFenced = vi.fn(async () => ({ outcome: 'ended' as const })) - legacy.supportsFencedTermination = () => false - const { promise: staleCurrent, resolve: releaseCurrent } = - Promise.withResolvers() - const { promise: staleLegacy, resolve: releaseLegacy } = - Promise.withResolvers() - vi.mocked(current.listProcesses) - .mockReturnValueOnce(staleCurrent) - .mockResolvedValueOnce([{ id: 'discovered', incarnationId: 'new', cwd: '', title: '' }]) - vi.mocked(legacy.listProcesses).mockReturnValueOnce(staleLegacy).mockResolvedValueOnce([]) - const router = new DaemonPtyRouter({ current, legacy: [legacy] }) - const staleRefresh = router.listProcesses() - await router.discoverLegacySessions() - releaseCurrent([]) - releaseLegacy([{ id: 'discovered', incarnationId: 'old', cwd: '', title: '' }]) - await expect(staleRefresh).rejects.toBeInstanceOf(TerminalSessionOwnerUnverifiedError) - const request = { - id: 'discovered', - expectedIncarnationId: 'new', - requireNoAgentOwner: true, - requireUnattached: true - } - - expect(router.supportsFencedTermination('discovered')).toBe(true) - await expect(router.killFenced(request)).resolves.toEqual({ outcome: 'ended' }) - expect(current.killFenced).toHaveBeenCalledExactlyOnceWith(request) - expect(current.listProcesses).toHaveBeenCalledTimes(2) - expect(legacy.listProcesses).toHaveBeenCalledTimes(2) - }) -}) diff --git a/src/main/daemon/providers/routing/testing/daemon-session-owner-resolution.test.ts b/src/main/daemon/providers/routing/testing/daemon-session-owner-resolution.test.ts deleted file mode 100644 index b5fe5898c84..00000000000 --- a/src/main/daemon/providers/routing/testing/daemon-session-owner-resolution.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import type { IPtyProvider, PtySpawnOptions } from '../../../../providers/pty/provider-contract' -import type { PtyProcessInfo } from '../../../../providers/pty/process-info' -import type { PtySpawnResult } from '../../../../providers/pty/spawn-result' -import { - SessionNotFoundError, - TerminalSessionOwnerUnverifiedError -} from '../../../protocol/daemon-errors' -import { DaemonSessionOwnerResolver } from '../daemon-session-owner-resolution' - -function provider( - processes: () => Promise, - spawn: (opts: PtySpawnOptions) => Promise = async (opts) => ({ - id: opts.sessionId ?? 'new' - }) -): IPtyProvider { - return { - listProcesses: vi.fn(processes), - spawn: vi.fn(spawn) - } as unknown as IPtyProvider -} - -describe('DaemonSessionOwnerResolver', () => { - it('re-resolves a routed incarnation mismatch against every possible owner', async () => { - const staleRoute = provider( - async () => [{ id: 'session', incarnationId: 'other', cwd: '', title: 'stale' }], - async () => ({ id: 'session', incarnationId: 'other', isReattach: true }) - ) - const exactOwner = provider( - async () => [{ id: 'session', incarnationId: 'expected', cwd: '', title: 'exact' }], - async () => ({ id: 'session', incarnationId: 'expected', isReattach: true }) - ) - const resolver = new DaemonSessionOwnerResolver( - [staleRoute, exactOwner], - new Map([['session', staleRoute]]) - ) - - await expect( - resolver.spawnAttachOnly({ - sessionId: 'session', - expectedIncarnationId: 'expected', - expectedIncarnationIsAuthoritative: true, - attachOnly: true, - cols: 80, - rows: 24 - }) - ).resolves.toMatchObject({ incarnationId: 'expected' }) - expect(staleRoute.spawn).not.toHaveBeenCalled() - }) - - it('refreshes incomplete inventory when a routed owner refuses a moved session', async () => { - let firstOwnsSession = true - let secondOwnsSession = false - const first = provider( - async () => - firstOwnsSession - ? [{ id: 'session', incarnationId: 'runtime', cwd: '', title: 'first' }] - : [], - async () => { - throw new SessionNotFoundError('session') - } - ) - const second = provider( - async () => - secondOwnsSession - ? [{ id: 'session', incarnationId: 'runtime', cwd: '', title: 'second' }] - : [], - async () => ({ id: 'session', incarnationId: 'runtime', isReattach: true }) - ) - const unavailable = provider(async () => { - throw new Error('offline') - }) - const resolver = new DaemonSessionOwnerResolver([first, second, unavailable], new Map()) - - await expect(resolver.resolve('session', 'runtime', true)).resolves.toMatchObject({ - kind: 'owner', - provider: first - }) - firstOwnsSession = false - secondOwnsSession = true - - await expect( - resolver.spawnAttachOnly({ - sessionId: 'session', - expectedIncarnationId: 'runtime', - expectedIncarnationIsAuthoritative: true, - attachOnly: true, - cols: 80, - rows: 24 - }) - ).resolves.toMatchObject({ id: 'session', incarnationId: 'runtime', isReattach: true }) - expect(first.spawn).toHaveBeenCalledOnce() - expect(second.spawn).toHaveBeenCalledOnce() - expect(second.listProcesses).toHaveBeenCalledTimes(2) - }) - - it('fails closed across identity replacement then confirms absence on retry', async () => { - let releaseCurrent!: (processes: PtyProcessInfo[]) => void - let releaseLegacy!: (processes: PtyProcessInfo[]) => void - const currentGate = new Promise((resolve) => { - releaseCurrent = resolve - }) - const legacyGate = new Promise((resolve) => { - releaseLegacy = resolve - }) - const currentInventory = vi - .fn<() => Promise>() - .mockReturnValueOnce(currentGate) - .mockResolvedValueOnce([]) - const legacyInventory = vi - .fn<() => Promise>() - .mockReturnValueOnce(legacyGate) - .mockResolvedValueOnce([]) - const current = provider(currentInventory) - const legacy = provider(legacyInventory) - const resolver = new DaemonSessionOwnerResolver([current, legacy], new Map()) - const attach = { - sessionId: 'pty-persisted-after-identity-change', - expectedIncarnationId: 'incarnation-before-identity-change', - attachOnly: true, - cols: 80, - rows: 24 - } as const - - const staleAttempt = resolver.spawnAttachOnly(attach) - expect(currentInventory).toHaveBeenCalledOnce() - expect(legacyInventory).toHaveBeenCalledOnce() - resolver.invalidateProvider(current) - releaseCurrent([]) - releaseLegacy([]) - - await expect(staleAttempt).rejects.toBeInstanceOf(TerminalSessionOwnerUnverifiedError) - expect(current.spawn).not.toHaveBeenCalled() - expect(legacy.spawn).not.toHaveBeenCalled() - - await expect(resolver.spawnAttachOnly(attach)).rejects.toBeInstanceOf(SessionNotFoundError) - expect(currentInventory).toHaveBeenCalledTimes(2) - expect(legacyInventory).toHaveBeenCalledTimes(2) - expect(current.spawn).not.toHaveBeenCalled() - expect(legacy.spawn).not.toHaveBeenCalled() - }) -}) diff --git a/src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts b/src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts deleted file mode 100644 index fb84c023544..00000000000 --- a/src/main/daemon/providers/routing/testing/degraded-daemon-pty-provider.test.ts +++ /dev/null @@ -1,229 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { DegradedDaemonPtyProvider } from '../degraded-daemon-pty-provider' - -import type { DaemonPtyAdapter } from '../../adapter/daemon-pty-adapter' -import { settledWriteStub } from '../../../../providers/testing/settled-pty-write-stub' -import type { IPtyProvider, PtySpawnOptions } from '../../../../providers/pty/provider-contract' -import type { PtySpawnResult } from '../../../../providers/pty/spawn-result' -import type { PtyProcessInspectionOptions } from '../../../../providers/pty/process-inspection' -import type { TerminalProcessInspection as PtyProcessInspection } from '../../../../../shared/terminal/pty/lifecycle/terminal-process-inspection' - -import { SessionNotFoundError } from '../../../protocol/daemon-errors' - -type ProviderMock = IPtyProvider & { - probePtyLiveness: (id: string) => Promise - inspectProcess: ( - id: string, - options?: PtyProcessInspectionOptions - ) => Promise - emitData: (id: string, data: string, sequenceChars?: number) => void - emitReplay: (id: string, data: string) => void - emitExit: (id: string, code: number) => void - onWriteUnavailable: (callback: (payload: { id: string }) => void) => () => void -} - -function createProvider( - label: string, - sessions: string[] = [], - authoritativeOwnerListings = false -): ProviderMock { - const dataListeners: ((payload: { id: string; data: string; sequenceChars?: number }) => void)[] = - [] - const replayListeners: ((payload: { id: string; data: string }) => void)[] = [] - const exitListeners: ((payload: { id: string; code: number }) => void)[] = [] - const writeUnavailableListeners: ((payload: { id: string }) => void)[] = [] - return { - spawn: vi.fn(async (opts: PtySpawnOptions): Promise => { - const id = opts.sessionId ?? `${label}-new` - sessions.push(id) - return { id } - }), - attach: vi.fn(async () => {}), - hasPty: vi.fn((id: string) => sessions.includes(id)), - probePtyLiveness: vi.fn(async (id: string) => sessions.includes(id)), - providesAgentSessionOwnerListings: vi.fn(() => authoritativeOwnerListings), - write: vi.fn(), - writeWithSettlement: vi.fn(settledWriteStub()), - resize: vi.fn(), - shutdown: vi.fn(async (id: string) => { - const idx = sessions.indexOf(id) - if (idx !== -1) { - sessions.splice(idx, 1) - } - }), - sendSignal: vi.fn(async () => {}), - getCwd: vi.fn(async () => ''), - getInitialCwd: vi.fn(async () => ''), - clearBuffer: vi.fn(async () => {}), - resetInputModes: vi.fn(async () => {}), - acknowledgeDataEvent: vi.fn(), - hasChildProcesses: vi.fn(async () => false), - getForegroundProcess: vi.fn(async () => null), - inspectProcess: vi.fn(async () => ({ foregroundProcess: null, hasChildProcesses: false })), - confirmForegroundProcess: vi.fn(async () => `${label}-confirmed`), - serialize: vi.fn(async () => '{}'), - revive: vi.fn(async () => {}), - listProcesses: vi.fn(async () => sessions.map((id) => ({ id, cwd: '', title: label }))), - getDefaultShell: vi.fn(async () => '/bin/zsh'), - getProfiles: vi.fn(async () => []), - onData: vi.fn( - (callback: (payload: { id: string; data: string; sequenceChars?: number }) => void) => { - dataListeners.push(callback) - return () => { - const idx = dataListeners.indexOf(callback) - if (idx !== -1) { - dataListeners.splice(idx, 1) - } - } - } - ), - onReplay: vi.fn((callback: (payload: { id: string; data: string }) => void) => { - replayListeners.push(callback) - return () => { - const idx = replayListeners.indexOf(callback) - if (idx !== -1) { - replayListeners.splice(idx, 1) - } - } - }), - onExit: vi.fn((callback: (payload: { id: string; code: number }) => void) => { - exitListeners.push(callback) - return () => { - const idx = exitListeners.indexOf(callback) - if (idx !== -1) { - exitListeners.splice(idx, 1) - } - } - }), - emitData: (id: string, data: string, sequenceChars?: number) => { - for (const listener of dataListeners) { - listener({ id, data, ...(sequenceChars === undefined ? {} : { sequenceChars }) }) - } - }, - emitReplay: (id: string, data: string) => { - for (const listener of replayListeners) { - listener({ id, data }) - } - }, - emitExit: (id: string, code: number) => { - for (const listener of exitListeners) { - listener({ id, code }) - } - }, - onWriteUnavailable: vi.fn((callback: (payload: { id: string }) => void) => { - writeUnavailableListeners.push(callback) - return () => { - const idx = writeUnavailableListeners.indexOf(callback) - if (idx !== -1) { - writeUnavailableListeners.splice(idx, 1) - } - } - }) - } -} - -function createDaemonAdapter( - label: string, - sessions: string[] = [] -): DaemonPtyAdapter & ProviderMock { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the degraded router reaches only these adapter members. - return { - ...createProvider(label, sessions, true), - protocolVersion: 13, - supportsGitCredentialGuardHost: vi.fn(() => true), - canProvideAuthoritativeBufferSnapshot: vi.fn(() => true), - listSessions: vi.fn(async () => []), - ackColdRestore: vi.fn(), - clearTombstone: vi.fn(), - reportFocusLost: vi.fn(async () => {}), - reconcileOnStartup: vi.fn(async () => ({ alive: sessions, killed: [] })), - dispose: vi.fn(), - disconnectOnly: vi.fn(async () => {}), - getActiveSessionIds: vi.fn(() => []), - fanoutSyntheticExits: vi.fn() - } as unknown as DaemonPtyAdapter & ProviderMock -} - -it('confirms repeated stale-binding absence without falling back or spawning', async () => { - const current = createDaemonAdapter('daemon-current') - const legacy = createDaemonAdapter('daemon-legacy') - const fallback = createProvider('local-fallback') - vi.mocked(fallback.spawn).mockImplementation(async (opts) => - opts.attachOnly - ? { - id: opts.sessionId!, - incarnationId: 'incarnation-fresh-local', - isReattach: true - } - : { id: 'pty-fresh-local', incarnationId: 'incarnation-fresh-local' } - ) - const provider = new DegradedDaemonPtyProvider({ current, legacy: [legacy], fallback }) - const attach = { - sessionId: 'pty-persisted-missing', - expectedIncarnationId: 'incarnation-persisted-missing', - attachOnly: true, - cols: 80, - rows: 24 - } as const - - await expect(provider.spawn(attach)).rejects.toBeInstanceOf(SessionNotFoundError) - await expect(provider.spawn(attach)).rejects.toBeInstanceOf(SessionNotFoundError) - - expect(fallback.listProcesses).toHaveBeenCalledTimes(2) - expect(current.listProcesses).toHaveBeenCalledTimes(2) - expect(legacy.listProcesses).toHaveBeenCalledTimes(2) - expect(fallback.spawn).not.toHaveBeenCalled() - expect(current.spawn).not.toHaveBeenCalled() - expect(legacy.spawn).not.toHaveBeenCalled() - - const fresh = await provider.spawn({ cols: 80, rows: 24 }) - await expect( - provider.spawn({ - sessionId: fresh.id, - expectedIncarnationId: fresh.incarnationId, - attachOnly: true, - cols: 80, - rows: 24 - }) - ).resolves.toMatchObject({ - id: 'pty-fresh-local', - incarnationId: 'incarnation-fresh-local', - isReattach: true - }) - - expect(fallback.spawn).toHaveBeenCalledTimes(2) - expect(vi.mocked(fallback.spawn).mock.calls[0]?.[0]).not.toHaveProperty('sessionId') - expect(vi.mocked(fallback.spawn).mock.calls[1]?.[0]).toMatchObject({ - sessionId: 'pty-fresh-local', - attachOnly: true - }) - expect(current.spawn).not.toHaveBeenCalled() - expect(legacy.spawn).not.toHaveBeenCalled() - expect(fallback.listProcesses).toHaveBeenCalledTimes(2) - expect(current.listProcesses).toHaveBeenCalledTimes(2) - expect(legacy.listProcesses).toHaveBeenCalledTimes(2) -}) - -describe('DegradedDaemonPtyProvider', () => { - it('does not retain recovered daemon ownership after exit beats the spawn reply', async () => { - const current = createDaemonAdapter('daemon') - const provider = new DegradedDaemonPtyProvider({ - current, - legacy: [], - fallback: createProvider('fallback'), - probeCurrentDaemonSpawn: vi.fn(async () => true) - }) - vi.mocked(current.spawn).mockImplementation(async () => { - current.emitExit('daemon-fast-exit', 0) - return { id: 'daemon-fast-exit', exitedBeforeSpawnReply: true } - }) - - await provider.recoverFreshSpawnRouting() - await expect(provider.spawn({ cols: 80, rows: 24 })).resolves.toMatchObject({ - id: 'daemon-fast-exit', - exitedBeforeSpawnReply: true - }) - - expect(provider.getSessionIdsRoutedTo('current-daemon')).toEqual([]) - }) -}) diff --git a/src/main/daemon/terminal-host/testing/terminal-host-cwd-readability.test.ts b/src/main/daemon/terminal-host/testing/terminal-host-cwd-readability.test.ts deleted file mode 100644 index 6d700d99642..00000000000 --- a/src/main/daemon/terminal-host/testing/terminal-host-cwd-readability.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { SubprocessHandle } from '../../sessions/session-subprocess-handle' -import { TerminalHost } from '../terminal-host' - -const { opendirMock, statMock } = vi.hoisted(() => ({ - opendirMock: vi.fn(), - statMock: vi.fn() -})) -// TCC directory enumeration can remain pending while a permission sheet is open. -vi.mock('node:fs/promises', async (importOriginal) => ({ - ...(await importOriginal>()), - opendir: opendirMock, - stat: statMock -})) - -vi.mock('../../../pty/process-retirement/pty-descendant-termination', () => ({ - killWithDescendantSweep: vi.fn() -})) - -const close = vi.fn(async () => {}) -const spawnSubprocess = vi.fn(() => createMockSubprocess()) - -function dirReading(read: () => unknown): { read: () => unknown; close: () => Promise } { - return { read, close } -} - -function stallEnumeration(stage: 'open' | 'read' | 'close') { - let release!: () => void - const pending = new Promise((resolve) => { - release = resolve - }) - const directory = { - read: async () => { - if (stage === 'read') { - await pending - } - return null - }, - close: vi.fn(async () => { - if (stage === 'close') { - await pending - } - }) - } - opendirMock.mockImplementation(async () => { - if (stage === 'open') { - await pending - } - return directory - }) - return { release, directory } -} - -function createMockSubprocess(): SubprocessHandle { - let onExitCb: ((code: number) => void) | null = null - return { - pid: 99999, - getForegroundProcess: vi.fn(() => null), - write: vi.fn(), - resize: vi.fn(), - kill: vi.fn(() => { - setTimeout(() => onExitCb?.(0), 5) - }), - terminateOwnedTree: () => 'unavailable' as const, - forceKill: vi.fn(() => onExitCb?.(137)), - signal: vi.fn(), - onData() {}, - onExit(cb) { - onExitCb = cb - }, - dispose: vi.fn() - } -} - -// Only the daemon can measure its own cwd permissions; other failures must not imply denial. -describe('TerminalHost cwd readability verdict', () => { - let host: TerminalHost - let platformDescriptor: PropertyDescriptor | undefined - - beforeEach(() => { - close.mockReset() - spawnSubprocess.mockClear() - statMock.mockReset() - opendirMock.mockReset().mockReturnValue(dirReading(() => ({ name: 'entry' }))) - platformDescriptor = Object.getOwnPropertyDescriptor(process, 'platform') - Object.defineProperty(process, 'platform', { configurable: true, value: 'darwin' }) - host = new TerminalHost({ spawnSubprocess }) - }) - - afterEach(async () => { - vi.useRealTimers() - await host.dispose() - if (platformDescriptor) { - Object.defineProperty(process, 'platform', platformDescriptor) - } - }) - - const create = (sessionId: string, cwd?: string, signal?: AbortSignal) => - host.createOrAttach({ - sessionId, - cols: 80, - rows: 24, - ...(cwd ? { cwd } : {}), - ...(signal ? { cancelSignal: signal, isCanceled: () => signal.aborted } : {}), - streamClient: { onData: vi.fn(), onExit: vi.fn() } - }) - - it.each(['open', 'read', 'close'] as const)( - 'starts a shell when the diagnostic directory %s remains pending', - async (stage) => { - vi.useFakeTimers() - const { release, directory } = stallEnumeration(stage) - const creation = create(`slow-${stage}`, `/work/slow-${stage}`) - try { - await vi.advanceTimersByTimeAsync(1_000) - expect(host.listSessions()).toHaveLength(1) - await expect(creation).resolves.toMatchObject({ isNew: true }) - expect((await creation).cwdReadableByDaemon).toBeUndefined() - } finally { - release() - await vi.advanceTimersByTimeAsync(0) - } - expect(directory.close).toHaveBeenCalledOnce() - } - ) - - it('cancels a pending probe without spawning and releases the session id for a retry', async () => { - vi.useFakeTimers() - const { release, directory } = stallEnumeration('open') - const abort = new AbortController() - const creation = create('canceled-prompt', '/work/canceled-prompt', abort.signal) - let error: unknown - void creation.catch((rejection: unknown) => { - error = rejection - }) - try { - abort.abort() - await vi.advanceTimersByTimeAsync(0) - expect(spawnSubprocess).not.toHaveBeenCalled() - expect(host.listSessions()).toEqual([]) - expect(error).toMatchObject({ message: 'Attach canceled for session canceled-prompt' }) - const retry = create('canceled-prompt', '/work/canceled-prompt') - await vi.advanceTimersByTimeAsync(1_000) - expect(host.listSessions()).toHaveLength(1) - await expect(retry).resolves.toMatchObject({ isNew: true }) - expect(opendirMock).toHaveBeenCalledOnce() - } finally { - release() - await vi.advanceTimersByTimeAsync(0) - } - expect(directory.close).toHaveBeenCalledOnce() - }) - - it('does not launch a shell when shutdown starts during a pending probe', async () => { - vi.useFakeTimers() - const { release } = stallEnumeration('open') - const creation = create('shutdown-prompt', '/work/shutdown-prompt') - let error: unknown - void creation.catch((rejection: unknown) => { - error = rejection - }) - const disposal = host.dispose() - try { - await vi.advanceTimersByTimeAsync(1_000) - expect(spawnSubprocess).not.toHaveBeenCalled() - expect(error).toMatchObject({ message: 'Terminal host is shutting down' }) - await disposal - expect(host.listSessions()).toEqual([]) - } finally { - release() - await vi.advanceTimersByTimeAsync(0) - } - }) -}) diff --git a/src/main/daemon/terminal-host/testing/terminal-host-process-inspection.test.ts b/src/main/daemon/terminal-host/testing/terminal-host-process-inspection.test.ts deleted file mode 100644 index cdc040432d9..00000000000 --- a/src/main/daemon/terminal-host/testing/terminal-host-process-inspection.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { TerminalForegroundSample } from '../../../../shared/terminal/pty/lifecycle/terminal-foreground-observation' -import '../../pty-subprocess/testing/mock-descendant-sweep' -import { describe, expect, it, vi } from 'vitest' -import type { SubprocessHandle } from '../../sessions/session-subprocess-handle' -import { TerminalHost } from '../terminal-host' - -function createSubprocess(): SubprocessHandle { - let onExit: ((code: number) => void) | null = null - return { - pid: 99_999, - getForegroundProcess: vi.fn(() => null), - write: vi.fn(), - resize: vi.fn(), - kill: vi.fn(() => onExit?.(0)), - terminateOwnedTree: () => 'unavailable', - forceKill: vi.fn(() => onExit?.(137)), - signal: vi.fn(), - onData: vi.fn(), - onExit: (callback) => { - onExit = callback - }, - dispose: vi.fn() - } -} - -describe('fresh foreground observation contract', () => { - it('returns no child-census proof and fences a session replaced during its read', async () => { - let finish!: (sample: TerminalForegroundSample) => void - const subprocess = createSubprocess() - subprocess.observeForeground = vi.fn( - () => - new Promise((resolve) => { - finish = resolve - }) - ) - const host = new TerminalHost({ spawnSubprocess: () => subprocess }) - const created = await host.createOrAttach({ - sessionId: 'foreground-contract', - cols: 80, - rows: 24, - streamClient: { onData: vi.fn(), onExit: vi.fn() } - }) - try { - const pending = host.inspectProcess('foreground-contract', { - foregroundOnly: true, - expectedIncarnationId: created.incarnationId - }) - finish({ - verdict: 'observed', - source: 'pty-master', - foregroundKind: 'non-shell', - processName: 'claude' - }) - expect(await pending).toMatchObject({ - foregroundProcess: 'claude', - hasChildProcesses: true, - childProcessEvidence: 'unverifiable', - foregroundObservation: { - ptyId: 'foreground-contract', - ptyIncarnationId: created.incarnationId, - verdict: 'observed' - } - }) - const late = host.inspectProcess('foreground-contract', { foregroundOnly: true }) - await host.kill('foreground-contract') - finish({ - verdict: 'observed', - source: 'pty-master', - foregroundKind: 'non-shell', - processName: 'claude' - }) - expect(await late).toMatchObject({ - foregroundProcess: null, - hasChildProcesses: true, - foregroundObservation: { verdict: 'unverifiable', reason: 'session_changed' } - }) - } finally { - await host.dispose() - } - }) -}) diff --git a/src/main/ipc/filesystem/local-disk-write-precondition.test.ts b/src/main/ipc/filesystem/local-disk-write-precondition.test.ts index 46ac27e445d..dc83e60e184 100644 --- a/src/main/ipc/filesystem/local-disk-write-precondition.test.ts +++ b/src/main/ipc/filesystem/local-disk-write-precondition.test.ts @@ -1,86 +1,10 @@ -import { link, mkdir, mkdtemp, readFile, realpath, rm, symlink, writeFile } from 'node:fs/promises' +import { link, mkdtemp, realpath, rm, symlink, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { getDiskBaselineSignature } from '../../../shared/file-write/disk-baseline-signature' import type { RuntimeStore } from '../../runtime/construction/runtime-store-contract' import { createLocalFilesystemProvider } from '../../providers/filesystem/local-provider' -import type * as LocalRegularFileRead from './local-regular-file-read' -import type * as NodeFsPromises from 'node:fs/promises' - -const fileRead = vi.hoisted(() => { - const state: { - understateSizeBy: number - blockedRead: { path: string; entered: () => void; wait: Promise } | null - openError: Error | null - } = { - understateSizeBy: 0, - blockedRead: null, - openError: null - } - return state -}) -const realpathFailure = vi.hoisted(() => { - const state: { path: string | null } = { path: null } - return state -}) -const zeroInodes = vi.hoisted(() => { - const state: { enabled: boolean } = { enabled: false } - return state -}) -const writeGates = vi.hoisted( - () => new Map void; release: Promise }>() -) -vi.mock('node:fs/promises', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - // Some RAM/subst/network/WSL volumes cannot resolve realpath. - realpath: async (path: string) => { - if (path === realpathFailure.path) { - throw Object.assign(new Error(`EIO: i/o error, realpath '${path}'`), { code: 'EIO' }) - } - return actual.realpath(path) - }, - // 9P and some SMB mounts expose inode zero for every file. - stat: async (path: string, options?: { bigint?: boolean }) => { - if (options?.bigint !== true) { - return actual.stat(path) - } - const stats = await actual.stat(path, { bigint: true }) - return zeroInodes.enabled ? Object.assign(stats, { ino: 0n }) : stats - }, - // Hold a save after comparison so competing saves must wait before checking. - writeFile: vi.fn(async (...args: Parameters) => { - const gate = typeof args[1] === 'string' ? writeGates.get(args[1]) : undefined - if (gate) { - gate.entered() - await gate.release - } - return actual.writeFile(...args) - }) - } -}) -vi.mock('./local-regular-file-read', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - // Model stat observing size just before a concurrent append. - openLocalRegularFile: async (filePath: string) => { - if (fileRead.openError) { - throw fileRead.openError - } - const blockedRead = fileRead.blockedRead - if (blockedRead?.path === filePath) { - blockedRead.entered() - await blockedRead.wait - } - const opened = await actual.openLocalRegularFile(filePath) - opened.stats.size -= fileRead.understateSizeBy - return opened - } - } -}) let base: string @@ -89,12 +13,6 @@ beforeEach(async () => { }) afterEach(async () => { - fileRead.understateSizeBy = 0 - fileRead.blockedRead = null - fileRead.openError = null - realpathFailure.path = null - writeGates.clear() - zeroInodes.enabled = false await rm(base, { recursive: true, force: true }) }) @@ -107,31 +25,6 @@ describe('local provider guarded write', () => { resolveAuthorizedPath: vi.fn(async (path: string) => path) }) - it('writes when disk matches and refuses without writing when it does not', async () => { - const filePath = join(base, 'a.ts') - await writeFile(filePath, 'original') - - await expect( - provider().writeFileWithDiskPrecondition(filePath, 'edited', { - expectedSignature: getDiskBaselineSignature('original') - }) - ).resolves.toEqual({ ok: true }) - expect(await readFile(filePath, 'utf8')).toBe('edited') - - await expect( - provider().writeFileWithDiskPrecondition(filePath, 'stale', { - expectedSignature: getDiskBaselineSignature('original') - }) - ).resolves.toEqual({ ok: false, diskConflict: 'changed' }) - expect(await readFile(filePath, 'utf8')).toBe('edited') - - await expect( - provider().writeFileWithDiskPrecondition(filePath, 'edited', { - expectedSignature: getDiskBaselineSignature('original') - }) - ).resolves.toEqual({ ok: true, unchanged: true }) - }) - // Windows symlink creation requires elevated rights or Developer Mode. it.skipIf(process.platform === 'win32').each([ ['a symlink', (target: string, alias: string) => symlink(target, alias)], @@ -153,81 +46,4 @@ describe('local provider guarded write', () => { expect(results.filter((result) => result.ok)).toHaveLength(1) } ) - - it.skipIf(process.platform === 'win32')( - 'lets only one save create a missing file reached through a dangling symlink', - async () => { - const target = join(base, 'target.ts') - const link = join(base, 'link.ts') - await symlink(target, link) - const createIfMissing = { - expectedSignature: getDiskBaselineSignature('original'), - allowMissing: true - } - - const results = await Promise.all([ - provider().writeFileWithDiskPrecondition(target, 'through target', createIfMissing), - provider().writeFileWithDiskPrecondition(link, 'through link', createIfMissing) - ]) - - expect(results.filter((result) => result.ok)).toHaveLength(1) - } - ) - - it.skipIf(process.platform === 'win32')( - 'refuses a guarded save through a symlink loop instead of failing', - async () => { - const a = join(base, 'a.ts') - const b = join(base, 'b.ts') - await symlink(b, a) - await symlink(a, b) - - await expect( - provider().writeFileWithDiskPrecondition(a, 'edited', { - expectedSignature: getDiskBaselineSignature('original'), - allowMissing: true - }) - ).resolves.toEqual({ ok: false, diskConflict: 'changed' }) - } - ) - - it.skipIf(process.platform === 'win32')( - 'resolves a relative dangling link from its real folder, not the symlinked path to it', - async () => { - // base/alias -> base/real/sub, and sub/link.ts -> ../target.ts, so the target is base/real/target.ts. - await mkdir(join(base, 'real', 'sub'), { recursive: true }) - await symlink(join(base, 'real', 'sub'), join(base, 'alias')) - await symlink('../target.ts', join(base, 'real', 'sub', 'link.ts')) - const createIfMissing = { - expectedSignature: getDiskBaselineSignature('original'), - allowMissing: true - } - - const results = await Promise.all([ - provider().writeFileWithDiskPrecondition( - join(base, 'alias', 'link.ts'), - 'through alias', - createIfMissing - ), - provider().writeFileWithDiskPrecondition( - join(base, 'real', 'target.ts'), - 'through target', - createIfMissing - ) - ]) - - expect(results.filter((result) => result.ok)).toHaveLength(1) - } - ) - - it('does not resurrect a deleted file without allowMissing', async () => { - const filePath = join(base, 'gone.ts') - - await expect( - provider().writeFileWithDiskPrecondition(filePath, 'edited', { - expectedSignature: getDiskBaselineSignature('original') - }) - ).resolves.toEqual({ ok: false, diskConflict: 'missing' }) - await expect(readFile(filePath, 'utf8')).rejects.toMatchObject({ code: 'ENOENT' }) - }) }) diff --git a/src/main/ipc/pty/pane/spawn-registration.test.ts b/src/main/ipc/pty/pane/spawn-registration.test.ts deleted file mode 100644 index 17e538b1f63..00000000000 --- a/src/main/ipc/pty/pane/spawn-registration.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { afterEach, describe, expect, it, vi, type Mock } from 'vitest' - -import type { IPtyProvider } from '../../../providers/pty/provider-contract' -import type { PtySpawnResult } from '../../../providers/pty/spawn-result' -import { ptyIncarnationById, ptyOwnership } from '../provider/ownership-state' -import { TerminalIntentionalStops } from '../../../runtime/terminal/stop/terminal-intentional-stops' -import { admitPtySpawnOwnership, discardUnpersistedPtySpawn } from './spawn-registration' - -type MarkUnverifiable = (ptyId: string, reason: string) => void -const PTY_ID = 'unpersisted-spawn' -const result: PtySpawnResult = { id: PTY_ID, incarnationId: 'inc-1' } - -function providerWhoseShutdown(shutdown: () => Promise): IPtyProvider { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: discard calls only shutdown. - return { shutdown } as unknown as IPtyProvider -} - -describe('discarding a spawn whose binding failed to save', () => { - afterEach(() => { - ptyOwnership.delete(PTY_ID) - ptyIncarnationById.delete(PTY_ID) - }) - - function discard( - shutdown: () => Promise, - runtime: { - markPtyLivenessUnverifiable: Mock - intentionalPtyStops?: TerminalIntentionalStops - } = { markPtyLivenessUnverifiable: vi.fn() } - ): Promise { - admitPtySpawnOwnership(undefined, result) - return discardUnpersistedPtySpawn(providerWhoseShutdown(shutdown), result, runtime).then( - () => runtime - ) - } - - // Unmarked cleanup exits once looked like agent completion and removed the user's tab (QA687). - it('retires the PTY once the host acknowledged the shutdown, as a stop that keeps the tab', async () => { - const intentionalPtyStops = new TerminalIntentionalStops() - const runtime = await discard(() => Promise.resolve(), { - markPtyLivenessUnverifiable: vi.fn(), - intentionalPtyStops - }) - - expect(ptyOwnership.has(PTY_ID)).toBe(false) - expect(runtime.markPtyLivenessUnverifiable).not.toHaveBeenCalled() - expect(intentionalPtyStops.claimExit(PTY_ID, 'inc-1')).toEqual(['replaced']) - }) -}) diff --git a/src/main/ipc/pty/runtime/kill-same-id-respawn.test.ts b/src/main/ipc/pty/runtime/kill-same-id-respawn.test.ts deleted file mode 100644 index cc8119717f5..00000000000 --- a/src/main/ipc/pty/runtime/kill-same-id-respawn.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { LOCAL_EXECUTION_HOST_ID } from '../../../../shared/execution-host' -import type { IPtyProvider } from '../../../providers/pty/provider-contract' - -import { finishPtyShutdown } from '../provider/liveness' -import { ptyIncarnationById, ptyOwnership } from '../provider/ownership-state' -import { getLocalPtyProvider, setLocalPtyProvider } from '../provider/registry' -import type { PtyRuntimeControllerDeps } from './controller-deps' -import { stopAndWaitPtyFromRuntimeController } from './kill' - -const PTY_ID = 'respawned-pty' - -function providerWhose(duringShutdown: () => void, listed: { id: string }[]): IPtyProvider { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the stops call only shutdown and listProcesses. - return { - shutdown: async () => duringShutdown(), - listProcesses: async () => listed - } as unknown as IPtyProvider -} - -function harness() { - const runtime = { - markPtyStopRequested: vi.fn(), - markPtyLivenessUnverifiable: vi.fn(), - markPtyLivenessLive: vi.fn(), - onPtyExit: vi.fn() - } - const ports = { - runtime, - getLocalPtyProviderStartupPromise: () => undefined, - shutdownProviderAndDetectExit: async (provider: IPtyProvider, id: string) => { - await provider.shutdown(id, { immediate: true }) - return false - }, - rememberSyntheticKillExit: vi.fn(), - sendPtyExitToRenderer: vi.fn(), - finishPtyShutdown, - retiredRejectedPtyIds: new Map() - } - return { runtime, ports } -} - -// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the stops read only these ports and an optional store. -const asControllerDeps = (ports: unknown) => ports as PtyRuntimeControllerDeps -// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the tab-close stop reads only these ports and an optional store. - -const original = getLocalPtyProvider() -afterEach(() => { - setLocalPtyProvider(original) - ptyOwnership.delete(PTY_ID) - ptyIncarnationById.delete(PTY_ID) -}) - -describe('a stop whose shutdown lands after a same-id respawn', () => { - function respawn(): void { - ptyOwnership.set(PTY_ID, LOCAL_EXECUTION_HOST_ID) - ptyIncarnationById.set(PTY_ID, 'inc-respawn') - } - function start(): ReturnType { - setLocalPtyProvider(providerWhose(respawn, [{ id: PTY_ID }])) - ptyOwnership.set(PTY_ID, LOCAL_EXECUTION_HOST_ID) - ptyIncarnationById.set(PTY_ID, 'inc-stopped') - return harness() - } - - function expectRespawnUntouched({ runtime, ports }: ReturnType): void { - expect(ptyOwnership.get(PTY_ID)).toBe(LOCAL_EXECUTION_HOST_ID) - expect(ptyIncarnationById.get(PTY_ID)).toBe('inc-respawn') - expect(runtime.onPtyExit).not.toHaveBeenCalled() - expect(ports.sendPtyExitToRenderer).not.toHaveBeenCalled() - } - - it('reports the listed respawn live after a stop-and-wait', async () => { - const h = start() - await expect( - stopAndWaitPtyFromRuntimeController(asControllerDeps(h.ports), PTY_ID) - ).resolves.toBe(false) - expect(h.runtime.markPtyLivenessLive).toHaveBeenCalledWith(PTY_ID) - expectRespawnUntouched(h) - }) -}) diff --git a/src/main/providers/local-pty/local-pty-child-process-verdict.test.ts b/src/main/providers/local-pty/local-pty-child-process-verdict.test.ts deleted file mode 100644 index a95afdd5806..00000000000 --- a/src/main/providers/local-pty/local-pty-child-process-verdict.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as pty from 'node-pty' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type * as PtyMasterForegroundObservation from '../../pty/process-retirement/pty-master-foreground-observation' - -const { resolveForegroundMock } = vi.hoisted(() => ({ resolveForegroundMock: vi.fn() })) -const jobMembers = vi.hoisted(() => vi.fn()) -vi.mock('../foreground/windows/pty-job-membership', () => ({ - readWindowsPtyJobProcessIds: jobMembers, - isWindowsPtyJobReadable: () => true -})) - -const { confirmShellForegroundMock } = vi.hoisted(() => ({ - confirmShellForegroundMock: vi.fn() -})) - -vi.mock('../foreground/agent-process', () => ({ - resolveAgentForegroundProcessWithAvailability: resolveForegroundMock, - confirmShellForegroundProcess: confirmShellForegroundMock -})) - -const observeForegroundMock = vi.hoisted(() => vi.fn()) -vi.mock( - '../../pty/process-retirement/pty-master-foreground-observation', - async (importOriginal) => ({ - ...(await importOriginal()), - observePtyForeground: observeForegroundMock - }) -) -import { isRetiredPtyMaster } from '../../pty/process-retirement/node-pty-master-fd-retirement' - -import { LocalPtyProvider } from './local-pty-provider' -import { ptyIncarnations, ptyProcesses, ptyShellPath } from './local-pty-provider-state' -import { inspectPtyProviderProcess } from '../pty/process-inspection' - -// Match the recorded basename so only the retired descriptor distinguishes the pane from idle. -const POSIX_SHELL = 'sh' - -function registerPane(id: string, foreground: string | (() => string), shell?: string): void { - const pane: pty.IPty = { - pid: 4242, - cols: 80, - rows: 24, - get process(): string { - return typeof foreground === 'function' ? foreground() : foreground - }, - handleFlowControl: false, - onData: () => ({ dispose() {} }), - onExit: () => ({ dispose() {} }), - resize() {}, - clear() {}, - write() {}, - kill() {}, - pause() {}, - resume() {} - } - ptyProcesses.set(id, pane) - ptyIncarnations.set(id, `incarnation:${id}`) - if (shell) { - ptyShellPath.set(id, shell) - } -} - -/** Retired node-pty getters still report the spawn name; only descriptor state distinguishes this from idle. */ -async function registerRetiredPane(id: string): Promise { - const term = pty.spawn(POSIX_SHELL, ['-c', 'exit 0'], { - name: 'xterm-256color', - cols: 80, - rows: 24, - cwd: process.cwd(), - env: { ...process.env } - }) - await new Promise((resolve) => { - term.onExit(() => resolve()) - }) - // `onExit` runs before node-pty's `_close()`, which is where the patch retires `_fd`. - await vi.waitFor(() => expect(isRetiredPtyMaster(term)).toBe(true), { - timeout: 10000, - interval: 10 - }) - ptyProcesses.set(id, term) - ptyIncarnations.set(id, `incarnation:${id}`) - ptyShellPath.set(id, POSIX_SHELL) - return term -} - -beforeEach(() => { - // These fake foreground names model POSIX; Windows uses the owned-job fixture below. - vi.spyOn(process, 'platform', 'get').mockReturnValue('darwin') - jobMembers.mockReset() - resolveForegroundMock.mockReset() - resolveForegroundMock.mockResolvedValue({ available: true, processName: '/bin/zsh' }) -}) - -afterEach(() => { - vi.restoreAllMocks() - ptyProcesses.clear() - ptyIncarnations.clear() - ptyShellPath.clear() -}) - -// Windows has no master fd to retire, and `WindowsTerminal.process` answers from the spawn name. -const describeOnPosix = process.platform === 'win32' ? describe.skip : describe - -describe('inspectPtyProviderProcess child-process evidence', () => { - const provider = new LocalPtyProvider() - - it('refuses to pair one panes foreground with its replacements children', async () => { - registerPane('pty-swapped', '/bin/zsh', '/bin/zsh') - resolveForegroundMock.mockImplementation(async () => { - // Cleanup plus reactivation lands a different IPty under the same id mid-read. - registerPane('pty-swapped', 'vim', '/bin/zsh') - return { available: true, processName: '/bin/zsh' } - }) - - await expect(inspectPtyProviderProcess(provider, 'pty-swapped')).resolves.toEqual({ - foregroundProcess: null, - hasChildProcesses: true, - childProcessEvidence: 'unverifiable' - }) - }) -}) - -describeOnPosix('inspectPtyProviderProcess on a retired master', () => { - const provider = new LocalPtyProvider() - - it('carries unverifiable child evidence beside the foreground it could still read', async () => { - await registerRetiredPane('pty-retired') - - const inspection = await inspectPtyProviderProcess(provider, 'pty-retired') - expect(inspection.hasChildProcesses).toBe(true) - expect(inspection.childProcessEvidence).toBe('unverifiable') - }, 15000) -}) diff --git a/src/main/providers/local-pty/local-pty-default-shell.test.ts b/src/main/providers/local-pty/local-pty-default-shell.test.ts deleted file mode 100644 index 1502cf278f0..00000000000 --- a/src/main/providers/local-pty/local-pty-default-shell.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { spawnSync } from 'node:child_process' -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { REMOTE_VIEWER_CLIPBOARD_ENV } from '../../../shared/remote-viewer-clipboard-env' -import { finalizeLocalPtySpawnEnvironment } from './local-pty-finalize-environment' -import { createLocalPtyLaunchPlan } from './local-pty-launch-plan' - -vi.mock('./local-pty-executable-spawn', () => ({ - ensureNodePtySpawnHelperExecutable: vi.fn() -})) - -vi.mock('../working-directory/validation', () => ({ - validateWorkingDirectory: vi.fn() -})) - -afterEach(() => vi.unstubAllEnvs()) - -describe.skipIf(process.platform === 'win32')('default terminal shell', () => { - it.for([ - { shell: '/bin/bash', shellArgs: ['--rcfile', 'custom rc'], expected: 'custom:off' }, - { shell: '/bin/zsh', shellArgs: ['-f'], expected: 'unset:off' }, - { shell: '/bin/zsh', shellArgs: [], expected: 'zshrc:off' } - ])( - 'honors remote profile startup behavior for $shell $shellArgs', - ({ shell, shellArgs, expected }, { skip }) => { - if (spawnSync(shell, ['--version']).status !== 0) { - skip('Shell is not installed') - } - const home = mkdtempSync(join(tmpdir(), 'orca-profile-argv-')) - vi.stubEnv('ORCA_USER_DATA_PATH', home) - vi.stubEnv('ZDOTDIR', home) - try { - writeFileSync(join(home, '.bash_profile'), 'export ORCA_TEST_STARTUP=profile\n') - writeFileSync(join(home, 'custom rc'), 'export ORCA_TEST_STARTUP=custom\n') - writeFileSync(join(home, '.zshrc'), 'export ORCA_TEST_STARTUP=zshrc\n') - const terminalShellArgs = shellArgs.map((arg) => - arg === 'custom rc' ? join(home, arg) : arg - ) - const spawn = { cwd: home, cols: 80, rows: 24, terminalShellArgs } - const getOptions = () => ({ getDefaultShell: () => shell }) - const plan = createLocalPtyLaunchPlan(spawn, getOptions) - if (!('shellArgs' in plan)) { - throw new Error('Expected a POSIX shell launch plan') - } - const env = { HOME: home, PATH: '/usr/bin:/bin', [REMOTE_VIEWER_CLIPBOARD_ENV]: '1' } - finalizeLocalPtySpawnEnvironment({ spawn, getOptions, plan, env }) - - const result = spawnSync( - plan.shellPath, - [ - ...plan.shellArgs, - '-i', - '-c', - 'login=off; if [ -n "${ZSH_VERSION:-}" ]; then [[ -o login ]] && login=on; else shopt -q login_shell && login=on; fi; printf "\\nORCA_STARTUP_RESULT=%s:%s\\n" "${ORCA_TEST_STARTUP:-unset}" "$login"' - ], - { env, encoding: 'utf8', timeout: 10_000 } - ) - expect(result.status, result.stderr).toBe(0) - expect(result.stdout.match(/^ORCA_STARTUP_RESULT=(.*)$/m)?.[1]).toBe(expected) - } finally { - rmSync(home, { recursive: true, force: true }) - } - } - ) -}) diff --git a/src/main/providers/local-pty/local-pty-executable-spawn.test.ts b/src/main/providers/local-pty/local-pty-executable-spawn.test.ts deleted file mode 100644 index 696b4e7aef1..00000000000 --- a/src/main/providers/local-pty/local-pty-executable-spawn.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type * as fs from 'node:fs' -import type * as FsPromises from 'node:fs/promises' -import type { Stats } from 'node:fs' - -const { - existsSyncMock, - statSyncMock, - statMock, - accessSyncMock, - wslUncDirectoryExistsAsyncMock, - wrapSpawnMock -} = vi.hoisted(() => ({ - existsSyncMock: vi.fn(), - statSyncMock: vi.fn(), - statMock: vi.fn(), - accessSyncMock: vi.fn(), - wslUncDirectoryExistsAsyncMock: vi.fn(), - wrapSpawnMock: vi.fn() -})) - -vi.mock('fs', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - existsSync: existsSyncMock, - statSync: statSyncMock, - accessSync: accessSyncMock - } -}) - -vi.mock('node:fs/promises', async (importOriginal) => ({ - ...(await importOriginal()), - stat: statMock -})) - -function dirStats(isDirectory: boolean): Stats { - return { isDirectory: () => isDirectory } as Stats -} - -vi.mock('../../wsl/filesystem/unc-path-access', () => ({ - wslUncDirectoryExistsAsync: wslUncDirectoryExistsAsyncMock -})) - -vi.mock('../shell-launch/macos/tcc-login-shell', () => ({ - wrapShellSpawnForMacosTccAttribution: wrapSpawnMock, - hostReportsChildExitStatus: (file: string) => file !== '/usr/bin/login' -})) -import { spawnShellWithFallback } from './local-pty-executable-spawn' -import { _resetWorkingDirectoryValidationStateForTest } from '../working-directory/validation' - -describe('spawnShellWithFallback macOS TCC login wrapping', () => { - let origPlatform: PropertyDescriptor | undefined - - beforeEach(() => { - origPlatform = Object.getOwnPropertyDescriptor(process, 'platform') - Object.defineProperty(process, 'platform', { configurable: true, value: 'darwin' }) - existsSyncMock.mockReturnValue(true) - statSyncMock.mockReturnValue(dirStats(true)) - accessSyncMock.mockReturnValue(undefined) - wrapSpawnMock.mockImplementation((file: string, args: string[]) => ({ - file: '/wrapped/login', - args: ['wrapped', file, ...args] - })) - }) - - afterEach(() => { - if (origPlatform) { - Object.defineProperty(process, 'platform', origPlatform) - } - vi.restoreAllMocks() - }) - - it('drops the first fallback’s launch env when a second fallback takes over', () => { - // Scrub the last attempt keys; a once-only primary scrub would leak the first fallback env. - const bashLaunchEnv = { ORCA_SHELL_FEATURES: 'markers', BASH_ENV: '/userdata/bash/rcfile' } - const env: Record = { HOME: '/home/jin', ZDOTDIR: '/userdata/shell-ready/zsh' } - const ptySpawn = vi - .fn() - .mockImplementationOnce(() => { - throw new Error('primary boom') - }) - .mockImplementationOnce(() => { - throw new Error('first fallback boom') - }) - .mockReturnValue({ pid: 4 }) - - spawnShellWithFallback({ - shellPath: '/bin/zsh', - shellArgs: ['-l'], - cols: 80, - rows: 24, - cwd: '/work', - env, - ptySpawn: ptySpawn as never, - preLaunchEnv: { ZDOTDIR: undefined }, - getShellReadyConfig: (shell) => - shell === '/bin/bash' ? { args: ['--rcfile', '/rc'], env: bashLaunchEnv } : null - }) - - expect(env.ORCA_SHELL_FEATURES).toBeUndefined() - expect(env.BASH_ENV).toBeUndefined() - expect(env.ZDOTDIR).toBeUndefined() - expect(env.HOME).toBe('/home/jin') - }) -}) diff --git a/src/main/providers/local-pty/local-pty-provider-foreground-process.test.ts b/src/main/providers/local-pty/local-pty-provider-foreground-process.test.ts deleted file mode 100644 index 88371bd8eaa..00000000000 --- a/src/main/providers/local-pty/local-pty-provider-foreground-process.test.ts +++ /dev/null @@ -1,255 +0,0 @@ -import type * as WslPathOwner from '../../../shared/wsl-paths' -import type * as WslAvailabilityTypes from '../../wsl/discovery/wsl-availability' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type * as MacosTccLoginShell from '../shell-launch/macos/tcc-login-shell' -import type * as ForegroundProcess from '../foreground/agent-process' - -const { - fullTable, - identityTable, - consoleMembers, - existsSyncMock, - statSyncMock, - accessSyncMock, - mkdirSyncMock, - writeFileSyncMock, - spawnMock, - prepareMacosTccLoginShellMock, - resolveAgentForegroundProcessMock, - readWindowsPtyJobProcessIdsMock, - killWithDescendantSweepMock, - isWslAvailableAsyncMock, - wslUncDirectoryExistsAsyncMock, - createShellPromptReadinessProbeMock -} = vi.hoisted(() => ({ - fullTable: vi.fn(), - identityTable: vi.fn(), - consoleMembers: vi.fn(), - existsSyncMock: vi.fn(), - statSyncMock: vi.fn(), - accessSyncMock: vi.fn(), - mkdirSyncMock: vi.fn(), - writeFileSyncMock: vi.fn(), - spawnMock: vi.fn(), - prepareMacosTccLoginShellMock: vi.fn(), - resolveAgentForegroundProcessMock: vi.fn(), - readWindowsPtyJobProcessIdsMock: vi.fn(), - killWithDescendantSweepMock: vi.fn(), - isWslAvailableAsyncMock: vi.fn(), - wslUncDirectoryExistsAsyncMock: vi.fn(), - createShellPromptReadinessProbeMock: vi.fn() -})) - -vi.mock('../../../shared/wsl-paths', async (importOriginal) => ({ - ...(await importOriginal()), - toLinuxPath: (path: string) => path.replace(/^C:\\/i, '/mnt/c/').replace(/\\/g, '/'), - toWindowsWslPath: (path: string, distro: string) => - `\\\\wsl.localhost\\${distro}${path.replace(/\//g, '\\')}` -})) - -vi.mock('fs', () => ({ - existsSync: existsSyncMock, - statSync: statSyncMock, - accessSync: accessSyncMock, - mkdirSync: mkdirSyncMock, - writeFileSync: writeFileSyncMock, - chmodSync: vi.fn(), - renameSync: vi.fn(), - rmSync: vi.fn(), - constants: { X_OK: 1 } -})) - -vi.mock('electron', () => ({ - app: { - getPath: vi.fn(() => '/tmp/orca-user-data') - } -})) - -vi.mock('node-pty', () => ({ - spawn: spawnMock -})) - -vi.mock('../shell-launch/macos/tcc-login-shell', async (importOriginal) => ({ - ...(await importOriginal()), - prepareMacosTccLoginShell: prepareMacosTccLoginShellMock -})) - -vi.mock('../../pty/process-retirement/pty-descendant-termination', () => ({ - killWithDescendantSweep: killWithDescendantSweepMock -})) - -// The fs mock misses executable probes; pin paths here and leave Store-alias coverage to the resolver tests. -const WINDOWS_POWERSHELL_ABS = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' -const PWSH7_ABS = 'C:\\Program Files\\PowerShell\\7\\pwsh.exe' -const CMD_ABS = 'C:\\Windows\\System32\\cmd.exe' -vi.mock('../shell-launch/windows/powershell-executable', () => ({ - resolveWindowsPowerShellExecutablePath: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' ? PWSH7_ABS : WINDOWS_POWERSHELL_ABS, - resolveWindowsPowerShellSpawnChain: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' - ? [PWSH7_ABS, WINDOWS_POWERSHELL_ABS, CMD_ABS] - : [WINDOWS_POWERSHELL_ABS, CMD_ABS], - getWindowsCmdPath: () => CMD_ABS -})) - -vi.mock('../foreground/agent-process', async (importOriginal) => ({ - ...(await importOriginal()), - resolveAgentForegroundProcessWithAvailability: (...args: unknown[]) => - resolveAgentForegroundProcessMock(...args) -})) - -vi.mock('../foreground/windows/pty-job-membership', () => ({ - readWindowsPtyJobProcessIds: (...args: unknown[]) => readWindowsPtyJobProcessIdsMock(...args), - isWindowsPtyJobReadable: () => true -})) - -vi.mock('../../windows/windows-process-table', () => ({ - readWindowsProcessTable: fullTable, - readWindowsProcessTableFresh: fullTable, - readWindowsProcessIdentityTable: identityTable, - readWindowsProcessIdentityTableFresh: identityTable -})) -vi.mock('../foreground/windows/console-attached-processes', () => ({ - readWindowsConsoleAttachedProcessIds: consoleMembers -})) - -vi.mock('../../wsl/filesystem/unc-path-access', () => ({ - parseWslPath: (path: string) => { - const match = path.match(/^\\\\wsl\.localhost\\([^\\]+)(.*)$/) - if (!match) { - return null - } - return { - distro: match[1], - linuxPath: (match[2] || '').replace(/\\/g, '/') || '/' - } - }, - wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args) -})) -vi.mock('../../wsl/discovery/distro-catalog', () => ({ - getDefaultWslDistro: () => 'Ubuntu' -})) - -vi.mock('../../wsl/discovery/wsl-availability', async (importOriginal) => ({ - ...(await importOriginal()), - isWslAvailableAsync: () => isWslAvailableAsyncMock() -})) - -vi.mock('../../pty/shell-startup/shell-prompt-readiness-probe', () => ({ - createShellPromptReadinessProbe: createShellPromptReadinessProbeMock -})) - -import { LocalPtyProvider } from './local-pty-provider' -import { - applyLocalPtyProviderMockDefaults, - createLocalPtyMockProcess, - installLocalPtyProviderEnvSandbox, - type LocalPtyMockProcess -} from './local-pty-provider-test-harness' - -describe('LocalPtyProvider', () => { - let provider: LocalPtyProvider - let mockProc: LocalPtyMockProcess - let exitCb: ((info: { exitCode: number }) => void) | undefined - - installLocalPtyProviderEnvSandbox() - - beforeEach(() => { - applyLocalPtyProviderMockDefaults({ - existsSyncMock, - statSyncMock, - accessSyncMock, - mkdirSyncMock, - writeFileSyncMock, - prepareMacosTccLoginShellMock, - resolveAgentForegroundProcessMock, - readWindowsPtyJobProcessIdsMock, - killWithDescendantSweepMock, - isWslAvailableAsyncMock, - wslUncDirectoryExistsAsyncMock, - createShellPromptReadinessProbeMock - }) - - exitCb = undefined - mockProc = createLocalPtyMockProcess({ - get: () => exitCb, - set: (cb) => { - exitCb = cb - } - }) - spawnMock.mockReturnValue(mockProc) - - provider = new LocalPtyProvider() - }) - - describe('hasChildProcesses', () => { - it('fences full inspection with the original PTY incarnation across awaits', async () => { - const { id, incarnationId } = await provider.spawn({ cols: 80, rows: 24 }) - await expect( - provider.inspectProcess(id, { expectedIncarnationId: 'replacement' }) - ).resolves.toMatchObject({ childProcessEvidence: 'unverifiable' }) - let resolveScan!: (resolution: { available: boolean; processName: string }) => void - resolveAgentForegroundProcessMock.mockReturnValue( - new Promise((resolve) => { - resolveScan = resolve - }) - ) - const inspection = provider.inspectProcess(id, { expectedIncarnationId: incarnationId }) - exitCb?.({ exitCode: 0 }) - resolveScan({ available: true, processName: 'zsh' }) - await expect(inspection).resolves.toMatchObject({ childProcessEvidence: 'unverifiable' }) - }) - }) - - describe('getForegroundProcess', () => { - it('retires an anchored agent immediately when its pid leaves the job, despite a leftover', async () => { - // A missing anchor proves exit immediately; detached leftovers cannot preserve that identity. - Object.defineProperty(process, 'platform', { - configurable: true, - value: 'win32' - }) - mockProc.process = 'powershell.exe' - resolveAgentForegroundProcessMock - .mockResolvedValueOnce({ - available: true, - processName: 'claude', - processId: 999 - }) - .mockResolvedValue({ available: true, processName: null }) - readWindowsPtyJobProcessIdsMock.mockReturnValue(new Set([12345, 999])) - const { id } = await provider.spawn({ cols: 80, rows: 24 }) - - await expect(provider.getForegroundProcess(id)).resolves.toBe('claude') - readWindowsPtyJobProcessIdsMock.mockReturnValue(new Set([12345, 777])) - await expect(provider.getForegroundProcess(id)).resolves.toBeNull() - expect(resolveAgentForegroundProcessMock).toHaveBeenCalledTimes(2) - }) - - it('drops an anchored agent when the drift recheck proves the pid was recycled', async () => { - // A recycled PID still in the job cannot preserve an identity contradicted by the scan. - Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' }) - mockProc.process = 'powershell.exe' - resolveAgentForegroundProcessMock - .mockResolvedValueOnce({ available: true, processName: 'claude', processId: 999 }) - .mockResolvedValue({ available: true, processName: null, anchorPidForeign: true }) - readWindowsPtyJobProcessIdsMock.mockReturnValue(new Set([12345, 999])) - vi.useFakeTimers({ toFake: ['Date'] }) - try { - vi.setSystemTime(1_000_000) - const { id } = await provider.spawn({ cols: 80, rows: 24 }) - - await expect(provider.getForegroundProcess(id)).resolves.toBe('claude') - - vi.setSystemTime(1_000_000 + 40_000) - await expect(provider.getForegroundProcess(id)).resolves.toBeNull() - expect(resolveAgentForegroundProcessMock).toHaveBeenLastCalledWith( - mockProc.pid, - 'powershell.exe', - expect.objectContaining({ anchorProcessId: 999 }) - ) - } finally { - vi.useRealTimers() - } - }) - }) -}) diff --git a/src/main/providers/local-pty/local-pty-provider-shutdown.test.ts b/src/main/providers/local-pty/local-pty-provider-shutdown.test.ts index 84a8bf61840..1b38279902a 100644 --- a/src/main/providers/local-pty/local-pty-provider-shutdown.test.ts +++ b/src/main/providers/local-pty/local-pty-provider-shutdown.test.ts @@ -130,7 +130,6 @@ vi.mock('../../wsl/discovery/wsl-availability', async (importOriginal) => ({ vi.mock('../../pty/shell-startup/shell-prompt-readiness-probe', () => ({ createShellPromptReadinessProbe: createShellPromptReadinessProbeMock })) -import { LOCAL_PTY_PHYSICAL_EXIT_TIMEOUT_MS } from './local-pty-termination' import { LocalPtyProvider } from './local-pty-provider' import { applyLocalPtyProviderMockDefaults, @@ -195,42 +194,5 @@ describe('LocalPtyProvider', () => { await shutdown expect(provider.hasPty(id)).toBe(false) }) - - it('rejects a physical-exit timeout but retains the owner for a successful retry', async () => { - vi.useFakeTimers() - try { - const killSpy = vi.fn() - mockProc.kill = killSpy - const { id } = await provider.spawn({ cols: 80, rows: 24 }) - - const shutdown = provider.shutdown(id, { immediate: true }) - const rejected = expect(shutdown).rejects.toThrow('Timed out waiting for PTY process exit') - await vi.advanceTimersByTimeAsync(LOCAL_PTY_PHYSICAL_EXIT_TIMEOUT_MS) - await rejected - expect(provider.hasPty(id)).toBe(true) - - const retry = provider.shutdown(id, { immediate: true }) - expect(killSpy).toHaveBeenCalledTimes(1) - exitCb?.({ exitCode: 137 }) - await expect(retry).resolves.toBeUndefined() - expect(provider.hasPty(id)).toBe(false) - } finally { - vi.useRealTimers() - } - }) - - it('propagates kill failure without dropping the physical owner', async () => { - mockProc.kill = vi.fn(() => { - throw new Error('kill denied') - }) - const { id } = await provider.spawn({ cols: 80, rows: 24 }) - - await expect(provider.shutdown(id, { immediate: true })).rejects.toThrow('kill denied') - expect(provider.hasPty(id)).toBe(true) - - mockProc.kill = vi.fn(() => exitCb?.({ exitCode: 137 })) - await expect(provider.shutdown(id, { immediate: true })).resolves.toBeUndefined() - expect(provider.hasPty(id)).toBe(false) - }) }) }) diff --git a/src/main/providers/local-pty/local-pty-provider-spawn-session.test.ts b/src/main/providers/local-pty/local-pty-provider-spawn-session.test.ts index 6aab23eb946..58595d543bf 100644 --- a/src/main/providers/local-pty/local-pty-provider-spawn-session.test.ts +++ b/src/main/providers/local-pty/local-pty-provider-spawn-session.test.ts @@ -132,12 +132,6 @@ vi.mock('../../pty/shell-startup/shell-prompt-readiness-probe', () => ({ })) import { LocalPtyProvider } from './local-pty-provider' -import { - ptyDisposables, - ptyExitDisposables, - ptyPhysicalExits, - startupIngressByPty -} from './local-pty-provider-state' import { applyLocalPtyProviderMockDefaults, createLocalPtyMockProcess, @@ -181,64 +175,6 @@ describe('LocalPtyProvider', () => { }) describe('spawn', () => { - it('retires buffered output and synchronous Bun exit before replying to spawn', async () => { - const disposeData = vi.fn() - const disposeExit = vi.fn() - const onExit = vi.fn() - provider.configure({ onExit }) - mockProc.onData.mockImplementation((listener: (data: string) => void) => { - listener('last output') - return { dispose: disposeData } - }) - mockProc.onExit.mockImplementation((listener: (event: { exitCode: number }) => void) => { - listener({ exitCode: 17 }) - return { dispose: disposeExit } - }) - const result = await provider.spawn({ - cols: 80, - rows: 24, - sessionId: 'already-exited-bun-shell', - command: 'must-not-run' - }) - expect(result.exitedBeforeSpawnReply).toBe(true) - expect(provider.getPtyProcess(result.id)).toBeUndefined() - for (const map of [ - ptyDisposables, - ptyExitDisposables, - ptyPhysicalExits, - startupIngressByPty - ]) { - expect(map.has(result.id)).toBe(false) - } - expect(disposeData).toHaveBeenCalledOnce() - expect(disposeExit).toHaveBeenCalledOnce() - expect(onExit).toHaveBeenCalledOnce() - await Promise.resolve() - expect(mockProc.write).not.toHaveBeenCalled() - }) - - // Hook/trust setup makes env build a long pre-spawn await; shutdown must cancel during it (#16441). - it('does not spawn after shutdown cancels a pending spawn during the env build', async () => { - spawnMock.mockClear() - let finishEnvBuild!: (env: Record) => void - const buildSpawnEnv = vi.fn( - (_id: string, baseEnv: Record) => - new Promise>((resolve) => { - finishEnvBuild = () => resolve(baseEnv) - }) - ) - const envProvider = new LocalPtyProvider({ buildSpawnEnv }) - - const spawn = envProvider.spawn({ cols: 80, rows: 24, sessionId: 'env-build-session' }) - const canceledSpawn = expect(spawn).rejects.toThrow('PTY spawn canceled: env-build-session') - await vi.waitFor(() => expect(buildSpawnEnv).toHaveBeenCalledOnce()) - - await envProvider.shutdown('env-build-session', { immediate: true }) - finishEnvBuild({}) - await canceledSpawn - expect(spawnMock).not.toHaveBeenCalled() - }) - it('coalesces a concurrent same-session-id spawn before launching a redundant shell (F3)', async () => { spawnMock.mockClear() const procA = { ...mockProc, pid: 1001 } diff --git a/src/main/providers/local-pty/local-pty-provider-windows-shell-launch.test.ts b/src/main/providers/local-pty/local-pty-provider-windows-shell-launch.test.ts deleted file mode 100644 index 603d45c9be5..00000000000 --- a/src/main/providers/local-pty/local-pty-provider-windows-shell-launch.test.ts +++ /dev/null @@ -1,194 +0,0 @@ -import type * as WslPathOwner from '../../../shared/wsl-paths' -import type * as WslAvailabilityTypes from '../../wsl/discovery/wsl-availability' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type * as MacosTccLoginShell from '../shell-launch/macos/tcc-login-shell' - -const { toLinuxPathMock } = vi.hoisted(() => ({ - toLinuxPathMock: (path: string) => path.replace(/^C:\\/i, '/mnt/c/').replace(/\\/g, '/') -})) - -const { - existsSyncMock, - statSyncMock, - accessSyncMock, - mkdirSyncMock, - writeFileSyncMock, - spawnMock, - prepareMacosTccLoginShellMock, - resolveAgentForegroundProcessMock, - readWindowsPtyJobProcessIdsMock, - killWithDescendantSweepMock, - isWslAvailableAsyncMock, - wslUncDirectoryExistsAsyncMock, - createShellPromptReadinessProbeMock, - readWslDistroRunningStateMock -} = vi.hoisted(() => ({ - existsSyncMock: vi.fn(), - statSyncMock: vi.fn(), - accessSyncMock: vi.fn(), - mkdirSyncMock: vi.fn(), - writeFileSyncMock: vi.fn(), - spawnMock: vi.fn(), - prepareMacosTccLoginShellMock: vi.fn(), - resolveAgentForegroundProcessMock: vi.fn(), - readWindowsPtyJobProcessIdsMock: vi.fn(), - killWithDescendantSweepMock: vi.fn(), - isWslAvailableAsyncMock: vi.fn(), - wslUncDirectoryExistsAsyncMock: vi.fn(), - createShellPromptReadinessProbeMock: vi.fn(), - readWslDistroRunningStateMock: vi.fn() -})) - -vi.mock('../../../shared/wsl-paths', async (importOriginal) => ({ - ...(await importOriginal()), - toLinuxPath: toLinuxPathMock, - toWindowsWslPath: (path: string, distro: string) => - `\\\\wsl.localhost\\${distro}${path.replace(/\//g, '\\')}` -})) - -vi.mock('fs', () => ({ - existsSync: existsSyncMock, - statSync: statSyncMock, - accessSync: accessSyncMock, - mkdirSync: mkdirSyncMock, - writeFileSync: writeFileSyncMock, - chmodSync: vi.fn(), - renameSync: vi.fn(), - rmSync: vi.fn(), - constants: { X_OK: 1 } -})) - -vi.mock('electron', () => ({ - app: { - getPath: vi.fn(() => '/tmp/orca-user-data') - } -})) - -vi.mock('node-pty', () => ({ - spawn: spawnMock -})) - -vi.mock('../shell-launch/macos/tcc-login-shell', async (importOriginal) => ({ - ...(await importOriginal()), - prepareMacosTccLoginShell: prepareMacosTccLoginShellMock -})) - -vi.mock('../../pty/process-retirement/pty-descendant-termination', () => ({ - killWithDescendantSweep: killWithDescendantSweepMock -})) - -// The fs mock misses executable probes; pin paths and leave Store-alias coverage to resolver tests. -const WINDOWS_POWERSHELL_ABS = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' -const PWSH7_ABS = 'C:\\Program Files\\PowerShell\\7\\pwsh.exe' -const CMD_ABS = 'C:\\Windows\\System32\\cmd.exe' -vi.mock('../shell-launch/windows/powershell-executable', () => ({ - resolveWindowsPowerShellExecutablePath: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' ? PWSH7_ABS : WINDOWS_POWERSHELL_ABS, - resolveWindowsPowerShellSpawnChain: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' - ? [PWSH7_ABS, WINDOWS_POWERSHELL_ABS, CMD_ABS] - : [WINDOWS_POWERSHELL_ABS, CMD_ABS], - getWindowsCmdPath: () => CMD_ABS -})) - -vi.mock('../foreground/agent-process', () => ({ - resolveAgentForegroundProcessWithAvailability: (...args: unknown[]) => - resolveAgentForegroundProcessMock(...args) -})) - -vi.mock('../foreground/windows/pty-job-membership', () => ({ - readWindowsPtyJobProcessIds: (...args: unknown[]) => readWindowsPtyJobProcessIdsMock(...args), - isWindowsPtyJobReadable: () => true -})) - -vi.mock('../../wsl/filesystem/unc-path-access', async () => ({ - parseWslPath: (await import('../../../shared/wsl-paths')).parseWslUncPath, - wslUncDirectoryExistsAsync: (...args: unknown[]) => wslUncDirectoryExistsAsyncMock(...args) -})) -vi.mock('../../wsl/discovery/distro-catalog', async () => ({ - getDefaultWslDistro: () => 'Ubuntu' -})) - -vi.mock('../../wsl/discovery/wsl-availability', async (importOriginal) => ({ - ...(await importOriginal()), - isWslAvailableAsync: () => isWslAvailableAsyncMock() -})) - -vi.mock('../../wsl/discovery/wsl-background-distro-gate', () => ({ - readWslDistroRunningState: (...args: unknown[]) => readWslDistroRunningStateMock(...args) -})) - -vi.mock('../../pty/shell-startup/shell-prompt-readiness-probe', () => ({ - createShellPromptReadinessProbe: createShellPromptReadinessProbeMock -})) - -import { LocalPtyProvider } from './local-pty-provider' -import { - applyLocalPtyProviderMockDefaults, - createLocalPtyMockProcess, - installLocalPtyProviderEnvSandbox, - type LocalPtyMockProcess -} from './local-pty-provider-test-harness' - -describe('LocalPtyProvider', () => { - let provider: LocalPtyProvider - let mockProc: LocalPtyMockProcess - let exitCb: ((info: { exitCode: number }) => void) | undefined - - installLocalPtyProviderEnvSandbox() - - beforeEach(() => { - applyLocalPtyProviderMockDefaults({ - existsSyncMock, - statSyncMock, - accessSyncMock, - mkdirSyncMock, - writeFileSyncMock, - prepareMacosTccLoginShellMock, - resolveAgentForegroundProcessMock, - readWindowsPtyJobProcessIdsMock, - killWithDescendantSweepMock, - isWslAvailableAsyncMock, - wslUncDirectoryExistsAsyncMock, - createShellPromptReadinessProbeMock - }) - - exitCb = undefined - mockProc = createLocalPtyMockProcess({ - get: () => exitCb, - set: (cb) => { - exitCb = cb - } - }) - spawnMock.mockReturnValue(mockProc) - - provider = new LocalPtyProvider() - }) - - describe('spawn', () => { - it('rejects a canceled spawn while its WSL cwd probe is still pending', async () => { - Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' }) - const probeStarted = Promise.withResolvers() - wslUncDirectoryExistsAsyncMock.mockImplementation(() => { - probeStarted.resolve() - return new Promise(() => {}) - }) - const spawnCallsBefore = spawnMock.mock.calls.length - - const spawn = provider.spawn({ - cols: 80, - rows: 24, - cwd: '\\\\wsl.localhost\\Stalled\\home\\jin\\repo', - sessionId: 'stalled-wsl-cwd-session' - }) - const canceledSpawn = expect(spawn).rejects.toThrow( - 'PTY spawn canceled: stalled-wsl-cwd-session' - ) - await probeStarted.promise - await provider.shutdown('stalled-wsl-cwd-session', { immediate: true }) - - await canceledSpawn - expect(spawnMock.mock.calls).toHaveLength(spawnCallsBefore) - }) - }) -}) diff --git a/src/main/ssh/remote-exec/ssh-remote-windows-command-line-limit.test.ts b/src/main/ssh/remote-exec/ssh-remote-windows-command-line-limit.test.ts deleted file mode 100644 index 5618cacf322..00000000000 --- a/src/main/ssh/remote-exec/ssh-remote-windows-command-line-limit.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { gunzipSync } from 'node:zlib' -import { describe, expect, it } from 'vitest' -import { CMD_EXE_COMMAND_LINE_MAX_CHARS } from '../../providers/shell-launch/windows/shell-args' -import { decodeRemotePowerShellScript, powerShellCommand } from './ssh-remote-powershell' - -// Stock sshd's cmd.exe DefaultShell rejects oversized exec lines with localized diagnostics. -describe('Windows remote command line limit', () => { - it('carries an oversized script through gzip without altering it', () => { - const script = Array.from( - { length: 200 }, - (_unused, index) => `Write-Output ${index}; $slot = 'C:\\Users\\orca\\stage-${index}'` - ).join('\n') - const command = powerShellCommand(script) - expect(command.length).toBeLessThanOrEqual(CMD_EXE_COMMAND_LINE_MAX_CHARS) - expect(decodeRemotePowerShellScript(command)).toBe(script) - const bootstrap = Buffer.from( - command.match(/-EncodedCommand\s+([A-Za-z0-9+/=]+)$/u)?.[1] ?? '', - 'base64' - ).toString('utf16le') - const payload = bootstrap.match(/FromBase64String\('([A-Za-z0-9+/=]+)'\)/u)?.[1] ?? '' - expect(gunzipSync(Buffer.from(payload, 'base64')).toString('utf-8')).toBe(script) - expect(bootstrap).toContain('Invoke-Expression $OrcaScriptText') - }) -}) diff --git a/src/shared/workspace-cleanup/workspace-cleanup-evaluation.test.ts b/src/shared/workspace-cleanup/workspace-cleanup-evaluation.test.ts index d0dab784ea8..c8589c259c2 100644 --- a/src/shared/workspace-cleanup/workspace-cleanup-evaluation.test.ts +++ b/src/shared/workspace-cleanup/workspace-cleanup-evaluation.test.ts @@ -42,38 +42,6 @@ function workspace( const MERGED_ON = policy({ merged: { enabled: true } }) describe('evaluateWorkspaceCleanup', () => { - it('deletes a clean merged workspace and keeps it once anything local is at risk', () => { - const merged = workspace({ reasons: ['merged'] }) - expect(evaluateWorkspaceCleanup(merged, MERGED_ON, NOW)).toEqual({ - action: 'delete', - reason: 'merged' - }) - expect( - evaluateWorkspaceCleanup({ ...merged, blockers: ['dirty-files'] }, MERGED_ON, NOW) - ).toEqual({ action: 'keep', cause: 'uncommitted-changes', reason: 'merged' }) - expect( - evaluateWorkspaceCleanup({ ...merged, blockers: ['running-terminal'] }, MERGED_ON, NOW) - ).toEqual({ action: 'keep', cause: 'terminal-running', reason: 'merged' }) - }) - - it('removes finished automation runs and released workers only when their rule is on', () => { - const run = workspace({ reasons: ['automation-run'] }) - expect(evaluateWorkspaceCleanup(run, MERGED_ON, NOW)).toEqual({ - action: 'keep', - cause: 'no-rule-matched' - }) - expect( - evaluateWorkspaceCleanup(run, policy({ automationRuns: { enabled: true } }), NOW) - ).toEqual({ action: 'delete', reason: 'automation-run' }) - expect( - evaluateWorkspaceCleanup( - workspace({ reasons: ['orchestration-worker'], blockers: ['unpushed-commits'] }), - policy({ orchestrationWorkers: { enabled: true } }), - NOW - ) - ).toEqual({ action: 'keep', cause: 'unpushed-commits', reason: 'orchestration-worker' }) - }) - it('keeps a merged workspace whose git status was never read', () => { const unread = workspace({ reasons: ['merged'], @@ -85,51 +53,4 @@ describe('evaluateWorkspaceCleanup', () => { reason: 'merged' }) }) - - it('counts ignored files as local work only when keepWhen asks for it', () => { - const withEnv = workspace({ reasons: ['merged'], blockers: ['ignored-files'] }) - expect(evaluateWorkspaceCleanup(withEnv, MERGED_ON, NOW)).toMatchObject({ - action: 'keep', - cause: 'ignored-files' - }) - expect( - evaluateWorkspaceCleanup( - withEnv, - policy({ merged: { enabled: true }, keepWhen: 'uncommitted' }), - NOW - ) - ).toEqual({ action: 'delete', reason: 'merged' }) - }) - - it('reports how far a time rule has counted before it fires', () => { - const inactive = policy({ inactive: { enabled: true, afterDays: 30, action: 'delete' } }) - expect( - evaluateWorkspaceCleanup(workspace({ lastActivityAt: NOW - 12.5 * DAY }), inactive, NOW) - ).toEqual({ action: 'keep', cause: 'waiting', waiting: { elapsedDays: 12, afterDays: 30 } }) - expect( - evaluateWorkspaceCleanup(workspace({ lastActivityAt: NOW - 31 * DAY }), inactive, NOW) - ).toEqual({ action: 'delete', reason: 'inactive' }) - }) - - it('keeps an inactive workspace set to archive while archiving is unavailable', () => { - const archive = policy({ inactive: { enabled: true, afterDays: 30, action: 'archive' } }, false) - expect( - evaluateWorkspaceCleanup(workspace({ lastActivityAt: NOW - 40 * DAY }), archive, NOW) - ).toEqual({ action: 'keep', cause: 'archive-unavailable', reason: 'inactive' }) - }) - - it('keeps pinned, just-touched and opted-out workspaces before any rule fires', () => { - const merged = workspace({ reasons: ['merged'] }) - expect(evaluateWorkspaceCleanup({ ...merged, blockers: ['pinned'] }, MERGED_ON, NOW)).toEqual({ - action: 'keep', - cause: 'pinned' - }) - expect( - evaluateWorkspaceCleanup({ ...merged, lastActivityAt: NOW - 10 * 60 * 1000 }, MERGED_ON, NOW) - ).toEqual({ action: 'keep', cause: 'recently-active', reason: 'merged' }) - expect(evaluateWorkspaceCleanup(merged, { ...MERGED_ON, source: 'project-off' }, NOW)).toEqual({ - action: 'keep', - cause: 'project-off' - }) - }) }) From e9e4de1d27b6ec73a766a840a11337292b4bc943 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 12 Nov 2026 21:36:13 -0800 Subject: [PATCH 019/178] fix(notifications): lift toasts and corner cards onto a shared notice surface (#41628) * fix(notifications): lift toasts and corner cards onto a shared notice surface * fix(notifications): keep corner-card shadows unclipped and match the StarNag toast * fix(notifications): keep the stack's scrollbar on screen and its overflow off the status bar * fix(notifications): collapse stack shadow room while scrolling and paint lower cards above upper shadows * fix(notifications): pair re-pointed surfaces with popover foregrounds and keep close-hover border * fix(notifications): keep notice text readable on plugin themes and catch card growth in a capped stack * fix(notifications): track stack cards from mutation records and tidy test types * fix(notifications): check painted notice colors and outline unfilled secondary buttons * fix(notifications): paint app-theme notice colors as the exact hex measured, with a one-step margin * fix(notifications): keep muted panels on the notice surface's polarity for app themes * fix(notifications): keep toast descriptions and outline buttons readable on notice surfaces The surface lift now also keeps the 80% toast description blend readable, so mid-gray dark popovers no longer drop descriptions under 4.5:1. The notice scope remaps --background with --foreground, so outline buttons keep a dark surface under light popover text in light-base themes with dark popovers. --- docs/STYLEGUIDE.md | 2 +- .../app-shell/appearance/document-theme.ts | 4 +- .../appearance/notice-surface-tokens.test.ts | 145 ++++++++++++++++++ .../appearance/notice-surface-tokens.ts | 124 +++++++++++++++ src/renderer/src/assets/main.css | 70 +++++++-- .../components/NotificationCardStack.test.tsx | 103 +++++++++++++ .../src/components/NotificationCardStack.tsx | 40 ++++- .../components/star-nag/StarNagToastHost.tsx | 2 +- src/renderer/src/components/ui/sonner.tsx | 18 +-- 9 files changed, 476 insertions(+), 32 deletions(-) create mode 100644 src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts create mode 100644 src/renderer/src/app-shell/appearance/notice-surface-tokens.ts create mode 100644 src/renderer/src/components/NotificationCardStack.test.tsx diff --git a/docs/STYLEGUIDE.md b/docs/STYLEGUIDE.md index 64157482f31..b6d534201cb 100644 --- a/docs/STYLEGUIDE.md +++ b/docs/STYLEGUIDE.md @@ -50,7 +50,7 @@ Tokens pair a **surface** with a **foreground** that meets contrast on it; alway ## Radius and elevation - Radii derive from `--radius` in `main.css`. Buttons and inputs use `rounded-md`, `Card` uses `rounded-xl`, badges use `rounded-full`. Match the existing primitive's radius; don't introduce a new one. -- Three elevation levels, no fourth: (1) `border` hairline, the default for almost everything; (2) `shadow-xs` + single-token border for outline buttons and embedded cards; (3) `shadow-floating`, reserved for popovers and popups that escape the editor surface. Needing more than floating usually means you want the focus `ring`. +- Three elevation levels, no fourth: (1) `border` hairline, the default for almost everything; (2) `shadow-xs` + single-token border for outline buttons and embedded cards; (3) `shadow-floating`, reserved for popovers and popups that escape the editor surface; toasts and bottom-right notification cards use its notice form (`--notice-surface`, `--notice-border`, `shadow-notice`) so they read over chat, editor and terminal. Needing more than floating usually means you want the focus `ring`. ## Components diff --git a/src/renderer/src/app-shell/appearance/document-theme.ts b/src/renderer/src/app-shell/appearance/document-theme.ts index f5d0fe509d6..4e529761e37 100644 --- a/src/renderer/src/app-shell/appearance/document-theme.ts +++ b/src/renderer/src/app-shell/appearance/document-theme.ts @@ -11,6 +11,7 @@ import { type ColorProbe } from './plugin-theme-tokens' import { CHAT_SELECTION_MIX_TOKEN, chatSelectionMix } from './chat-selection-mix' +import { noticeSurfaceTokens } from './notice-surface-tokens' export type DocumentThemePreference = GlobalSettings['theme'] @@ -185,7 +186,8 @@ function paintPluginTokens( setPluginTokens(root, { ...withFloatingSurfaces(safe), [HOST_PILL_OUTLINE_MIX_TOKEN]: `${hostPillOutlineMix(safe, base, builtIns, probe)}%`, - [CHAT_SELECTION_MIX_TOKEN]: `${chatSelectionMix(safe, base, builtIns, probe)}%` + [CHAT_SELECTION_MIX_TOKEN]: `${chatSelectionMix(safe, base, builtIns, probe)}%`, + ...noticeSurfaceTokens(safe, base, builtIns, probe) }) return safe } diff --git a/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts b/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts new file mode 100644 index 00000000000..79a1923a60c --- /dev/null +++ b/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts @@ -0,0 +1,145 @@ +import { readFileSync } from 'node:fs' +import { describe, expect, it } from 'vitest' +import { hexToRgb, rgbaToHex, type Rgba } from '../../../../shared/color/rgba-contrast' +import { noticeSurfaceTokens } from './notice-surface-tokens' +import { safePluginThemeTokens } from './plugin-theme-tokens' + +const DARK_BUILT_INS = { + '--foreground': '#f0f0f0', + '--popover': '#171717', + '--popover-foreground': '#f0f0f0', + '--muted': '#262626', + '--muted-foreground': '#acacac' +} +const probe = (value: string): Rgba | null => (value.startsWith('#') ? hexToRgb(value) : null) + +/** WCAG 2 contrast written out here, so the check shares no math with the code under test. */ +function wcagContrast(firstHex: string, secondHex: string): number { + const luminance = (hex: string): number => { + const [r, g, b] = [1, 3, 5].map((start) => { + const c = Number.parseInt(hex.slice(start, start + 2), 16) / 255 + return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4 + }) + return 0.2126 * r + 0.7152 * g + 0.0722 * b + } + const [high, low] = [luminance(firstHex), luminance(secondHex)].sort((a, b) => b - a) + return (high + 0.05) / (low + 0.05) +} + +/** `over` with `top` painted at `alpha`, as the browser blends a bg-muted/N panel. */ +function composite(topHex: string, overHex: string, alpha: number): string { + return `#${[1, 3, 5] + .map((start) => { + const top = Number.parseInt(topHex.slice(start, start + 2), 16) + const under = Number.parseInt(overHex.slice(start, start + 2), 16) + return Math.round(top * alpha + under * (1 - alpha)) + .toString(16) + .padStart(2, '0') + }) + .join('')}` +} + +/** The emitted tokens are the painted colors, so their contrast is what the user sees. */ +function expectReadable(themeTokens: Record): void { + const palette: Record = { ...DARK_BUILT_INS, ...themeTokens } + const text = palette['--popover-foreground'] + const notice: Record = noticeSurfaceTokens( + themeTokens, + 'dark', + DARK_BUILT_INS, + probe + ) + const surface = notice['--notice-surface'] + const muted = notice['--notice-muted-foreground'] + expect(wcagContrast(surface, text)).toBeGreaterThanOrEqual(4.5) + // Toast descriptions paint popover text at 80%. Lifting must not push them under 4.5:1, nor + // below the unlifted popover's own contrast when the theme already sat under it. + const description = (under: string): number => wcagContrast(composite(text, under, 0.8), under) + expect(description(surface)).toBeGreaterThanOrEqual( + Math.min(4.5, description(palette['--popover'])) + ) + expect(wcagContrast(notice['--notice-secondary'], text)).toBeGreaterThanOrEqual(4.5) + expect(wcagContrast(surface, muted)).toBeGreaterThanOrEqual(4.5) + // UpdateErrorCardContent's bg-muted/30, /40 and /50 panels hold muted and body text. Without a + // --notice-muted token, main.css paints them from the palette's --muted. + const mutedPanel = notice['--notice-muted'] ?? palette['--muted'] + for (const alpha of [0.3, 0.4, 0.5]) { + const panel = composite(mutedPanel, surface, alpha) + expect(wcagContrast(panel, muted)).toBeGreaterThanOrEqual(4.5) + expect(wcagContrast(panel, text)).toBeGreaterThanOrEqual(4.5) + } +} + +describe('noticeSurfaceTokens', () => { + it("paints Orca's dark notice colors for the built-in palette", () => { + expect(noticeSurfaceTokens({}, 'dark', DARK_BUILT_INS, probe)).toEqual({ + '--notice-surface': '#262626', + '--notice-secondary': '#3a3a3a', + '--notice-secondary-edge': 'transparent', + '--notice-muted': '#3a3a3a', + '--notice-muted-foreground': '#acacac' + }) + }) + + // Popovers the theme validator accepts that once painted notice text under 4.5:1: #5a5a5a + // through Chromium's color-mix rounding and an over-lifted 80% description, #383838 through hex + // rounding, white-on-dark-base through dark muted panels, the rest unlifted. + it.each>([ + { '--popover': '#5a5a5a' }, + { '--popover': '#383838' }, + { '--popover': '#444444' }, + { '--popover': '#303030', '--popover-foreground': '#a0a0a0' }, + { '--popover': '#ffffff', '--popover-foreground': '#111111' } + ])('keeps notice text 4.5:1 for an accepted %o', (themeTokens) => { + expectReadable(themeTokens) + }) + + it('keeps notice text 4.5:1 for every gray popover the validator accepts', () => { + const accepted = Array.from({ length: 256 }, (_, gray) => ({ + '--popover': rgbaToHex([gray, gray, gray, 1]) + })).filter( + (tokens) => safePluginThemeTokens(tokens, 'dark', DARK_BUILT_INS, probe)['--popover'] + ) + expect(accepted.length).toBeGreaterThan(50) + for (const tokens of accepted) { + expectReadable(tokens) + } + }) + + it('outlines secondary buttons only when their fill is gone', () => { + const tokensFor = (popoverForeground: string): Record => + noticeSurfaceTokens( + { '--popover': '#303030', '--popover-foreground': popoverForeground }, + 'dark', + DARK_BUILT_INS, + probe + ) + const unfilled = tokensFor('#989898') + expect(unfilled['--notice-secondary']).toBe(unfilled['--notice-surface']) + expect(unfilled['--notice-secondary-edge']).toBe('var(--notice-border)') + const filled = tokensFor('#a0a0a0') + expect(filled['--notice-secondary']).not.toBe(filled['--notice-surface']) + expect(filled['--notice-secondary-edge']).toBe('transparent') + }) + + it('remaps the surface paired with every foreground the notice scope remaps', () => { + const css = readFileSync(new URL('../../assets/main.css', import.meta.url), 'utf8') + const scope = css.match( + /\[data-sonner-toaster\],\s*\.notification-card-stack\s*\{([^}]*)\}/ + )?.[1] + expect(scope).toBeDefined() + const remapped = new Set([...(scope ?? '').matchAll(/(--[\w-]+):/g)].map(([, name]) => name)) + // Why: a foreground remapped onto the popover's polarity over an unremapped surface (outline + // buttons: text-foreground on bg-background) paints light-on-light or dark-on-dark. + const pairs = [ + ['--foreground', '--background'], + ['--card-foreground', '--card'], + ['--secondary-foreground', '--secondary'], + ['--muted-foreground', '--muted'] + ] + for (const [foreground, surface] of pairs) { + expect(remapped.has(foreground), foreground).toBe(true) + expect(remapped.has(surface), `${surface} pairs with ${foreground}`).toBe(true) + } + }) +}) diff --git a/src/renderer/src/app-shell/appearance/notice-surface-tokens.ts b/src/renderer/src/app-shell/appearance/notice-surface-tokens.ts new file mode 100644 index 00000000000..7afca6bb419 --- /dev/null +++ b/src/renderer/src/app-shell/appearance/notice-surface-tokens.ts @@ -0,0 +1,124 @@ +import { MIN_TEXT_CONTRAST } from './plugin-theme-contrast-rules' +import { paintedThemeColors, type ColorProbe } from './plugin-theme-tokens' +import { + contrastRatio, + hexToRgb, + mix, + rgbaToHex, + type Rgba +} from '../../../../shared/color/rgba-contrast' + +export const NOTICE_SURFACE_TOKEN = '--notice-surface' +export const NOTICE_SECONDARY_TOKEN = '--notice-secondary' +export const NOTICE_MUTED_FOREGROUND_TOKEN = '--notice-muted-foreground' +export const NOTICE_SECONDARY_EDGE_TOKEN = '--notice-secondary-edge' +export const NOTICE_MUTED_TOKEN = '--notice-muted' +// Keep in sync with the built-in notice mixes in main.css. +const NOTICE_LIFT = { light: 0, dark: 7 } as const +const NOTICE_SECONDARY_MIX = { light: 6, dark: 10 } as const +// Keep in sync with the [data-description] color-mix in main.css. +const NOTICE_DESCRIPTION_OPACITY = 80 + +const BLACK: Rgba = [0, 0, 0, 1] +const painted = (color: Rgba): Rgba => hexToRgb(rgbaToHex(color)) +const step = (color: Rgba, by: number): Rgba => [ + Math.min(255, Math.max(0, color[0] + by)), + Math.min(255, Math.max(0, color[1] + by)), + Math.min(255, Math.max(0, color[2] + by)), + 1 +] + +/** 4.5:1 even with each color one 8-bit step toward the other, so a theme color that paints a + * channel off (fractional CSS colors round their own way) can't sink below the floor. */ +function readable(a: Rgba, b: Rgba): boolean { + const [lighter, darker] = contrastRatio(a, BLACK) >= contrastRatio(b, BLACK) ? [a, b] : [b, a] + return contrastRatio(step(lighter, -1), step(darker, 1)) >= MIN_TEXT_CONTRAST +} + +/** `tint` mixed into `base` at the deepest percent, from `deepest` down, where `reads` holds. */ +function deepestReadableMix( + tint: Rgba, + base: Rgba, + deepest: number, + reads: (candidate: Rgba) => boolean +): Rgba { + for (let percent = deepest; percent > 0; percent--) { + const candidate = painted(mix(tint, base, percent)) + if (reads(candidate)) { + return candidate + } + } + return painted(base) +} + +/** + * Toasts and corner cards lift the popover toward the foreground and paint popover and muted + * text on it, but a theme's popover pair is only checked unlifted. So the lift and secondary + * fill shrink until popover text stays readable (on the surface, its 80% description blend + * too), and muted text moves toward popover text until it does too, on the surface and on muted + * panels. Emitted as final hex colors: CSS paints exactly the colors measured here. + */ +export function noticeSurfaceTokensFor( + colors: { foreground: Rgba; popover: Rgba; popoverForeground: Rgba; mutedForeground: Rgba }, + base: 'light' | 'dark' +): Record { + const { foreground, popover, popoverForeground, mutedForeground } = colors + if ([foreground, popover, popoverForeground, mutedForeground].some((color) => color[3] < 1)) { + return {} + } + const text = painted(popoverForeground) + // Why: toast descriptions paint popover text at 80% (main.css [data-description]), so the lift + // must keep that blend readable too, not just the opaque text. + const surface = deepestReadableMix( + foreground, + popover, + NOTICE_LIFT[base], + (candidate) => + readable(candidate, text) && + readable(candidate, painted(mix(text, candidate, NOTICE_DESCRIPTION_OPACITY))) + ) + const secondary = deepestReadableMix( + foreground, + surface, + NOTICE_SECONDARY_MIX[base], + (candidate) => readable(candidate, text) + ) + // Muted panels (bg-muted/N) paint between the surface and the secondary fill, so muted text + // must read on both ends. + const mutedReads = (color: Rgba): boolean => + readable(surface, color) && readable(secondary, color) + let muted = painted(mutedForeground) + for (let towardText = 1; towardText <= 100 && !mutedReads(muted); towardText++) { + muted = painted(mix(popoverForeground, mutedForeground, towardText)) + } + const surfaceHex = rgbaToHex(surface) + const secondaryHex = rgbaToHex(secondary) + return { + [NOTICE_SURFACE_TOKEN]: surfaceHex, + [NOTICE_SECONDARY_TOKEN]: secondaryHex, + // Why: with no fill left, secondary buttons need an edge to still read as buttons. + [NOTICE_SECONDARY_EDGE_TOKEN]: + secondaryHex === surfaceHex ? 'var(--notice-border)' : 'transparent', + // Why: built-in palettes set --muted equal to --secondary; keeping that here keeps muted panels + // on the notice surface's polarity when a theme's popover and app polarity differ. + [NOTICE_MUTED_TOKEN]: secondaryHex, + [NOTICE_MUTED_FOREGROUND_TOKEN]: rgbaToHex(muted) + } +} + +/** noticeSurfaceTokensFor a theme's tokens, which already passed safePluginThemeTokens. */ +export function noticeSurfaceTokens( + tokens: Readonly>, + base: 'light' | 'dark', + builtIns: Readonly>, + probe: ColorProbe +): Record { + const effective = paintedThemeColors(tokens, base, builtIns, probe) + const foreground = effective('--foreground') + const popover = effective('--popover') + const popoverForeground = effective('--popover-foreground') + const mutedForeground = effective('--muted-foreground') + return foreground && popover && popoverForeground && mutedForeground + ? noticeSurfaceTokensFor({ foreground, popover, popoverForeground, mutedForeground }, base) + : {} +} diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index 39c73d8af2b..4b11b0e9edb 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -179,6 +179,7 @@ --color-git-graph-lane-5: var(--git-graph-lane-5); --color-annotation-highlight: var(--annotation-highlight); --shadow-floating: 0 10px 24px rgb(0 0 0 / 0.18); + --shadow-notice: var(--notice-shadow); --radius-sm: calc(var(--radius) * 0.6); --radius-md: calc(var(--radius) * 0.8); --radius-lg: var(--radius); @@ -550,6 +551,52 @@ --linear-priority-bar-inactive-fill: color-mix(in srgb, lch(39.576 1.25 282) 62%, transparent); } +/* One lifted surface for toasts and corner notification cards; the default card/popover blends into the app. + App themes replace the surface, secondary and muted colors with readable hex (notice-surface-tokens.ts). */ +:root { + --notice-muted: var(--muted); + --notice-muted-foreground: var(--muted-foreground); + --notice-secondary-edge: transparent; + --notice-surface: var(--popover); + --notice-border: color-mix(in srgb, var(--foreground) 14%, transparent); + --notice-secondary: color-mix(in srgb, var(--foreground) 6%, var(--notice-surface)); + --notice-shadow: 0 11px 32px -6px rgb(0 0 0 / 0.2), 0 2px 6px rgb(0 0 0 / 0.08); +} + +.dark { + --notice-surface: color-mix(in srgb, var(--foreground) 7%, var(--popover)); + --notice-border: color-mix(in srgb, var(--foreground) 15%, transparent); + --notice-secondary: color-mix(in srgb, var(--foreground) 10%, var(--notice-surface)); + --notice-shadow: + 0 11px 32px -6px rgb(0 0 0 / 0.55), 0 2px 6px rgb(0 0 0 / 0.22), + inset 0 1px 0 rgb(255 255 255 / 0.06); +} + +/* Why: buttons, text and inner panels paint from these; foregrounds pair with the popover the surface lifts, + and every remapped foreground keeps its surface (outline buttons paint text-foreground on bg-background). */ +[data-sonner-toaster], +.notification-card-stack { + --popover: var(--notice-surface); + --card: var(--notice-surface); + --background: var(--notice-surface); + --foreground: var(--popover-foreground); + --card-foreground: var(--popover-foreground); + --muted: var(--notice-muted); + --muted-foreground: var(--notice-muted-foreground); + --secondary: var(--notice-secondary); + --secondary-foreground: var(--popover-foreground); +} + +/* Why inset outline: an edge for themes that leave secondary buttons unfilled, without moving layout. */ +:is([data-sonner-toaster], .notification-card-stack) + :is( + [data-slot='button'][data-variant='secondary'], + [data-sonner-toast] [data-button][data-cancel] + ) { + outline: 1px solid var(--notice-secondary-edge); + outline-offset: -1px; +} + [data-sonner-toaster] { font-family: var(--font-sans); /* Why: above the floating workspace (z-45 panel, z-46 launcher), which parks in the toasts' corner. */ @@ -572,7 +619,7 @@ body[data-modal-overlay-open] [data-toasts-raised] [data-sonner-toaster] { flex-wrap: wrap; gap: 10px; padding: 14px 16px; - box-shadow: var(--shadow-floating); + box-shadow: var(--notice-shadow); } [data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-icon] { @@ -657,28 +704,21 @@ body[data-modal-overlay-open] pointer-events: auto; } -[data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-close-button] { +[data-sonner-toaster][data-sonner-theme] + [data-sonner-toast][data-styled='true'] + [data-close-button] { /* Override Sonner’s dark defaults so the close icon contrasts in both themes. */ color: var(--popover-foreground); background: var(--popover); - border-color: rgb(0 0 0 / 0.14); + border-color: var(--notice-border); } -[data-sonner-toaster][data-sonner-theme='dark'] +[data-sonner-toaster][data-sonner-theme] [data-sonner-toast][data-styled='true'] - [data-close-button] { - color: var(--popover-foreground); - background: var(--popover); - border-color: rgb(255 255 255 / 0.14); -} - -.dark [data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-close-button] { - border-color: rgb(255 255 255 / 0.14); -} - -[data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-close-button]:hover { + [data-close-button]:hover { color: var(--accent-foreground); background: var(--accent); + border-color: var(--notice-border); } /* Re-evaluate chat colors where the appearance producer supplies their source colors. */ diff --git a/src/renderer/src/components/NotificationCardStack.test.tsx b/src/renderer/src/components/NotificationCardStack.test.tsx new file mode 100644 index 00000000000..97922b47164 --- /dev/null +++ b/src/renderer/src/components/NotificationCardStack.test.tsx @@ -0,0 +1,103 @@ +// @vitest-environment happy-dom +import { act, cleanup, render, screen } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { NotificationCardStack } from './NotificationCardStack' + +/** A ResizeObserver that reports only what it observes, when the test resizes an element. */ +class FakeResizeObserver implements ResizeObserver { + static all: FakeResizeObserver[] = [] + private readonly targets = new Set() + + constructor(private readonly callback: ResizeObserverCallback) { + FakeResizeObserver.all.push(this) + } + + observe(target: Element): void { + this.targets.add(target) + } + + unobserve(target: Element): void { + this.targets.delete(target) + } + + disconnect(): void { + this.targets.clear() + } + + static resize(target: Element): void { + for (const observer of FakeResizeObserver.all) { + if (observer.targets.has(target)) { + observer.callback([], observer) + } + } + } +} + +function stackOf(card: HTMLElement): HTMLElement { + const stack = card.parentElement + if (!stack) { + throw new Error('card is not in a stack') + } + return stack +} + +/** Pins the stack's scroll metrics; its box stays capped at 220px while content grows. */ +function capStack(stack: HTMLElement, content: { height: number }): void { + Object.defineProperty(stack, 'clientHeight', { configurable: true, get: () => 220 }) + Object.defineProperty(stack, 'scrollHeight', { configurable: true, get: () => content.height }) +} + +describe('NotificationCardStack', () => { + beforeEach(() => { + FakeResizeObserver.all = [] + vi.stubGlobal('ResizeObserver', FakeResizeObserver) + }) + + afterEach(() => { + cleanup() + vi.unstubAllGlobals() + }) + + it('starts scrolling when a card grows inside a stack already at its max height', () => { + render( + +
+ + ) + const card = screen.getByTestId('card') + const stack = stackOf(card) + const content = { height: 220 } + capStack(stack, content) + act(() => FakeResizeObserver.resize(stack)) + expect(stack.hasAttribute('data-overflowing')).toBe(false) + + content.height = 240 + act(() => FakeResizeObserver.resize(card)) + + expect(stack.hasAttribute('data-overflowing')).toBe(true) + }) + + it('watches a card that mounts after the stack', async () => { + const { rerender } = render( + +
+ + ) + const stack = stackOf(screen.getByTestId('first')) + const content = { height: 200 } + capStack(stack, content) + rerender( + +
+
+ + ) + // MutationObserver delivers on a microtask. + await act(async () => {}) + + content.height = 260 + act(() => FakeResizeObserver.resize(screen.getByTestId('second'))) + + expect(stack.hasAttribute('data-overflowing')).toBe(true) + }) +}) diff --git a/src/renderer/src/components/NotificationCardStack.tsx b/src/renderer/src/components/NotificationCardStack.tsx index c315855e263..17ae93a94a3 100644 --- a/src/renderer/src/components/NotificationCardStack.tsx +++ b/src/renderer/src/components/NotificationCardStack.tsx @@ -8,9 +8,15 @@ import { setNotificationCardLayoutVar } from '@/lib/notifications/notification-card-layout-vars' -const STACK_STYLE = { - bottom: NOTIFICATION_CARD_STACK_BOTTOM, - maxHeight: `calc(100vh - 2.5rem - ${NOTIFICATION_CARD_STACK_BOTTOM})` +// Why: the scroll box clips, so it pads out (offset back) to show card shadows; the room collapses while it scrolls. +const SHADOW_ROOM_Y_PX = 40 +const ROOM_Y = 'var(--stack-shadow-room-y)' + +const STACK_STYLE: React.CSSProperties & Record<`--${string}`, string> = { + '--stack-shadow-room-y': `${SHADOW_ROOM_Y_PX}px`, + padding: `${ROOM_Y} 1rem`, + bottom: `calc(${NOTIFICATION_CARD_STACK_BOTTOM} - ${ROOM_Y})`, + maxHeight: `calc(100vh - 2.5rem - ${NOTIFICATION_CARD_STACK_BOTTOM} + 2 * ${ROOM_Y})` } export function NotificationCardStack({ children }: { children: ReactNode }): React.JSX.Element { @@ -24,17 +30,41 @@ export function NotificationCardStack({ children }: { children: ReactNode }): Re const unregister = registerNotificationCardLayoutVarReader(stack) // Why publish: toasts share this corner and would otherwise render behind the cards (z-50). const publish = (): void => { - const height = stack.offsetHeight + const overflowing = stack.hasAttribute('data-overflowing') + const height = stack.offsetHeight - (overflowing ? 0 : 2 * SHADOW_ROOM_Y_PX) + // Toggling swaps padding for max-height, so the content height published above stays valid. + stack.toggleAttribute('data-overflowing', stack.scrollHeight - stack.clientHeight > 1) setNotificationCardLayoutVar( NOTIFICATION_CARD_STACK_HEIGHT_VAR, height > 0 ? `calc(${height}px + 0.5rem)` : null ) } publish() + // Why each card too: once the box hits max-height, a growing card no longer resizes the box. const observer = new ResizeObserver(publish) observer.observe(stack) + for (const card of stack.children) { + observer.observe(card) + } + const cardList = new MutationObserver((records) => { + for (const record of records) { + for (const node of record.addedNodes) { + if (node instanceof Element) { + observer.observe(node) + } + } + for (const node of record.removedNodes) { + if (node instanceof Element) { + observer.unobserve(node) + } + } + } + publish() + }) + cardList.observe(stack, { childList: true }) window.addEventListener('resize', publish) return () => { + cardList.disconnect() observer.disconnect() window.removeEventListener('resize', publish) setNotificationCardLayoutVar(NOTIFICATION_CARD_STACK_HEIGHT_VAR, null) @@ -46,7 +76,7 @@ export function NotificationCardStack({ children }: { children: ReactNode }): Re
{children}
diff --git a/src/renderer/src/components/star-nag/StarNagToastHost.tsx b/src/renderer/src/components/star-nag/StarNagToastHost.tsx index 14fe940ee2e..a0ce3595b73 100644 --- a/src/renderer/src/components/star-nag/StarNagToastHost.tsx +++ b/src/renderer/src/components/star-nag/StarNagToastHost.tsx @@ -95,7 +95,7 @@ function StarNagToast({ : 'min-w-0 flex-1 gap-1.5 border-amber-400/60 bg-amber-400/15 text-amber-800 hover:bg-amber-400/25 dark:text-amber-100' return ( -
+
diff --git a/src/renderer/src/components/ui/sonner.tsx b/src/renderer/src/components/ui/sonner.tsx index dd4addc3a79..9e79ab8e399 100644 --- a/src/renderer/src/components/ui/sonner.tsx +++ b/src/renderer/src/components/ui/sonner.tsx @@ -52,6 +52,14 @@ const TOASTER_OFFSET = { right: '1rem' } +const TOASTER_STYLE: React.CSSProperties & Record<`--${string}`, string> = { + '--normal-bg': 'var(--notice-surface)', + '--normal-text': 'var(--popover-foreground)', + '--normal-border': 'var(--notice-border)', + '--border-radius': 'var(--radius)', + '--width': 'min(26rem, calc(100vw - 2rem))' +} + const Toaster = ({ ...props }: ToasterProps) => { const theme = useAppStore((s) => s.settings?.theme) || 'system' const wrapperRef = useRef(null) @@ -101,15 +109,7 @@ const Toaster = ({ ...props }: ToasterProps) => { error: , loading: }} - style={ - { - '--normal-bg': 'var(--popover)', - '--normal-text': 'var(--popover-foreground)', - '--normal-border': 'var(--border)', - '--border-radius': 'var(--radius)', - '--width': 'min(26rem, calc(100vw - 2rem))' - } as React.CSSProperties - } + style={TOASTER_STYLE} {...props} />
From 466937ab540488ea41a0bb9bf8eed203d3ee299f Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 12 Nov 2026 21:37:09 -0800 Subject: [PATCH 020/178] Improve browser performance (#41830) --- .../browser-extension-discarded-tabs.test.ts | 22 ++++ .../browser-extension-discarded-tabs.ts | 115 ++++++++++++------ 2 files changed, 99 insertions(+), 38 deletions(-) diff --git a/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.test.ts b/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.test.ts index 482e93828e4..d8dde11508b 100644 --- a/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.test.ts +++ b/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.test.ts @@ -265,6 +265,10 @@ beforeEach(() => { } }) discardedTabs.forgetBrowserPageGuestsGone() + // Retire ids an earlier test handed out, so each test starts with none listed. + for (const pageId of ['live', 'cold', 'doc', 'notes', 'job', 'remote', 'streamed']) { + discardedTabs.takeDiscardedBrowserTabId(pageId) + } registerTabsNamespace() guest(11, mocks.sessions.default, 'https://live.test/') mocks.pageByGuest.set(11, 'live') @@ -347,6 +351,24 @@ describe('restored browser pages without a guest are discarded tabs', () => { expect(await urls()).toContain('https://live.test/') }) + it('showing one restored page gives no other page a tab id extensions never saw', async () => { + recordActiveBrowserPage('cold') + guest(12, mocks.sessions.default, 'https://cold.test/') + mocks.pageByGuest.set(12, 'cold') + announceRegisteredBrowserExtensionTab('cold', 12) + saved.local.browserPagesByWorkspace = Object.fromEntries( + Object.entries(saved.local.browserPagesByWorkspace ?? {}).map(([id, pages]) => [ + id, + pages.filter((entry) => entry.id !== 'notes') + ]) + ) + writeListener?.('local') + expect(events('onRemoved')).toEqual([]) + expect((await query({ discarded: true })).map((entry) => entry.url)).toEqual([ + 'https://remote.test/' + ]) + }) + it('reports a discarded tab closed once, when the session save drops its page', async () => { const [cold] = await query({ discarded: true }) saved.local.browserPagesByWorkspace = Object.fromEntries( diff --git a/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.ts b/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.ts index 7f18c6b46cb..ee019d54609 100644 --- a/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.ts +++ b/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.ts @@ -1,7 +1,6 @@ import { webContents } from 'electron' import type { Store } from '../../../persistence/loading-store/store' -import type { BrowserWorkspace } from '../../../../shared/browser-workspace-types' -import type { WorkspaceSessionState } from '../../../../shared/workspace-session/workspace-session-state-types' +import type { BrowserPage, BrowserWorkspace } from '../../../../shared/browser-workspace-types' // oxlint-disable-next-line import/no-cycle -- browser subsystem singleton hub (browserManager); breaking it needs DI and test-mock updates, left for a human import { browserManager } from '../../guest-lifecycle/manager/browser-manager' import { emitBrowserExtensionEvent } from '../api-runtime/browser-extension-api-host' @@ -54,21 +53,52 @@ function extensionSessionFor(tab: BrowserWorkspace): Electron.Session | null { return profile ? getBrowserExtensionSessionForProfile(profile.id) : null } -function savedPagesIn(state: WorkspaceSessionState): Omit[] { - return Object.values(state.browserTabsByWorktree ?? {}).flatMap((tabs) => - tabs.flatMap((tab) => { - const session = extensionSessionFor(tab) - if (!session) { - return [] +/** + * Each saved web page with the session its guest would load in, in host order (a page id saved + * under two hosts comes first from the first). Stops once `visit` returns true. + */ +function visitSavedPages( + from: SavedBrowserTabsSource, + visit: (page: BrowserPage, session: Electron.Session) => boolean | void +): void { + for (const hostId of from.getWorkspaceSessionHostIds()) { + const state = from.getWorkspaceSession(hostId) + for (const tabs of Object.values(state.browserTabsByWorktree ?? {})) { + for (const tab of tabs) { + const session = extensionSessionFor(tab) + if (!session) { + continue + } + for (const page of state.browserPagesByWorkspace?.[tab.id] ?? []) { + // A document page is no web page; a runtime-hosted page streams, never a local guest. + if ( + page.docLocation || + (page.remoteBrowserPageId && !page.remoteBrowserPageClientHosted) + ) { + continue + } + if (visit(page, session)) { + return + } + } } - return (state.browserPagesByWorkspace?.[tab.id] ?? []).flatMap((page) => - // A document page is no web page; a runtime-hosted page streams, never a local guest. - page.docLocation || (page.remoteBrowserPageId && !page.remoteBrowserPageClientHosted) - ? [] - : [{ browserPageId: page.id, url: page.url, title: page.title, session }] - ) - }) - ) + } + } +} + +function withTabId(page: BrowserPage, session: Electron.Session): DiscardedBrowserTab { + let assigned = assignedByPageId.get(page.id) + if (!assigned) { + assigned = { tabId: nextTabId++, session } + assignedByPageId.set(page.id, assigned) + } + return { + browserPageId: page.id, + url: page.url, + title: page.title, + session, + tabId: assigned.tabId + } } function savedPageIdsIn(hostIds: readonly string[]): Set { @@ -109,11 +139,17 @@ export function setSavedBrowserTabsSource(next: SavedBrowserTabsSource | null): return } const allHostIds = next.getWorkspaceSessionHostIds() - const written = savedPageIdsIn(hostId ? [hostId] : allHostIds) const anywhere = savedPageIdsIn(allHostIds) - for (const pageId of unloadedPageIds) { - if (written.has(pageId) || !anywhere.has(pageId)) { - unloadedPageIds.delete(pageId) + if (unloadedPageIds.size > 0) { + // Why reuse: a write to every host, or to the only one, saves exactly `anywhere`. + const written = + !hostId || (allHostIds.length === 1 && allHostIds[0] === hostId) + ? anywhere + : savedPageIdsIn([hostId]) + for (const pageId of unloadedPageIds) { + if (written.has(pageId) || !anywhere.has(pageId)) { + unloadedPageIds.delete(pageId) + } } } // A listed page no save keeps was closed before its guest ever loaded. @@ -133,29 +169,32 @@ export function listDiscardedBrowserTabs(): DiscardedBrowserTab[] { } const seen = new Set() const tabs: DiscardedBrowserTab[] = [] - for (const hostId of source.getWorkspaceSessionHostIds()) { - for (const page of savedPagesIn(source.getWorkspaceSession(hostId))) { - const { browserPageId } = page - if (seen.has(browserPageId) || unloadedPageIds.has(browserPageId)) { - continue - } - seen.add(browserPageId) - if (hasLiveGuest(browserPageId)) { - continue - } - let assigned = assignedByPageId.get(browserPageId) - if (!assigned) { - assigned = { tabId: nextTabId++, session: page.session } - assignedByPageId.set(browserPageId, assigned) - } - tabs.push({ ...page, tabId: assigned.tabId }) + visitSavedPages(source, (page, session) => { + if (seen.has(page.id) || unloadedPageIds.has(page.id)) { + return } - } + seen.add(page.id) + if (!hasLiveGuest(page.id)) { + tabs.push(withTabId(page, session)) + } + }) return tabs } +/** Looks up one page without listing (and numbering) every other discarded tab. */ export function findDiscardedBrowserTabForPage(browserPageId: string): DiscardedBrowserTab | null { - return listDiscardedBrowserTabs().find((tab) => tab.browserPageId === browserPageId) ?? null + if (!source || unloadedPageIds.has(browserPageId) || hasLiveGuest(browserPageId)) { + return null + } + let found: DiscardedBrowserTab | null = null + visitSavedPages(source, (page, session) => { + if (page.id !== browserPageId) { + return false + } + found = withTabId(page, session) + return true + }) + return found } /** A page's guest registered: the synthetic id it was listed under (if any) retires with it. */ From b0c0377664adbbd67e893a8534c793daf9ee7727 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 21:49:42 -0800 Subject: [PATCH 021/178] test: remove subprocess and mobile navigation projections (#41842) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/reliability-gates.jsonc | 30 +-- .../scripts/ci/sharding/ci-shard-timings.json | 6 - .../node-server-change-scope.mjs | 1 - .../qualification/node-server-test-paths.mjs | 2 - .../use-mobile-file-tap-handlers.test.tsx | 109 --------- .../launched-agent-opener.test.ts | 44 ---- ...wn-file-foreground-rejected-agents.test.ts | 127 ----------- ...ubprocess-foreground-degraded-scan.test.ts | 147 ------------- ...pty-subprocess-foreground-identity.test.ts | 206 ------------------ .../pty-subprocess-handle-lifecycle.test.ts | 30 --- ...y-subprocess-spawn-file-foreground.test.ts | 99 --------- ...ty-subprocess-windows-shell-launch.test.ts | 126 ----------- .../testing/pty-subprocess.test.ts | 136 ------------ 13 files changed, 15 insertions(+), 1048 deletions(-) delete mode 100644 mobile/src/files/session-documents/use-mobile-file-tap-handlers.test.tsx delete mode 100644 mobile/src/session/agent-launch/launched-agent-opener.test.ts delete mode 100644 src/main/daemon/pty-subprocess/spawn-file-foreground-rejected-agents.test.ts delete mode 100644 src/main/daemon/pty-subprocess/testing/pty-subprocess-foreground-degraded-scan.test.ts delete mode 100644 src/main/daemon/pty-subprocess/testing/pty-subprocess-foreground-identity.test.ts delete mode 100644 src/main/daemon/pty-subprocess/testing/pty-subprocess-spawn-file-foreground.test.ts delete mode 100644 src/main/daemon/pty-subprocess/testing/pty-subprocess-windows-shell-launch.test.ts delete mode 100644 src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 65af45df642..a6727c1d6bb 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -2182,10 +2182,10 @@ "providers": ["local-daemon", "ssh-daemon", "paired-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": ["local-daemon"], - "coverageNotes": "Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Real TerminalHost, Session, and subprocess wrapper with injected native I/O failures and mocked OS signals. Local non-daemon and SSH-relay implementations are unaffected; daemon consumers on SSH, WSL, paired runtimes, and mobile retain host-owned semantics. Live Linux/Windows/WSL and remote runs remain gaps. No git or folder-workspace assumptions. The fault-injection suite also runs with simulated darwin/linux/win32 platform branches; these do not constitute native OS coverage. Archived native macOS results previously exercised shell exit and PTY master-fd closure, input/output round trips, and teardown of a paused producer; that suite is now retired without replacement. Windows single-close/job escalation and pre-listener output/status are fault-injected contracts. Audit1302: src/main/daemon/terminal-host-teardown-recreate.test.ts: Fake subprocess handles synthesize exits via real40ms/30s timers or manual exit callback, with descendant retirement mocked away; assertions inspect isNew/refusal/spawn counts. No real subprocess/PTY teardown/recreation or owned-tree retention boundary. Lose artificial teardown/attach/shutdown/cancellation scheduling alarms; no equivalent replacement. Reduced selectors require fresh validation; prior full-suite receipts do not validate removed or changed coverage. No equivalent replacement or physical-provider claim.\nOrcad folder cleanup intentionally retires the Cartesian native failed-I/O test under repository test policy, without equivalent replacement. Remaining selectors retain their narrower scope. Dated evidenceRuns remain historical and do not validate this changed selector.", + "coverageNotes": "Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Real TerminalHost, Session, and subprocess wrapper with injected native I/O failures and mocked OS signals. Local non-daemon and SSH-relay implementations are unaffected; daemon consumers on SSH, WSL, paired runtimes, and mobile retain host-owned semantics. Live Linux/Windows/WSL and remote runs remain gaps. No git or folder-workspace assumptions. The fault-injection suite also runs with simulated darwin/linux/win32 platform branches; these do not constitute native OS coverage. Archived native macOS results previously exercised shell exit and PTY master-fd closure, input/output round trips, and teardown of a paused producer; that suite is now retired without replacement. Windows single-close/job escalation and pre-listener output/status are fault-injected contracts. Audit1302: src/main/daemon/terminal-host-teardown-recreate.test.ts: Fake subprocess handles synthesize exits via real40ms/30s timers or manual exit callback, with descendant retirement mocked away; assertions inspect isNew/refusal/spawn counts. No real subprocess/PTY teardown/recreation or owned-tree retention boundary. Lose artificial teardown/attach/shutdown/cancellation scheduling alarms; no equivalent replacement. Reduced selectors require fresh validation; prior full-suite receipts do not validate removed or changed coverage. No equivalent replacement or physical-provider claim.\nOrcad folder cleanup intentionally retires the Cartesian native failed-I/O test under repository test policy, without equivalent replacement. Remaining selectors retain their narrower scope. Dated evidenceRuns remain historical and do not validate this changed selector.", "motivatingLinks": ["docs/terminal-daemon-session-leak-investigation.md"], "invariant": "I/O errors must not establish physical exit or disable termination of an owned PTY. Session and native handle disposal require the exit event.", - "oracle": "Inject write and resize failures, require graceful and forced signals to reach the native owner, keep producer resume available, suppress repeated failed I/O, deliver output and exit, and suppress signals after exit. Across 32 create/close cycles per failure, retain each session before exit and release its native handle and emulator exactly once afterwards. Mark physical exit before notifying listeners; reentrant kill/forceKill/signal from those listeners must never signal the retired PID. The former native POSIX shell/PID/fd oracle is retired without replacement.", + "oracle": "Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Inject write and resize failures, require graceful and forced signals to reach the native owner, keep producer resume available, suppress repeated failed I/O, deliver output and exit, and suppress signals after exit. Across 32 create/close cycles per failure, retain each session before exit and release its native handle and emulator exactly once afterwards. Mark physical exit before notifying listeners; reentrant kill/forceKill/signal from those listeners must never signal the retired PID. The former native POSIX shell/PID/fd oracle is retired without replacement.", "commands": [ "pnpm test src/main/daemon/pty-subprocess/testing/pty-subprocess-io-failure-cleanup.test.ts", "pnpm test src/main/daemon/pty-subprocess/testing/pty-subprocess-io-failure-cleanup.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess-handle-lifecycle.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts src/main/daemon/terminal-host/testing/terminal-session-teardown.test.ts" @@ -8052,7 +8052,7 @@ "providers": ["local", "daemon", "wsl"], "coveredPlatforms": ["windows"], "coveredProviders": ["daemon"], - "coverageNotes": "Audit1713 removes low-value test definitions and retires affected assertion or execution-receipt claims without equivalent replacement coverage. Per-source reviews bound retained behavior and injected/native/remote limits. Unchanged assertions and receipts keep their original scope; gates lacking current active evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Issue #8048 now has deterministic wrapper and cold-restore re-anchor tests plus a Windows PR-CI harness that drives the built daemon through 25 real ConPTY workspace-close races while an unrelated witness PTY stays alive. A Windows-only patched-node-pty test injects EAGAIN on one ConPTY input pipe and requires only that PTY to close while an unrelated PTY remains writable. Keyboard reset, CJK repaint, WSL, and full visible Electron coverage remain gaps.", + "coverageNotes": "Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit1713 removes low-value test definitions and retires affected assertion or execution-receipt claims without equivalent replacement coverage. Per-source reviews bound retained behavior and injected/native/remote limits. Unchanged assertions and receipts keep their original scope; gates lacking current active evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Issue #8048 now has deterministic wrapper and cold-restore re-anchor tests plus a Windows PR-CI harness that drives the built daemon through 25 real ConPTY workspace-close races while an unrelated witness PTY stays alive. A Windows-only patched-node-pty test injects EAGAIN on one ConPTY input pipe and requires only that PTY to close while an unrelated PTY remains writable. Keyboard reset, CJK repaint, WSL, and full visible Electron coverage remain gaps.", "motivatingLinks": [ "https://github.com/stablyai/orca/pull/6541", "https://github.com/stablyai/orca/pull/6858", @@ -8064,14 +8064,13 @@ "https://github.com/stablyai/orca/issues/17027" ], "invariant": "Windows local and daemon terminals must spawn with the intended shell, survive overlapping graceful/forced workspace teardown without affecting unrelated PTYs, contain an asynchronous ConPTY input-pipe failure to the affected terminal without killing the daemon, retain recovered scrollback across the fresh daemon's first checkpoint, accept normal Enter/Backspace/Arrow input after agent or TUI exit, render cursor/CJK/wide-glyph redraws without stale cells, and converge to nonzero applied size.", - "oracle": "Audit1713 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: The issue #8048 slice asserts one node-pty ConPTY close for a graceful-then-force sequence, atomically seeds recovered history before fresh shell output and re-anchoring, preserves recovery after seed failure plus adapter restart, and runs 25 built-daemon close races while checking victim session/PID reaping, a stable daemon PID, and a live witness PTY. The EAGAIN slice emits an error from one real patched node-pty Windows input socket, requires its terminal to become unwritable and run the normal per-PTY kill path, then writes through an unrelated PTY without an uncaught exception. A broader Windows live gate still needs shell input, resize, cursor, and CJK/wide-glyph pixel evidence.", + "oracle": "Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit1713 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: The issue #8048 slice asserts one node-pty ConPTY close for a graceful-then-force sequence, atomically seeds recovered history before fresh shell output and re-anchoring, preserves recovery after seed failure plus adapter restart, and runs 25 built-daemon close races while checking victim session/PID reaping, a stable daemon PID, and a live witness PTY. The EAGAIN slice emits an error from one real patched node-pty Windows input socket, requires its terminal to become unwritable and run the normal per-PTY kill path, then writes through an unrelated PTY without an uncaught exception. A broader Windows live gate still needs shell input, resize, cursor, and CJK/wide-glyph pixel evidence.", "commands": [ - "pnpm vitest run src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/pty-subprocess/testing/node-pty-windows-input-error.win32.test.ts", + "pnpm vitest run src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/pty-subprocess/testing/node-pty-windows-input-error.win32.test.ts", "pnpm build:electron-vite && node config/scripts/diagnostics/runtime-lifecycle/windows-daemon-workspace-close-repro.mjs", "node config/scripts/diagnostics/runtime-lifecycle/windows-daemon-workspace-close-repro.mjs" ], "testFiles": [ - "src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts", "src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts", "src/main/daemon/pty-subprocess/testing/node-pty-windows-input-error.win32.test.ts", "config/scripts/diagnostics/runtime-lifecycle/windows-daemon-workspace-close-repro.mjs" @@ -8123,7 +8122,8 @@ "Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded.", "Real IME composition may require a separate lower-layer/native-text-forwarding gate.", "The EAGAIN oracle injects the real input socket event rather than inducing kernel resource exhaustion on a packaged Windows host.", - "The built-daemon harness proves process/session liveness but not renderer pixels; visible shell input, resize, cursor, and CJK repaint remain uncovered." + "The built-daemon harness proves process/session liveness but not renderer pixels; visible shell input, resize, cursor, and CJK repaint remain uncovered.", + "Audit4358 retired original selector fields (historical provenance only; no current execution claim): {\"commands\":[\"pnpm vitest run src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/pty-subprocess/testing/node-pty-windows-input-error.win32.test.ts\",\"pnpm build:electron-vite && node config/scripts/diagnostics/runtime-lifecycle/windows-daemon-workspace-close-repro.mjs\",\"node config/scripts/diagnostics/runtime-lifecycle/windows-daemon-workspace-close-repro.mjs\"],\"testFiles\":[\"src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts\",\"src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts\",\"src/main/daemon/pty-subprocess/testing/node-pty-windows-input-error.win32.test.ts\",\"config/scripts/diagnostics/runtime-lifecycle/windows-daemon-workspace-close-repro.mjs\"]}" ], "demotionRule": "Cannot promote while Windows E2E is flaky, silently skipped, or screenshot-only." }, @@ -12371,19 +12371,19 @@ "providers": ["local", "remote-runtime", "ssh"], "coveredPlatforms": ["macos", "linux"], "coveredProviders": ["local", "remote-runtime", "ssh"], - "coverageNotes": "Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous account (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous account (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous account (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous account (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous account (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous account (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous account (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous account (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous account (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous account (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous account (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous account (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous account (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous account (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous account (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous account (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Independently remove one authored/finite Playwright closed-gate app-boot inventory: accept loss of real renderer/preload/main refusal composition, saved-host row and Add/Move hiding/Remove visibility alarms. Keep the unchanged scripted-move rendered dialog lifecycle/reload case. It runs no real WSL migration, host connection, file copy or automation restoration. No selector or Playwright participation cleanup is required because the spec remains. Previous account (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1645 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1516 removes package-electron-runtime-contract.test.mjs, a copied release-workflow platform and marker matrix. Its workflow wiring alarm is intentionally retired; no actual packaged watcher delivery was exercised and no equivalent replacement is claimed. Unaffected dated execution receipts retain their original scope; changed optional selectors have no fresh full-gate execution result. This audit removes the paired-web stopped-during-outage and late-ready direct-branch examples. The retained pre-ready cancellation, failed-unwatch retry and replay-continuity cases are not equivalent replacements for those ordering alarms. Current web-runtime-client.test.ts assertions below name only its retained pre-ready callback case; replay cases remain in their separate suite. Historical commands and results remain historical. Deterministic tests cover one shared healthy child per runtime process plus at most four bounded fault-quarantine children, native desktop, paired-runtime, and WSL snapshot processes sharing the same eight-physical-child reservation with typed serialized event-driven capacity recovery, including recursive re-wait when crash recovery reclaims an announced slot, one quarantine attempt per watch lifetime, generation-scoped cancellation, bounded termination with removable deadline waiters, replacement-crawl errors that cannot be blessed by a late readiness ack, pre-ready paired-web cancellation registered before an unbounded capacity wait, already-resolved and late physical-exit retry of a rejected paired-web teardown, physical desktop-install and local/SSH PTY teardown, stale-generation rejection, per-child stat bounds, final-overflow RPC delivery, and renderer eviction before terminal callbacks can retry. Destructive local and SSH removal fences both ID-derived and resolved-cwd terminal roots, including sibling-root/cwd combinations with reverse admission rollback, closes descendant relay watches before parent deletion, enumerates authoritative provider/cwd ownership, falls back to daemon spawn cwd before OSC 7, and keeps daemon/relay immediate shutdown pending until native exit or a bounded fail-closed error; a dead relay PID discovered during attach also settles concurrent shutdown before its stale entry is reaped, while Windows runtime watcher deadlines retain one removable physical-close waiter, treat Node's error-close path as positive physical-exit proof, and clear root ownership on late close. The relay policy test maps a standard repository's base, Git common directory, and worktree roots to one healthy supervisor, then proves a shared-child failure recovers those roots in separate quarantine supervisors with overflow and resumed events. Abortable capacity, quarantine, runtime-root, relay, SSH, and relay pre-install setup waits attach one reaction to each shared promise and explicitly remove 10,000 cancelled caller closures while one anchor remains. Built-entry macOS harnesses kill the desktop/runtime and SSH relay watcher children, then require automatic resubscription and later events while the host process and relay PTY survive. SSH bundles the child boundary beside relay.js, requires both artifacts for install completeness, and preserves registration-owned same-root cancellation. The test-value audit corrects the active renderer watch command/file reference to the existing runtime-file-client-watch.test.ts, whose three cases are retained unchanged. They observe synthetic subscription retry/fanout and unsubscribe/unwatch calls, not actual preload/main bookkeeping or physical watcher-process cleanup. The separate read/preview client suite loses forwarding, supplied-error and negative-only examples, retaining binary detection and Unicode SVG decoding. Historical evidence commands/results are unchanged. Test-value audit1293 removes src/main/providers/local-pty/local-pty-provider-session-inventory.test.ts (2 authored cases). Their isolated assertions no longer run; surviving tests are not claimed as equivalent coverage. Fresh execution against the candidate SHA is required. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1412: src/main/workspaces/removal/watcher-removal-gate.test.ts reduced. Loses exact removal-error compatibility with renderer predicate under shared message constants. Kept public refusal outcomes do not establish that renderer classification. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous account (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous account (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous account (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous account (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous account (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous account (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous account (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous account (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous account (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous account (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous account (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous account (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous account (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous account (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous account (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous account (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Independently remove one authored/finite Playwright closed-gate app-boot inventory: accept loss of real renderer/preload/main refusal composition, saved-host row and Add/Move hiding/Remove visibility alarms. Keep the unchanged scripted-move rendered dialog lifecycle/reload case. It runs no real WSL migration, host connection, file copy or automation restoration. No selector or Playwright participation cleanup is required because the spec remains. Previous account (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1645 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1516 removes package-electron-runtime-contract.test.mjs, a copied release-workflow platform and marker matrix. Its workflow wiring alarm is intentionally retired; no actual packaged watcher delivery was exercised and no equivalent replacement is claimed. Unaffected dated execution receipts retain their original scope; changed optional selectors have no fresh full-gate execution result. This audit removes the paired-web stopped-during-outage and late-ready direct-branch examples. The retained pre-ready cancellation, failed-unwatch retry and replay-continuity cases are not equivalent replacements for those ordering alarms. Current web-runtime-client.test.ts assertions below name only its retained pre-ready callback case; replay cases remain in their separate suite. Historical commands and results remain historical. Deterministic tests cover one shared healthy child per runtime process plus at most four bounded fault-quarantine children, native desktop, paired-runtime, and WSL snapshot processes sharing the same eight-physical-child reservation with typed serialized event-driven capacity recovery, including recursive re-wait when crash recovery reclaims an announced slot, one quarantine attempt per watch lifetime, generation-scoped cancellation, bounded termination with removable deadline waiters, replacement-crawl errors that cannot be blessed by a late readiness ack, pre-ready paired-web cancellation registered before an unbounded capacity wait, already-resolved and late physical-exit retry of a rejected paired-web teardown, physical desktop-install and local/SSH PTY teardown, stale-generation rejection, per-child stat bounds, final-overflow RPC delivery, and renderer eviction before terminal callbacks can retry. Destructive local and SSH removal fences both ID-derived and resolved-cwd terminal roots, including sibling-root/cwd combinations with reverse admission rollback, closes descendant relay watches before parent deletion, enumerates authoritative provider/cwd ownership, falls back to daemon spawn cwd before OSC 7, and keeps daemon/relay immediate shutdown pending until native exit or a bounded fail-closed error; a dead relay PID discovered during attach also settles concurrent shutdown before its stale entry is reaped, while Windows runtime watcher deadlines retain one removable physical-close waiter, treat Node's error-close path as positive physical-exit proof, and clear root ownership on late close. The relay policy test maps a standard repository's base, Git common directory, and worktree roots to one healthy supervisor, then proves a shared-child failure recovers those roots in separate quarantine supervisors with overflow and resumed events. Abortable capacity, quarantine, runtime-root, relay, SSH, and relay pre-install setup waits attach one reaction to each shared promise and explicitly remove 10,000 cancelled caller closures while one anchor remains. Built-entry macOS harnesses kill the desktop/runtime and SSH relay watcher children, then require automatic resubscription and later events while the host process and relay PTY survive. SSH bundles the child boundary beside relay.js, requires both artifacts for install completeness, and preserves registration-owned same-root cancellation. The test-value audit corrects the active renderer watch command/file reference to the existing runtime-file-client-watch.test.ts, whose three cases are retained unchanged. They observe synthetic subscription retry/fanout and unsubscribe/unwatch calls, not actual preload/main bookkeeping or physical watcher-process cleanup. The separate read/preview client suite loses forwarding, supplied-error and negative-only examples, retaining binary detection and Unicode SVG decoding. Historical evidence commands/results are unchanged. Test-value audit1293 removes src/main/providers/local-pty/local-pty-provider-session-inventory.test.ts (2 authored cases). Their isolated assertions no longer run; surviving tests are not claimed as equivalent coverage. Fresh execution against the candidate SHA is required. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1412: src/main/workspaces/removal/watcher-removal-gate.test.ts reduced. Loses exact removal-error compatibility with renderer predicate under shared message constants. Kept public refusal outcomes do not establish that renderer classification. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/5308", "https://github.com/stablyai/orca/issues/7547", "https://github.com/stablyai/orca/issues/8212" ], "invariant": "Local runtime and SSH relay roots share one watcher child per host process while healthy and use at most four quarantine children after shard failure; native desktop, paired-runtime, and direct WSL snapshot children consume the same process-wide eight-child reservation and retry transient exhaustion without permanently disabling a root. Cancellation remains owned by its child generation and settles only after acknowledgement or bounded physical exit; paired-web retains cleanup ownership and can cancel before readiness even while capacity is exhausted, retries a failed unwatch after already-resolved or late physical exit without duplicating stream termination, replacements never overlap a still-retiring generation, and aborted or timed-out callers detach immediately even while queued behind same-root relay teardown. Destructive local and SSH cleanup holds one containment-aware watcher/terminal removal fence through descendant watcher and PTY physical exit plus parent Git/filesystem deletion; daemon and relay kill requests retain ownership until the actual subprocess exit callback or an equivalent positive dead-PID proof and propagate rejected signals or a bounded failure otherwise. A crash-fused shared shard moves affected roots into the bounded quarantine pool, a second fused quarantine ends those watch lifetimes, interrupted setup emits overflow after recovery, metadata work stays bounded, and the SSH relay, PTYs, Git, and other filesystem operations never load the watcher native addon or share its fault boundary.", - "oracle": "Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Previous oracle (preserved verbatim): Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous oracle (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous oracle (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous oracle (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous oracle (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous oracle (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous oracle (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous oracle (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous oracle (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous oracle (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous oracle (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous oracle (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous oracle (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous oracle (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous oracle (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Previous oracle (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Fill the global watcher-child reservation, require native desktop, paired-runtime, and WSL setup to retain typed transient capacity and retry after one serialized release, then cancel pending desktop and paired-client watches and require no retry. Hold one live anchor while aborting 10,000 callers at the capacity, quarantine-assignment, runtime-root, relay-setup, and SSH-registration layers; require exactly one explicit waiter and one base-promise reaction to remain, then zero after settlement. Hold an old relay unsubscribe, queue one same-root setup leader and one anchor, then abort 10,000 joiners; require every cancellation to settle before teardown releases, exactly one anchor waiter, and only one later physical install. Advance the former paired-web callback deadline while capacity remains full, require cancellation ownership to stay live, publish the server token before ready, and require files.unwatch to abort setup before a later slot release can install a native root. Reject the first ready-watch unwatch with a physical-exit promise, resolve that exit later, and require a fresh physical unwatch attempt, registry release, and exactly one stream end. Fill one healthy shared child with multiple roots, map a standard SSH repository's base, Git common directory, and worktree roots to one relay supervisor, then inject IPC disconnect-before-exit, queued and active cancellation, operations during termination, kill-without-exit, repeated unkillable generations, initial and recovery timeouts, and shard failure. During local and SSH deletion, attempt a late terminal spawn whose worktree identity and cwd name different roots, enumerate a non-prefix remote PTY by authoritative worktree ownership, withhold daemon/relay exit callbacks, make attach positively prove a concurrent-shutdown PID dead, and make the provider unavailable; require every acquired admission to roll back on rejection, dead-PID proof to settle physical exit before reaping, timeout to retain ownership, and Git/filesystem deletion never to start. Require bounded watcher process count, physical-exit settlement, healthy-root recovery, stale-generation rejection, overflow plus later events, and fail-closed destructive cleanup. Verify relay build hashes and remote install probes require both relay.js and relay-watcher.js. Launch the built relay with a real PTY and Parcel watch, kill only the watcher child, and require the same relay PID and PTY to answer before a replacement child delivers overflow and a post-crash event.", + "oracle": "Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Previous oracle (preserved verbatim): Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous oracle (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous oracle (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous oracle (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous oracle (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous oracle (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous oracle (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous oracle (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous oracle (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous oracle (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous oracle (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous oracle (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous oracle (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous oracle (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous oracle (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Previous oracle (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Fill the global watcher-child reservation, require native desktop, paired-runtime, and WSL setup to retain typed transient capacity and retry after one serialized release, then cancel pending desktop and paired-client watches and require no retry. Hold one live anchor while aborting 10,000 callers at the capacity, quarantine-assignment, runtime-root, relay-setup, and SSH-registration layers; require exactly one explicit waiter and one base-promise reaction to remain, then zero after settlement. Hold an old relay unsubscribe, queue one same-root setup leader and one anchor, then abort 10,000 joiners; require every cancellation to settle before teardown releases, exactly one anchor waiter, and only one later physical install. Advance the former paired-web callback deadline while capacity remains full, require cancellation ownership to stay live, publish the server token before ready, and require files.unwatch to abort setup before a later slot release can install a native root. Reject the first ready-watch unwatch with a physical-exit promise, resolve that exit later, and require a fresh physical unwatch attempt, registry release, and exactly one stream end. Fill one healthy shared child with multiple roots, map a standard SSH repository's base, Git common directory, and worktree roots to one relay supervisor, then inject IPC disconnect-before-exit, queued and active cancellation, operations during termination, kill-without-exit, repeated unkillable generations, initial and recovery timeouts, and shard failure. During local and SSH deletion, attempt a late terminal spawn whose worktree identity and cwd name different roots, enumerate a non-prefix remote PTY by authoritative worktree ownership, withhold daemon/relay exit callbacks, make attach positively prove a concurrent-shutdown PID dead, and make the provider unavailable; require every acquired admission to roll back on rejection, dead-PID proof to settle physical exit before reaping, timeout to retain ownership, and Git/filesystem deletion never to start. Require bounded watcher process count, physical-exit settlement, healthy-root recovery, stale-generation rejection, overflow plus later events, and fail-closed destructive cleanup. Verify relay build hashes and remote install probes require both relay.js and relay-watcher.js. Launch the built relay with a real PTY and Parcel watch, kill only the watcher child, and require the same relay PID and PTY to answer before a replacement child delivers overflow and a post-crash event.", "commands": [ - "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/shared/promise-settlement-waiters.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", - "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", - "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", - "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/shared/promise-settlement-waiters.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", + "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", + "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", + "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts", @@ -12408,7 +12408,6 @@ "src/main/workspaces/removal/watcher-removal-gate.test.ts", "src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts", "src/main/providers/local-pty/local-pty-provider-shutdown.test.ts", - "src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts", "src/main/daemon/terminal-host/testing/terminal-host.test.ts", "src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts", "src/main/runtime/files/watch/orca-runtime-files-watch.test.ts", @@ -12864,7 +12863,8 @@ "Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope.", "Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope.", "Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed.", - "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed." + "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed.", + "Audit4358 retired original selector fields (historical provenance only; no current execution claim): {\"commands\":[\"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/shared/promise-settlement-waiters.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot\",\"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot\",\"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot\",\"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts\",\"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts\",\"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts\",\"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts\",\"pnpm run build:electron-vite\",\"node config/scripts/diagnostics/runtime-watchers/runtime-file-watcher-resource-probe.mjs\",\"node config/scripts/diagnostics/runtime-watchers/runtime-file-watcher-fault-harness.mjs\",\"ELECTRON_RUN_AS_NODE=1 pnpm exec electron config/scripts/diagnostics/runtime-watchers/runtime-file-watcher-fault-harness.mjs\",\"pnpm exec vitest run --config config/vitest.config.ts\"],\"testFiles\":[\"src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts\",\"src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts\",\"src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts\",\"src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts\",\"src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts\",\"src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts\",\"src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts\",\"src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts\",\"src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts\",\"src/main/ipc/worktrees-orphan-directory-cleanup.test.ts\",\"src/main/ipc/worktrees-remove-preflight.test.ts\",\"src/main/workspaces/removal/watcher-removal-gate.test.ts\",\"src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts\",\"src/main/providers/local-pty/local-pty-provider-shutdown.test.ts\",\"src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts\",\"src/main/daemon/terminal-host/testing/terminal-host.test.ts\",\"src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts\",\"src/main/runtime/files/watch/orca-runtime-files-watch.test.ts\",\"src/main/runtime/workspaces/removal/worktree-teardown.test.ts\",\"src/main/runtime/construction/orca-runtime.test.ts\",\"src/main/runtime/rpc/methods/files/files.test.ts\",\"src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts\",\"src/renderer/src/web/web-runtime-client.test.ts\",\"src/shared/cross-platform-path.test.ts\",\"src/shared/physical-exit-tracker.test.ts\",\"src/shared/promise-settlement-waiters.test.ts\",\"config/scripts/diagnostics/runtime-watchers/runtime-file-watcher-fault-harness.mjs\",\"config/scripts/diagnostics/runtime-watchers/runtime-file-watcher-resource-probe.mjs\"]}" ], "demotionRule": "Demote or quarantine if the fault harness flakes without a product or harness bug, if healthy operation exceeds one runtime watcher child, if total physical operation exceeds eight children including retiring generations, if quarantine children outlive their roots or repeat after fusing, if event delivery becomes unbounded, or if metadata/stat work returns to the serve process." }, diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 880f5d55907..1f0564da74e 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -1537,15 +1537,9 @@ "src/main/daemon/pty-subprocess/testing/pty-input-closed-delivery.node-pty.test.ts": 2130, "src/main/daemon/sessions/testing/pty-session-id.test.ts": 62, "src/main/daemon/pty-subprocess/testing/pty-subprocess-env-retention.test.ts": 431, - "src/main/daemon/pty-subprocess/testing/pty-subprocess-foreground-degraded-scan.test.ts": 406, - "src/main/daemon/pty-subprocess/testing/pty-subprocess-foreground-identity.test.ts": 818, "src/main/daemon/pty-subprocess/testing/pty-subprocess-handle-lifecycle.test.ts": 449, "src/main/daemon/pty-subprocess/testing/pty-subprocess-io-failure-cleanup.test.ts": 1127, - "src/main/daemon/pty-subprocess/testing/pty-subprocess-spawn-file-foreground.test.ts": 528, - "src/main/daemon/pty-subprocess/testing/pty-subprocess-windows-shell-launch.test.ts": 459, - "src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts": 431, "src/main/daemon/pty-subprocess/native-pty-spawn-windows.test.ts": 1264, - "src/main/daemon/pty-subprocess/spawn-file-foreground-rejected-agents.test.ts": 181, "src/main/daemon/sessions/testing/reattach-snapshot.test.ts": 1130, "src/main/daemon/sessions/testing/repro-12101-mouse-tracking-survives-agent-death.test.ts": 276, "src/main/daemon/emulation/testing/repro-7329-remote-snapshot-corruption.test.ts": 172, diff --git a/config/scripts/node-server/qualification/node-server-change-scope.mjs b/config/scripts/node-server/qualification/node-server-change-scope.mjs index ee5990e62e7..7b2fe08f657 100644 --- a/config/scripts/node-server/qualification/node-server-change-scope.mjs +++ b/config/scripts/node-server/qualification/node-server-change-scope.mjs @@ -75,7 +75,6 @@ const ALWAYS_PREFIXES = [ 'src/main/daemon/pty-subprocess/spawn-environment.ts', 'src/main/daemon/pty-subprocess/spawn-file-child-processes.ts', 'src/main/daemon/pty-subprocess/spawn-file-foreground-process.ts', - 'src/main/daemon/pty-subprocess/spawn-file-foreground-rejected-agents.test.ts', 'src/main/daemon/pty-subprocess/spawn-preflight.ts', 'src/main/daemon/pty-subprocess/subprocess-handle.ts', 'src/main/providers/', diff --git a/config/scripts/node-server/qualification/node-server-test-paths.mjs b/config/scripts/node-server/qualification/node-server-test-paths.mjs index 22a124b5f34..29e3d5979f6 100644 --- a/config/scripts/node-server/qualification/node-server-test-paths.mjs +++ b/config/scripts/node-server/qualification/node-server-test-paths.mjs @@ -35,10 +35,8 @@ export function nodeServerTestPaths({ 'src/main/daemon/pty-subprocess/spawn-environment.ts', 'src/main/daemon/pty-subprocess/spawn-file-child-processes.ts', 'src/main/daemon/pty-subprocess/spawn-file-foreground-process.ts', - 'src/main/daemon/pty-subprocess/spawn-file-foreground-rejected-agents.test.ts', 'src/main/daemon/pty-subprocess/spawn-preflight.ts', 'src/main/daemon/pty-subprocess/subprocess-handle.ts', - 'src/main/daemon/pty-subprocess/testing/pty-subprocess-spawn-file-foreground.test.ts', ...(artifact ? [ 'tests/e2e/daemon-running-work-probe.unit.test.ts', diff --git a/mobile/src/files/session-documents/use-mobile-file-tap-handlers.test.tsx b/mobile/src/files/session-documents/use-mobile-file-tap-handlers.test.tsx deleted file mode 100644 index 771c4dc74ee..00000000000 --- a/mobile/src/files/session-documents/use-mobile-file-tap-handlers.test.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { createElement } from 'react' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { RpcResponse } from '../../transport/rpc/rpc-wire-types' - -const push = vi.hoisted(() => vi.fn()) -vi.mock('../../navigation/workspace-execution-host', () => ({ - useWorkspaceExecutionHost: () => undefined -})) -vi.mock('../../navigation/workspace-route-handoff', () => ({ - useWorkspaceRouteHandoff: () => ({ push }) -})) -vi.mock('../../platform/haptics', () => ({ triggerSelection: () => {} })) - -import { useMobileFileTapHandlers } from './use-mobile-file-tap-handlers' - -type Client = { - getGeneration: () => number - sendRequest: () => Promise -} - -const SIBLING_ROOT: RpcResponse = { - id: 'rpc', - ok: true, - result: { - worktree: 'wt-2', - relativePath: '', - absolutePath: '/workspaces/wt-2', - exists: true, - isDirectory: true - }, - _meta: { runtimeId: 'runtime-1' } -} - -function mountChat() { - let generation = 1 - let reply: (response: RpcResponse) => void = () => {} - const createClient = (): Client => ({ - getGeneration: () => generation, - sendRequest: () => - new Promise((resolve) => { - reply = resolve - }) - }) - const reportChatTapFailure = vi.fn() - let tap: (pathText: string) => void = () => {} - const stable = { - nativeChatSessionId: 'session-1', - activeHandleRef: { current: null }, - terminalCwdRef: { current: new Map() }, - openBrowser: () => {}, - fetchSessionTabs: async () => {}, - getSessionTabs: () => [], - getActiveSessionTabId: () => 'chat-tab', - getActiveSessionTabType: () => 'agent-session', - switchSessionTab: () => {}, - scheduleDelayedAction: () => {}, - reportChatTapFailure, - // Held constant so only the authority fence can drop the reply. - getSendCompletionGeneration: () => 1 - } - function Probe(props: { client: Client; hostId: string }) { - tap = useMobileFileTapHandlers({ - ...stable, - ...props, - worktreeId: 'wt-1' - }).handleNativeChatFileTap - return null - } - const first = createClient() - let renderer!: ReactTestRenderer - act(() => { - renderer = create(createElement(Probe, { client: first, hostId: 'host-a' })) - }) - return { - first, - createClient, - reportChatTapFailure, - tap: () => tap('/workspaces/wt-2'), - render: (client: Client, hostId: string) => - act(() => renderer.update(createElement(Probe, { client, hostId }))), - moveGeneration: () => { - generation = 2 - }, - reply: async () => { - await act(async () => { - reply(SIBLING_ROOT) - await new Promise((resolve) => setTimeout(resolve, 0)) - }) - } - } -} - -describe('useMobileFileTapHandlers workspace-root reply', () => { - beforeEach(() => { - push.mockClear() - }) - - it('drops the reply after the host changes away and back', async () => { - const chat = mountChat() - chat.tap() - chat.render(chat.first, 'host-b') - chat.render(chat.first, 'host-a') - await chat.reply() - - expect(push).not.toHaveBeenCalled() - expect(chat.reportChatTapFailure).not.toHaveBeenCalled() - }) -}) diff --git a/mobile/src/session/agent-launch/launched-agent-opener.test.ts b/mobile/src/session/agent-launch/launched-agent-opener.test.ts deleted file mode 100644 index 814652315b2..00000000000 --- a/mobile/src/session/agent-launch/launched-agent-opener.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' - -vi.mock('expo-router', () => ({})) - -import { planLaunchedAgentOpen } from './launched-agent-opener' - -const SESSION = '[hostId]/session/[worktreeId]' -const SOURCE_CONTROL = '[hostId]/source-control/[worktreeId]' -const session = { hostId: 'host-a', worktreeId: 'wt-a', executionHost: undefined } -const paneKey = 'tab-1:11111111-1111-4111-8111-111111111111' -const sessionEntry = (worktreeId: string) => ({ - name: SESSION, - params: { hostId: 'host-a', worktreeId, name: 'Workspace A', paneKey: '' } -}) -const sourceControl = { name: SOURCE_CONTROL, params: { hostId: 'host-a', worktreeId: 'wt-a' } } - -describe('planLaunchedAgentOpen', () => { - it('returns only to the nearest session entry at or beneath this screen', () => { - const shell = { - kind: 'shell', - href: '/h/host-a/session/wt-a?paneKey=tab-1%3A11111111-1111-4111-8111-111111111111' - } - // Dismissing lands on the nearest session entry, which here shows another workspace. - expect( - planLaunchedAgentOpen({ - stack: [sessionEntry('wt-a'), sessionEntry('wt-b'), sourceControl], - current: 2, - session, - paneKey, - shellReturns: true - }) - ).toEqual(shell) - // An entry above this screen is not one to go back to. - expect( - planLaunchedAgentOpen({ - stack: [sourceControl, sessionEntry('wt-a')], - current: 0, - session, - paneKey, - shellReturns: true - }) - ).toEqual(shell) - }) -}) diff --git a/src/main/daemon/pty-subprocess/spawn-file-foreground-rejected-agents.test.ts b/src/main/daemon/pty-subprocess/spawn-file-foreground-rejected-agents.test.ts deleted file mode 100644 index 4266de9cc2d..00000000000 --- a/src/main/daemon/pty-subprocess/spawn-file-foreground-rejected-agents.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -import type { IPty } from 'node-pty' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { ProcessTableRow } from '../../../shared/process-table-snapshot' -import { __setWindowsProcessTreeLoaderForTests } from '../../windows/windows-process-table' -import { - resolveSpawnFileForegroundFromRows, - resolveSpawnFileForegroundProcess -} from './spawn-file-foreground-process' - -const { members } = vi.hoisted(() => ({ members: vi.fn() })) -vi.mock('../../providers/foreground/windows/pty-job-membership', () => ({ - readWindowsPtyJobProcessIds: members -})) - -const proc: IPty = { - pid: 100, - cols: 80, - rows: 24, - handleFlowControl: false, - process: 'powershell.exe', - onData: () => ({ dispose() {} }), - onExit: () => ({ dispose() {} }), - write() {}, - resize() {}, - clear() {}, - kill() {}, - pause() {}, - resume() {} -} - -const root: ProcessTableRow = { - pid: 100, - ppid: 1, - pgid: 100, - tpgid: 101, - tty: 'pts/test', - stat: 'S', - startTime: 'shell-start', - command: '/bin/zsh' -} - -beforeEach(() => members.mockReturnValue(new Set([100, 101, 102]))) -afterEach(() => { - __setWindowsProcessTreeLoaderForTests() - vi.restoreAllMocks() - vi.clearAllMocks() -}) - -describe('POSIX static-name agent selection', () => { - it('does not pick a rejected sibling agent by its executable basename', () => { - expect( - resolveSpawnFileForegroundFromRows( - [ - root, - { ...root, pid: 101, ppid: 100, pgid: 101, stat: 'S+', command: 'claude' }, - { ...root, pid: 102, ppid: 100, pgid: 101, stat: 'S+', command: 'codex' } - ], - 100 - ) - ).toEqual({ available: true, processName: null }) - }) - - it('does not promote a headless one-shot agent from its executable basename', () => { - expect( - resolveSpawnFileForegroundFromRows( - [ - root, - { ...root, pid: 101, ppid: 100, pgid: 101, stat: 'S+', command: 'claude -p "review"' } - ], - 100 - ) - ).toEqual({ available: true, processName: null }) - }) - - it('reports a foreground whose argv ps withheld as unavailable, never as no agent', () => { - expect( - resolveSpawnFileForegroundFromRows( - [root, { ...root, pid: 101, ppid: 100, pgid: 101, stat: 'S+', command: '(node)' }], - 100 - ) - ).toEqual({ available: false, processName: null }) - }) -}) - -describe('Windows static-name agent selection', () => { - function installRows(names: string[]): void { - vi.spyOn(process, 'platform', 'get').mockReturnValue('win32') - const rows = [ - { pid: process.pid, ppid: 0, name: 'vitest.exe', commandLine: 'vitest' }, - { pid: 100, ppid: 1, name: 'powershell.exe', commandLine: 'powershell.exe' }, - ...names.map((name, index) => ({ pid: 101 + index, ppid: 100, name, commandLine: name })) - ] - __setWindowsProcessTreeLoaderForTests(() => ({ - ProcessDataFlag: { None: 0, Memory: 1, CommandLine: 2, CreationTime: 4 }, - getAllProcesses: (callback) => callback(rows) - })) - } - - it('does not re-admit a detached agent through owned job membership', async () => { - installRows(['droid.exe']) - const consoleMembers = vi.fn(async () => new Set([100, 999])) - expect( - await resolveSpawnFileForegroundProcess(proc, 'powershell.exe', { - fresh: true, - readWindowsConsoleAttachedProcessIds: consoleMembers - }) - ).toEqual({ available: true, processName: 'powershell.exe' }) - expect(consoleMembers).toHaveBeenCalledOnce() - }) - - it('does not choose a rejected sibling agent from the identity table', async () => { - installRows(['claude.exe', 'codex.exe']) - expect( - await resolveSpawnFileForegroundProcess(proc, 'powershell.exe', { fresh: true }) - ).toEqual({ available: true, processName: 'powershell.exe' }) - }) - - it('retains a positively authorized agent', async () => { - installRows(['droid.exe']) - expect( - await resolveSpawnFileForegroundProcess(proc, 'powershell.exe', { - fresh: true, - readWindowsConsoleAttachedProcessIds: async () => new Set([100, 101]) - }) - ).toEqual({ available: true, processName: 'droid', processId: 101 }) - }) -}) diff --git a/src/main/daemon/pty-subprocess/testing/pty-subprocess-foreground-degraded-scan.test.ts b/src/main/daemon/pty-subprocess/testing/pty-subprocess-foreground-degraded-scan.test.ts deleted file mode 100644 index d031496fd6f..00000000000 --- a/src/main/daemon/pty-subprocess/testing/pty-subprocess-foreground-degraded-scan.test.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { mkdtempSync, rmSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' - -const { - spawnMock, - isPwshAvailableMock, - resolveAgentForegroundProcessMock, - readConptyMock, - jobReadableMock -} = vi.hoisted(() => ({ - spawnMock: vi.fn(), - isPwshAvailableMock: vi.fn(), - resolveAgentForegroundProcessMock: vi.fn(), - readConptyMock: vi.fn(), - jobReadableMock: vi.fn() -})) - -vi.mock('node-pty', () => ({ spawn: spawnMock })) -vi.mock('../../../pty/shell-startup/pwsh', () => ({ isPwshAvailable: isPwshAvailableMock })) - -const PWSH7_ABS = 'C:\\Program Files\\PowerShell\\7\\pwsh.exe' -const WINDOWS_POWERSHELL_ABS = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' -const CMD_ABS = 'C:\\Windows\\System32\\cmd.exe' -vi.mock('../../../providers/shell-launch/windows/powershell-executable', () => ({ - resolveWindowsPowerShellExecutablePath: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' ? PWSH7_ABS : WINDOWS_POWERSHELL_ABS, - resolveWindowsPowerShellSpawnChain: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' - ? [PWSH7_ABS, WINDOWS_POWERSHELL_ABS, CMD_ABS] - : [WINDOWS_POWERSHELL_ABS, CMD_ABS], - getWindowsCmdPath: () => CMD_ABS -})) - -vi.mock('../../../providers/foreground/agent-process', () => ({ - resolveAgentForegroundProcessWithAvailability: (...args: unknown[]) => - resolveAgentForegroundProcessMock(...args) -})) - -vi.mock('../../../providers/foreground/windows/pty-job-membership', () => ({ - readWindowsPtyJobProcessIds: (...args: unknown[]) => readConptyMock(...args), - isWindowsPtyJobReadable: () => jobReadableMock() -})) - -import { createPtySubprocess } from '../pty-subprocess' -import { createConptyPreludeSender } from './pty-subprocess-test-harness' - -const BASE_TIME_MS = 1_000_000 - -function mockPtyProcess(processName: string, pid = 12345) { - const sendConptyPrelude = createConptyPreludeSender() - return { - pid, - write: vi.fn(), - resize: vi.fn(), - kill: vi.fn(), - process: processName, - onData: vi.fn((cb: (data: string) => void) => { - sendConptyPrelude(cb) - return { dispose: vi.fn() } - }), - onExit: vi.fn(() => ({ dispose: vi.fn() })) - } -} - -async function flushAsyncTicks(count = 12): Promise { - for (let i = 0; i < count; i++) { - await Promise.resolve() - } -} - -async function readForegroundAt( - handle: { getForegroundProcess: () => string | null }, - atMs: number -): Promise { - vi.setSystemTime(BASE_TIME_MS + atMs) - const foreground = handle.getForegroundProcess() - await flushAsyncTicks() - return foreground -} - -describe('daemon pty foreground degraded-scan handling', () => { - let platform: PropertyDescriptor | undefined - let previousUserDataPath: string | undefined - let userDataPath: string - - beforeEach(() => { - spawnMock.mockReset() - isPwshAvailableMock.mockReset() - isPwshAvailableMock.mockReturnValue(false) - resolveAgentForegroundProcessMock.mockReset() - readConptyMock.mockReset() - readConptyMock.mockReturnValue(null) - jobReadableMock.mockReset() - jobReadableMock.mockReturnValue(true) - previousUserDataPath = process.env.ORCA_USER_DATA_PATH - userDataPath = mkdtempSync(join(tmpdir(), 'daemon-pty-degraded-scan-test-')) - process.env.ORCA_USER_DATA_PATH = userDataPath - platform = Object.getOwnPropertyDescriptor(process, 'platform') - vi.useFakeTimers({ toFake: ['Date'] }) - vi.setSystemTime(BASE_TIME_MS) - }) - - afterEach(() => { - vi.useRealTimers() - if (platform) { - Object.defineProperty(process, 'platform', platform) - } - if (previousUserDataPath === undefined) { - delete process.env.ORCA_USER_DATA_PATH - } else { - process.env.ORCA_USER_DATA_PATH = previousUserDataPath - } - rmSync(userDataPath, { recursive: true, force: true }) - }) - - async function spawnWindowsShell() { - Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' }) - const proc = mockPtyProcess('powershell.exe') - spawnMock.mockReturnValue(proc) - const handle = await createPtySubprocess({ sessionId: 'test', cols: 80, rows: 24 }) - return { proc, handle } - } - - it('keeps raw confirmation separate from a known detached writer decision', async () => { - resolveAgentForegroundProcessMock - .mockResolvedValueOnce({ available: true, processName: 'claude', processId: 999 }) - .mockResolvedValue({ available: true, processName: 'powershell.exe' }) - readConptyMock.mockReturnValue(new Set([12345, 999])) - const { handle } = await spawnWindowsShell() - await readForegroundAt(handle, 0) - if (!handle.confirmForegroundProcess) { - throw new Error('fixture confirmer missing') - } - vi.setSystemTime(BASE_TIME_MS + 40_000) - resolveAgentForegroundProcessMock.mockClear() - expect(await handle.confirmForegroundProcess({ decision: true })).toBe('claude') - expect(resolveAgentForegroundProcessMock).toHaveBeenCalledTimes(1) - expect(await handle.confirmForegroundProcess()).toBe('powershell.exe') - expect(resolveAgentForegroundProcessMock).toHaveBeenLastCalledWith( - 12345, - 'powershell.exe', - expect.not.objectContaining({ anchorProcessId: 999 }) - ) - }) -}) diff --git a/src/main/daemon/pty-subprocess/testing/pty-subprocess-foreground-identity.test.ts b/src/main/daemon/pty-subprocess/testing/pty-subprocess-foreground-identity.test.ts deleted file mode 100644 index 76fba8ee150..00000000000 --- a/src/main/daemon/pty-subprocess/testing/pty-subprocess-foreground-identity.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -import type * as PtySpawnValidationTypes from '../../../providers/working-directory/validation' -import { describe, expect, it, vi } from 'vitest' -import type * as LocalPtyUtils from '../../../providers/local-pty/local-pty-executable-spawn' - -const { - spawnMock, - isPwshAvailableMock, - validateWorkingDirectoryMock, - resolveUnixShellPathMock, - resolveAgentForegroundProcessMock -} = vi.hoisted(() => ({ - spawnMock: vi.fn(), - isPwshAvailableMock: vi.fn(), - resolveUnixShellPathMock: vi.fn((shellPath: string) => shellPath), - resolveAgentForegroundProcessMock: vi.fn(), - validateWorkingDirectoryMock: vi.fn((cwd: string) => { - if (cwd.includes('definitely-missing')) { - throw new Error( - `Working directory "${cwd}" does not exist. It may have been deleted or is on an unmounted volume.` - ) - } - }) -})) - -vi.mock('node-pty', () => ({ - spawn: spawnMock -})) - -vi.mock('../../../pty/shell-startup/pwsh', () => ({ - isPwshAvailable: isPwshAvailableMock -})) - -// Keep PowerShell resolution deterministic on non-Windows hosts. -const PWSH7_ABS = 'C:\\Program Files\\PowerShell\\7\\pwsh.exe' -const WINDOWS_POWERSHELL_ABS = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' -const CMD_ABS = 'C:\\Windows\\System32\\cmd.exe' -vi.mock('../../../providers/shell-launch/windows/powershell-executable', () => ({ - resolveWindowsPowerShellExecutablePath: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' ? PWSH7_ABS : WINDOWS_POWERSHELL_ABS, - resolveWindowsPowerShellSpawnChain: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' - ? [PWSH7_ABS, WINDOWS_POWERSHELL_ABS, CMD_ABS] - : [WINDOWS_POWERSHELL_ABS, CMD_ABS], - getWindowsCmdPath: () => CMD_ABS -})) - -vi.mock('../../../providers/local-pty/local-pty-executable-spawn', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - getNodePtySpawnHelperCandidates: () => [import.meta.filename], - resolveUnixShellPath: resolveUnixShellPathMock - } -}) - -vi.mock('../../../providers/working-directory/validation', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - validateWorkingDirectory: validateWorkingDirectoryMock, - validateWorkingDirectoryAsync: validateWorkingDirectoryMock - } -}) - -vi.mock('../../../providers/foreground/agent-process', () => ({ - resolveAgentForegroundProcessWithAvailability: async (...args: unknown[]) => { - const value = await resolveAgentForegroundProcessMock(...args) - return value && typeof value === 'object' && 'available' in value - ? value - : { available: true, processName: value } - } -})) - -vi.mock('../../../providers/foreground/windows/pty-job-membership', () => ({ - readWindowsPtyJobProcessIds: () => new Set([12345]), - isWindowsPtyJobReadable: () => true -})) - -import { createPtySubprocess } from '../pty-subprocess' -import { mockPtyProcess, useDaemonPtySubprocessEnv } from './pty-subprocess-test-harness' - -describe('createPtySubprocess', () => { - useDaemonPtySubprocessEnv({ - spawnMock, - isPwshAvailableMock, - resolveUnixShellPathMock, - resolveAgentForegroundProcessMock, - validateWorkingDirectoryMock - }) - - it('awaits a fresh delayed Windows scan instead of serving the shell fallback', async () => { - const proc = mockPtyProcess() - proc.process = 'powershell.exe' - spawnMock.mockReturnValue(proc) - const platform = Object.getOwnPropertyDescriptor(process, 'platform') - Object.defineProperty(process, 'platform', { value: 'win32' }) - let resolveFresh!: (processName: string) => void - resolveAgentForegroundProcessMock.mockReturnValue( - new Promise((resolve) => { - resolveFresh = resolve - }) - ) - - try { - const handle = await createPtySubprocess({ sessionId: 'test', cols: 80, rows: 24 }) - const confirmation = handle.confirmForegroundProcess!() - let settled = false - void confirmation.then(() => { - settled = true - }) - await Promise.resolve() - expect(settled).toBe(false) - - resolveFresh('droid') - await expect(confirmation).resolves.toBe('droid') - expect(resolveAgentForegroundProcessMock).toHaveBeenCalledExactlyOnceWith( - proc.pid, - 'powershell.exe', - expect.objectContaining({ fresh: true, forceProcessScan: true }) - ) - } finally { - if (platform) { - Object.defineProperty(process, 'platform', platform) - } - } - }) - - it('returns null when a fresh Windows confirmation scan is unavailable', async () => { - const proc = mockPtyProcess() - proc.process = 'powershell.exe' - spawnMock.mockReturnValue(proc) - const platform = Object.getOwnPropertyDescriptor(process, 'platform') - Object.defineProperty(process, 'platform', { value: 'win32' }) - resolveAgentForegroundProcessMock.mockResolvedValue({ - available: false, - processName: 'powershell.exe' - }) - - try { - const handle = await createPtySubprocess({ sessionId: 'test', cols: 80, rows: 24 }) - await expect(handle.confirmForegroundProcess!()).resolves.toBeNull() - } finally { - if (platform) { - Object.defineProperty(process, 'platform', platform) - } - } - }) - - it('returns null when a recognized Windows fallback disappears during confirmation', async () => { - const proc = mockPtyProcess() - proc.process = 'droid' - spawnMock.mockReturnValue(proc) - const platform = Object.getOwnPropertyDescriptor(process, 'platform') - Object.defineProperty(process, 'platform', { value: 'win32' }) - resolveAgentForegroundProcessMock.mockResolvedValue({ - available: true, - processName: null - }) - - try { - const handle = await createPtySubprocess({ sessionId: 'test', cols: 80, rows: 24 }) - await expect(handle.confirmForegroundProcess!()).resolves.toBeNull() - } finally { - if (platform) { - Object.defineProperty(process, 'platform', platform) - } - } - }) - - it('does not let stale shell enrichment clear a newer direct agent foreground cache', async () => { - const proc = mockPtyProcess() - proc.process = 'powershell.exe' - spawnMock.mockReturnValue(proc) - const platform = Object.getOwnPropertyDescriptor(process, 'platform') - Object.defineProperty(process, 'platform', { value: 'win32' }) - let resolveForeground!: (processName: string) => void - resolveAgentForegroundProcessMock.mockReturnValue( - new Promise((resolve) => { - resolveForeground = resolve - }) - ) - - try { - const handle = await createPtySubprocess({ - sessionId: 'test', - cols: 80, - rows: 24 - }) - - expect(handle.getForegroundProcess()).toBe('powershell.exe') - proc.process = 'codex' - expect(handle.getForegroundProcess()).toBe('codex') - - resolveForeground('powershell.exe') - await Promise.resolve() - await Promise.resolve() - - proc.process = 'powershell.exe' - expect(handle.getForegroundProcess()).toBe('codex') - } finally { - if (platform) { - Object.defineProperty(process, 'platform', platform) - } - } - }) -}) diff --git a/src/main/daemon/pty-subprocess/testing/pty-subprocess-handle-lifecycle.test.ts b/src/main/daemon/pty-subprocess/testing/pty-subprocess-handle-lifecycle.test.ts index 03c6f59a3d2..1301b59fafd 100644 --- a/src/main/daemon/pty-subprocess/testing/pty-subprocess-handle-lifecycle.test.ts +++ b/src/main/daemon/pty-subprocess/testing/pty-subprocess-handle-lifecycle.test.ts @@ -102,18 +102,6 @@ describe('createPtySubprocess', () => { validateWorkingDirectoryMock }) - it('replays data emitted before the Session registers onData', async () => { - const proc = mockPtyProcess() - spawnMock.mockReturnValue(proc) - - const handle = await createPtySubprocess({ sessionId: 'test', cols: 80, rows: 24 }) - proc._simulateData('early setup output\r\n') - const data: string[] = [] - handle.onData((d) => data.push(d)) - - expect(data).toEqual(['early setup output\r\n']) - }) - it('replays pre-listener data before a pre-listener exit', async () => { const proc = mockPtyProcess() spawnMock.mockReturnValue(proc) @@ -129,22 +117,4 @@ describe('createPtySubprocess', () => { expect(data).toEqual(['last output\r\n']) expect(codes).toEqual([7]) }) - - it('preserves pre-listener data when onExit is registered before onData', async () => { - const proc = mockPtyProcess() - spawnMock.mockReturnValue(proc) - - const handle = await createPtySubprocess({ sessionId: 'test', cols: 80, rows: 24 }) - proc._simulateData('last output\r\n') - proc._simulateExit(7) - const events: string[] = [] - handle.onExit((code) => events.push(`exit:${code}`)) - handle.onData((data) => events.push(`data:${data}`)) - - expect(events).toEqual(['exit:7', 'data:last output\r\n']) - }) - - // UnixTerminal.destroy() can send async SIGHUP after exit; neutralize kill before PID reuse. - - // Direct process.kill calls also need dead guards because a reaped PID can be reused. }) diff --git a/src/main/daemon/pty-subprocess/testing/pty-subprocess-spawn-file-foreground.test.ts b/src/main/daemon/pty-subprocess/testing/pty-subprocess-spawn-file-foreground.test.ts deleted file mode 100644 index 041a08717f5..00000000000 --- a/src/main/daemon/pty-subprocess/testing/pty-subprocess-spawn-file-foreground.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import type { IPty } from 'node-pty' -import { afterEach, describe, expect, it, vi } from 'vitest' -import type { ProcessTableRow } from '../../../../shared/process-table-snapshot' - -import { createDaemonPtySubprocessHandle } from '../subprocess-handle' -import { inspectSpawnFileChildProcessesFromRows } from '../spawn-file-child-processes' - -const { members, readWindows } = vi.hoisted(() => ({ - members: vi.fn(), - readWindows: vi.fn() -})) - -vi.mock('../../../providers/foreground/windows/pty-job-membership', () => ({ - readWindowsPtyJobProcessIds: members, - isWindowsPtyJobReadable: () => true -})) -vi.mock('../../../windows/windows-process-table', () => ({ - readWindowsProcessIdentityTable: readWindows, - readWindowsProcessIdentityTableFresh: readWindows -})) - -function createHandle(loginWrapper = false, shellPath = '/bin/zsh') { - const proc: IPty & { processNameIsSpawnFile: true } = { - pid: 100, - cols: 80, - rows: 24, - handleFlowControl: false, - process: loginWrapper ? '/Applications/Orca shell login' : '/bin/zsh', - processNameIsSpawnFile: true, - onData: () => ({ dispose() {} }), - onExit: () => ({ dispose() {} }), - write() {}, - resize() {}, - clear() {}, - kill() {}, - pause() {}, - resume() {} - } - return createDaemonPtySubprocessHandle({ - process: proc, - shellPath, - spawnCwd: '/tmp', - env: {}, - startupCommandDeliveredInShellArgs: false, - reportsChildExitStatus: true, - sessionId: 'static-name', - startupAgentRecognition: null - }) -} - -afterEach(() => { - vi.useRealTimers() - vi.restoreAllMocks() - vi.resetAllMocks() -}) - -// ps spells "no controlling terminal" as `?` on Linux, `??` on macOS and `-` on some builds. -describe.each(['??', '?', '-'] as const)('root on no controlling terminal (tty=%s)', (tty) => { - const detachedRoot: ProcessTableRow = { - pid: 100, - ppid: 1, - pgid: 100, - tpgid: 100, - tty, - startTime: 'Sat Sep 26 13:44:29 2026', - stat: 'S', - command: '/bin/zsh' - } - const detachedTree: ProcessTableRow[] = [ - detachedRoot, - { ...detachedRoot, pid: 101, ppid: 100, pgid: 100, command: 'claude' } - ] - - it('reports child evidence as unverifiable rather than reading an unfenceable table', () => { - expect(inspectSpawnFileChildProcessesFromRows(detachedTree, 100, 'zsh')).toBe('unverifiable') - }) -}) - -it('accepts only a complete actual-shell Git Bash launch chain', async () => { - vi.spyOn(process, 'platform', 'get').mockReturnValue('win32') - const shellPath = 'C:\\Program Files\\Git\\bin\\bash.exe' - const handle = createHandle(false, shellPath) - members.mockReturnValue(new Set([100, 102])) - readWindows.mockResolvedValue([ - { pid: 100, ppid: 1, name: 'bash.exe', creationTimeMs: 1 }, - { pid: 102, ppid: 100, name: 'bash.exe', creationTimeMs: 2 } - ]) - expect(await handle.inspectChildProcesses?.()).toBe('no-children') - readWindows.mockResolvedValue([{ pid: 100, ppid: 1, name: 'bash.exe' }]) - expect(await handle.inspectChildProcesses?.()).toBe('unverifiable') - readWindows.mockResolvedValue([ - { pid: 100, ppid: 1, name: 'bash.exe' }, - { pid: 102, ppid: 100, name: 'agent.exe' } - ]) - expect(await handle.inspectChildProcesses?.()).toBe('children') - members.mockReturnValue(new Set([102])) - expect(await handle.inspectChildProcesses?.()).toBe('unverifiable') - handle.dispose() -}) diff --git a/src/main/daemon/pty-subprocess/testing/pty-subprocess-windows-shell-launch.test.ts b/src/main/daemon/pty-subprocess/testing/pty-subprocess-windows-shell-launch.test.ts deleted file mode 100644 index e866dea8824..00000000000 --- a/src/main/daemon/pty-subprocess/testing/pty-subprocess-windows-shell-launch.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import type * as PtySpawnValidationTypes from '../../../providers/working-directory/validation' -import { describe, expect, it, vi } from 'vitest' -import { setImmediate as nextNativeTurn } from 'node:timers/promises' -import type * as LocalPtyUtils from '../../../providers/local-pty/local-pty-executable-spawn' - -const { - spawnMock, - isPwshAvailableMock, - validateWorkingDirectoryMock, - resolveUnixShellPathMock, - resolveAgentForegroundProcessMock -} = vi.hoisted(() => ({ - spawnMock: vi.fn(), - isPwshAvailableMock: vi.fn(), - resolveUnixShellPathMock: vi.fn((shellPath: string) => shellPath), - resolveAgentForegroundProcessMock: vi.fn(), - validateWorkingDirectoryMock: vi.fn((cwd: string) => { - if (cwd.includes('definitely-missing')) { - throw new Error( - `Working directory "${cwd}" does not exist. It may have been deleted or is on an unmounted volume.` - ) - } - }) -})) - -vi.mock('node-pty', () => ({ - spawn: spawnMock -})) - -vi.mock('../../../pty/shell-startup/pwsh', () => ({ - isPwshAvailable: isPwshAvailableMock -})) - -// Keep PowerShell resolution deterministic on non-Windows hosts. -const PWSH7_ABS = 'C:\\Program Files\\PowerShell\\7\\pwsh.exe' -const WINDOWS_POWERSHELL_ABS = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' -const CMD_ABS = 'C:\\Windows\\System32\\cmd.exe' -vi.mock('../../../providers/shell-launch/windows/powershell-executable', () => ({ - resolveWindowsPowerShellExecutablePath: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' ? PWSH7_ABS : WINDOWS_POWERSHELL_ABS, - resolveWindowsPowerShellSpawnChain: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' - ? [PWSH7_ABS, WINDOWS_POWERSHELL_ABS, CMD_ABS] - : [WINDOWS_POWERSHELL_ABS, CMD_ABS], - getWindowsCmdPath: () => CMD_ABS -})) - -vi.mock('../../../providers/local-pty/local-pty-executable-spawn', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - resolveUnixShellPath: resolveUnixShellPathMock - } -}) - -vi.mock('../../../providers/working-directory/validation', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - validateWorkingDirectory: validateWorkingDirectoryMock, - validateWorkingDirectoryAsync: validateWorkingDirectoryMock - } -}) - -vi.mock('../../../providers/foreground/agent-process', () => ({ - resolveAgentForegroundProcessWithAvailability: async (...args: unknown[]) => { - const value = await resolveAgentForegroundProcessMock(...args) - return value && typeof value === 'object' && 'available' in value - ? value - : { available: true, processName: value } - } -})) - -vi.mock('../../../providers/foreground/windows/pty-job-membership', () => ({ - readWindowsPtyJobProcessIds: () => new Set([12345]), - isWindowsPtyJobReadable: () => true -})) - -import { createPtySubprocess } from '../pty-subprocess' -import { - createPtyProcessEventEmitter, - mockPtyProcess, - useDaemonPtySubprocessEnv -} from './pty-subprocess-test-harness' - -describe('createPtySubprocess', () => { - useDaemonPtySubprocessEnv({ - spawnMock, - isPwshAvailableMock, - resolveUnixShellPathMock, - resolveAgentForegroundProcessMock, - validateWorkingDirectoryMock - }) - - it('delivers output and exit that land while ConPTY startup is still being confirmed', async () => { - const proc = { ...mockPtyProcess(), ...createPtyProcessEventEmitter() } - spawnMock.mockReturnValue(proc) - const platform = Object.getOwnPropertyDescriptor(process, 'platform') - Object.defineProperty(process, 'platform', { value: 'win32' }) - try { - let published = false - const pending = createPtySubprocess({ sessionId: 'test', cols: 80, rows: 24 }).then( - (handle) => { - published = true - return handle - } - ) - await vi.waitFor(() => expect(spawnMock).toHaveBeenCalledOnce()) - await nextNativeTurn() - expect(published).toBe(false) - proc._simulateData('\x1b[1t') - proc._simulateExit(7) - const handle = await pending - const data: string[] = [] - const exitCode = new Promise((resolve) => handle.onExit((code) => resolve(code))) - handle.onData((chunk) => data.push(chunk)) - expect(await exitCode).toBe(7) - expect(data[0]).toBe('\x1b[1t') - expect(spawnMock).toHaveBeenCalledOnce() - } finally { - if (platform) { - Object.defineProperty(process, 'platform', platform) - } - } - }) -}) diff --git a/src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts b/src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts deleted file mode 100644 index 488e9f08572..00000000000 --- a/src/main/daemon/pty-subprocess/testing/pty-subprocess.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import type * as PtySpawnValidationTypes from '../../../providers/working-directory/validation' -import { describe, expect, it, vi } from 'vitest' -import type * as LocalPtyUtils from '../../../providers/local-pty/local-pty-executable-spawn' - -const { - spawnMock, - isPwshAvailableMock, - validateWorkingDirectoryMock, - resolveUnixShellPathMock, - resolveAgentForegroundProcessMock -} = vi.hoisted(() => ({ - spawnMock: vi.fn(), - isPwshAvailableMock: vi.fn(), - resolveUnixShellPathMock: vi.fn((shellPath: string) => shellPath), - resolveAgentForegroundProcessMock: vi.fn(), - validateWorkingDirectoryMock: vi.fn((cwd: string) => { - if (cwd.includes('definitely-missing')) { - throw new Error( - `Working directory "${cwd}" does not exist. It may have been deleted or is on an unmounted volume.` - ) - } - }) -})) - -vi.mock('node-pty', () => ({ - spawn: spawnMock -})) - -vi.mock('../../../pty/shell-startup/pwsh', () => ({ - isPwshAvailable: isPwshAvailableMock -})) - -// Keep PowerShell resolution deterministic on non-Windows hosts. -const PWSH7_ABS = 'C:\\Program Files\\PowerShell\\7\\pwsh.exe' -const WINDOWS_POWERSHELL_ABS = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' -const CMD_ABS = 'C:\\Windows\\System32\\cmd.exe' -vi.mock('../../../providers/shell-launch/windows/powershell-executable', () => ({ - resolveWindowsPowerShellExecutablePath: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' ? PWSH7_ABS : WINDOWS_POWERSHELL_ABS, - resolveWindowsPowerShellSpawnChain: (family: 'pwsh.exe' | 'powershell.exe') => - family === 'pwsh.exe' - ? [PWSH7_ABS, WINDOWS_POWERSHELL_ABS, CMD_ABS] - : [WINDOWS_POWERSHELL_ABS, CMD_ABS], - getWindowsCmdPath: () => CMD_ABS -})) - -vi.mock('../../../providers/local-pty/local-pty-executable-spawn', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - resolveUnixShellPath: resolveUnixShellPathMock - } -}) - -vi.mock('../../../providers/working-directory/validation', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - validateWorkingDirectory: validateWorkingDirectoryMock, - validateWorkingDirectoryAsync: validateWorkingDirectoryMock - } -}) - -vi.mock('../../../providers/foreground/agent-process', () => ({ - resolveAgentForegroundProcessWithAvailability: async (...args: unknown[]) => { - const value = await resolveAgentForegroundProcessMock(...args) - return value && typeof value === 'object' && 'available' in value - ? value - : { available: true, processName: value } - } -})) - -vi.mock('../../../providers/foreground/windows/pty-job-membership', () => ({ - readWindowsPtyJobProcessIds: () => new Set([12345]), - isWindowsPtyJobReadable: () => true -})) -import { createPtySubprocess } from '../pty-subprocess' -import { useDaemonPtySubprocessEnv } from './pty-subprocess-test-harness' - -describe('createPtySubprocess', () => { - useDaemonPtySubprocessEnv({ - spawnMock, - isPwshAvailableMock, - resolveUnixShellPathMock, - resolveAgentForegroundProcessMock, - validateWorkingDirectoryMock - }) - - it('does not spawn after cancellation wins during async cwd validation', async () => { - let releaseValidation: () => void = () => {} - const validationGate = new Promise((resolve) => { - releaseValidation = resolve - }) - validateWorkingDirectoryMock.mockImplementationOnce(() => validationGate) - let canceled = false - - const spawning = createPtySubprocess({ - sessionId: 'canceled-validation', - cols: 80, - rows: 24, - cwd: process.platform === 'win32' ? 'C:\\orca-cwd-fixture' : '/tmp/orca-cwd-fixture', - isCanceled: () => canceled - }) - await vi.waitFor(() => expect(validateWorkingDirectoryMock).toHaveBeenCalled()) - - canceled = true - releaseValidation() - - await expect(spawning).rejects.toThrow('Attach canceled for session canceled-validation') - expect(spawnMock).not.toHaveBeenCalled() - }) - - it('rejects daemon automatic agent startup at POSIX root', async () => { - const platform = Object.getOwnPropertyDescriptor(process, 'platform') - Object.defineProperty(process, 'platform', { value: 'linux' }) - spawnMock.mockClear() - - try { - await expect( - createPtySubprocess({ - sessionId: 'test', - cols: 80, - rows: 24, - cwd: '/', - command: 'claude' - }) - ).rejects.toThrow(/requires a non-root workspace/) - } finally { - if (platform) { - Object.defineProperty(process, 'platform', platform) - } - } - - expect(spawnMock).not.toHaveBeenCalled() - }) -}) From 1cd89abca413df238e627e42661c8dd894fe976f Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 22:11:20 -0800 Subject: [PATCH 022/178] test: remove five low-value runtime pipeline suites (#41844) * test: remove runtime projection suites with low-value fixtures * test: remove simulated mobile and worker observation pipelines --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 1 - config/reliability-gates.jsonc | 48 ++-- .../scripts/ci/sharding/ci-shard-timings.json | 3 - .../runtime/construction/orca-runtime.test.ts | 2 - ...creation-and-orchestration-part-02.spec.ts | 81 ------ ...erminal-output-and-worker-recovery.spec.ts | 70 ------ .../worker-interactive-wait.test.ts | 134 ---------- .../lifecycle/mobile-session-dead-pty.test.ts | 232 ------------------ ...g-worktree-terminal-reconciliation.test.ts | 93 ------- 9 files changed, 24 insertions(+), 640 deletions(-) delete mode 100644 src/main/runtime/orca-runtime-tests/mobile-creation-and-orchestration-part-02.spec.ts delete mode 100644 src/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts delete mode 100644 src/main/runtime/rpc/methods/orchestration/worker/observation/worker-interactive-wait.test.ts delete mode 100644 src/main/runtime/terminal/lifecycle/mobile-session-dead-pty.test.ts delete mode 100644 src/main/runtime/workspaces/removal/missing-worktree-terminal-reconciliation.test.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index ce2660684da..74d59db41c9 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -480,7 +480,6 @@ 1 test-no-wall-clock:src/main/runtime/rpc/methods/agent-launch/agent-launch-replay.test.ts 1 test-no-wall-clock:src/main/runtime/rpc/methods/orchestration/federation/federation-output.test.ts 2 test-no-wall-clock:src/main/runtime/rpc/methods/orchestration/worker/observation/fleet-status-observed-identity.test.ts -1 test-no-wall-clock:src/main/runtime/rpc/methods/orchestration/worker/observation/worker-interactive-wait.test.ts 1 test-no-wall-clock:src/main/runtime/rpc/methods/orchestration/worker/output/worker-output.test.ts 2 test-no-wall-clock:src/main/runtime/rpc/server/remote-runtime-request-connection.integration.test.ts 2 test-no-wall-clock:src/main/runtime/rpc/server/runtime-rpc-pairing-mode-persistence.test.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index a6727c1d6bb..51ec636be7a 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -3608,13 +3608,13 @@ "providers": ["local", "daemon", "ssh"], "coveredPlatforms": ["linux"], "coveredProviders": [], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. The whole main-window attach-services suite is removed: runtime/window attach, notifier wiring, reload/teardown and permission-handler mock orchestration alarms are intentionally lost. Prune its exact deleted command selectors, testFiles entries and notifier assertionRef where present; complete prior selectors/ref are archived. Unrelated dated receipts remain exact and retain their original scope; no fresh reduced-selector, native-window or remote execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2271: Linux coverage is limited to the unchanged serve-signal-exit-diagnostic supervisor selector and the late-handoff-after-child-exit assertion recorded below. It does not cover desktop promotion, renderer continuity, persistent PTYs or native updater behavior. No current local, daemon or SSH provider execution is established; coveredProviders is empty. Historical macOS/provider scope remains only in preserved prior accounts and archived receipts. Audit2252 removes the entire handassembled serve promotion/renderer hydration suite. Retired alarms: queued and repeated activation coalescing, duplicate serve suppression, unavailable-provider diagnostic, and zero resume/replacement accounting on independently supplied renderer state. The fixture never joined actual daemon-to-renderer delivery. These exact alarms have no equivalent replacement assigned. Remaining gate selectors keep their existing narrower scope; historical dated receipts remain verbatim and do not validate this changed selector set. No fresh runtime, native/provider, live remote/mobile, cross-platform or performance result is claimed. Archived removed active entries verbatim: commands: \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/serve-desktop-promotion-session-continuity.test.ts\"\ntestFiles: \"src/renderer/src/lib/serve-desktop-promotion-session-continuity.test.ts\"\nassertionRefs: {\n \"file\": \"src/renderer/src/lib/serve-desktop-promotion-session-continuity.test.ts\",\n \"assertions\": [\n \"a forced desktop relaunch reaching the headless lock owner opens exactly one window, and only after the persistent provider settles\",\n \"a duplicate `orca serve` launch never promotes the headless owner\",\n \"the renderer promoted inside the serve process resumes zero agents while daemon panes survive, before and after those panes rebind their PTYs\",\n \"no replacement resume tab, startup command, or automatic-resume claim is created, and every surviving session record is left untouched\"\n ]\n } Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2020: Retire CLI supervisor outcome, local-open promotion and paired-open projection alarms; launch retains only the two referenced stream-framing cases. Remaining CLI refusal/timeout cases do not replace headless-promotion coverage. Preserve both independent dated receipts at their original commands. Reduced selectors have no fresh result or equivalent replacement claim. Historical prior account (preserved verbatim): Audit1589 removes mock-only CLI forwarding, canned provisioning/catalog/error projections and private inventories without equivalent replacement coverage. Retained cases exercise actual local markdown/paste/reconciliation, stream framing/backpressure/output-drain, temporary directory cleanup and persisted handoff with injected host/provider/process boundaries. No real Jira, provisioning, remote launch, native pipe/process-tree authority or deployed provider catalog proof. Assertion references use the surviving handoff registration titles; changed-selector historical receipts are retired and unaffected receipts remain unchanged. Historical scope before this audit: Deterministic unit coverage exercises activation gating, single-instance ownership, local/remote CLI status, headless binding persistence, local daemon identity, SSH identity transfer, the promoted renderer's agent-resume accounting, and the macOS serve update handoff from staged installer through atomic bundle replacement and target-version readiness. Supervisor settlement coverage prevents a late handoff-completion failure from rearming termination after the replacement child exits. A macOS Electron journey covers headless promotion and persistent PTY identity. A disposable locally signed Electron canary exercised real ShipIt and a temporary LaunchAgent with the compiled production supervisor; full packaged Orca and Linux/Windows serve updates remain uncollected. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. The whole main-window attach-services suite is removed: runtime/window attach, notifier wiring, reload/teardown and permission-handler mock orchestration alarms are intentionally lost. Prune its exact deleted command selectors, testFiles entries and notifier assertionRef where present; complete prior selectors/ref are archived. Unrelated dated receipts remain exact and retain their original scope; no fresh reduced-selector, native-window or remote execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2271: Linux coverage is limited to the unchanged serve-signal-exit-diagnostic supervisor selector and the late-handoff-after-child-exit assertion recorded below. It does not cover desktop promotion, renderer continuity, persistent PTYs or native updater behavior. No current local, daemon or SSH provider execution is established; coveredProviders is empty. Historical macOS/provider scope remains only in preserved prior accounts and archived receipts. Audit2252 removes the entire handassembled serve promotion/renderer hydration suite. Retired alarms: queued and repeated activation coalescing, duplicate serve suppression, unavailable-provider diagnostic, and zero resume/replacement accounting on independently supplied renderer state. The fixture never joined actual daemon-to-renderer delivery. These exact alarms have no equivalent replacement assigned. Remaining gate selectors keep their existing narrower scope; historical dated receipts remain verbatim and do not validate this changed selector set. No fresh runtime, native/provider, live remote/mobile, cross-platform or performance result is claimed. Archived removed active entries verbatim: commands: \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/serve-desktop-promotion-session-continuity.test.ts\"\ntestFiles: \"src/renderer/src/lib/serve-desktop-promotion-session-continuity.test.ts\"\nassertionRefs: {\n \"file\": \"src/renderer/src/lib/serve-desktop-promotion-session-continuity.test.ts\",\n \"assertions\": [\n \"a forced desktop relaunch reaching the headless lock owner opens exactly one window, and only after the persistent provider settles\",\n \"a duplicate `orca serve` launch never promotes the headless owner\",\n \"the renderer promoted inside the serve process resumes zero agents while daemon panes survive, before and after those panes rebind their PTYs\",\n \"no replacement resume tab, startup command, or automatic-resume claim is created, and every surviving session record is left untouched\"\n ]\n } Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2020: Retire CLI supervisor outcome, local-open promotion and paired-open projection alarms; launch retains only the two referenced stream-framing cases. Remaining CLI refusal/timeout cases do not replace headless-promotion coverage. Preserve both independent dated receipts at their original commands. Reduced selectors have no fresh result or equivalent replacement claim. Historical prior account (preserved verbatim): Audit1589 removes mock-only CLI forwarding, canned provisioning/catalog/error projections and private inventories without equivalent replacement coverage. Retained cases exercise actual local markdown/paste/reconciliation, stream framing/backpressure/output-drain, temporary directory cleanup and persisted handoff with injected host/provider/process boundaries. No real Jira, provisioning, remote launch, native pipe/process-tree authority or deployed provider catalog proof. Assertion references use the surviving handoff registration titles; changed-selector historical receipts are retired and unaffected receipts remain unchanged. Historical scope before this audit: Deterministic unit coverage exercises activation gating, single-instance ownership, local/remote CLI status, headless binding persistence, local daemon identity, SSH identity transfer, the promoted renderer's agent-resume accounting, and the macOS serve update handoff from staged installer through atomic bundle replacement and target-version readiness. Supervisor settlement coverage prevents a late handoff-completion failure from rearming termination after the replacement child exits. A macOS Electron journey covers headless promotion and persistent PTY identity. A disposable locally signed Electron canary exercised real ShipIt and a temporary LaunchAgent with the compiled production supervisor; full packaged Orca and Linux/Windows serve updates remain uncollected. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/8457", "https://github.com/stablyai/orca/issues/9563" ], "invariant": "A safely promotable headless serve process is the single app owner. Desktop activation preserves its daemon-backed sessions. On macOS, a CLI-supervised serve update keeps the node-mode parent alive across ShipIt's atomic bundle swap, restarts with the original serve arguments only after the target bundle is present, and clears handoff state only after that target version reports runtime readiness. Once a supervised child exits or fails to start, no late handoff completion may signal it or arm force-kill escalation. Unsupported or failed handoffs leave the current serving owner intact or recover it once without an install retry loop.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. The whole main-window attach-services suite is removed: runtime/window attach, notifier wiring, reload/teardown and permission-handler mock orchestration alarms are intentionally lost. Prune its exact deleted command selectors, testFiles entries and notifier assertionRef where present; complete prior selectors/ref are archived. Unrelated dated receipts remain exact and retain their original scope; no fresh reduced-selector, native-window or remote execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2252: The removed synthetic lock/gate/hydration fixture supplies no current promotion continuity oracle. Existing other selectors retain only their own independent scope; changed selector execution is pending. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2020: Retire CLI supervisor outcome, local-open promotion and paired-open projection alarms; launch retains only the two referenced stream-framing cases. Remaining CLI refusal/timeout cases do not replace headless-promotion coverage. Preserve both independent dated receipts at their original commands. Reduced selectors have no fresh result or equivalent replacement claim. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Unit tests coalesce early activation, preserve daemon and SSH identity, and reproduce the update race with a staged target, old serving child, persistent CLI parent, atomic .app replacement, and replacement readiness message. They assert the parent does not exit for launchd to respawn the old app, the native updater does not launch an interactive GUI, the replacement version is verified before handoff completion, mismatches become durable failures without retries, and unsupported/preflight-failed installs do not invoke native quit or PTY cleanup. Force handoff completion to fail after the replacement child exits and require zero later child signals and zero escalation timers. A joined lock-owner/activation/hydration contract asserts that a forced relaunch opens exactly one window and that the renderer promoted inside the serve process launches zero agent resumes, creates no replacement tab or startup command, and leaves every surviving session record untouched. The Electron journey independently verifies headless promotion retains owner/runtime/daemon/PTY identity and terminal I/O.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. The whole main-window attach-services suite is removed: runtime/window attach, notifier wiring, reload/teardown and permission-handler mock orchestration alarms are intentionally lost. Prune its exact deleted command selectors, testFiles entries and notifier assertionRef where present; complete prior selectors/ref are archived. Unrelated dated receipts remain exact and retain their original scope; no fresh reduced-selector, native-window or remote execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2252: The removed synthetic lock/gate/hydration fixture supplies no current promotion continuity oracle. Existing other selectors retain only their own independent scope; changed selector execution is pending. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2020: Retire CLI supervisor outcome, local-open promotion and paired-open projection alarms; launch retains only the two referenced stream-framing cases. Remaining CLI refusal/timeout cases do not replace headless-promotion coverage. Preserve both independent dated receipts at their original commands. Reduced selectors have no fresh result or equivalent replacement claim. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Unit tests coalesce early activation, preserve daemon and SSH identity, and reproduce the update race with a staged target, old serving child, persistent CLI parent, atomic .app replacement, and replacement readiness message. They assert the parent does not exit for launchd to respawn the old app, the native updater does not launch an interactive GUI, the replacement version is verified before handoff completion, mismatches become durable failures without retries, and unsupported/preflight-failed installs do not invoke native quit or PTY cleanup. Force handoff completion to fail after the replacement child exits and require zero later child signals and zero escalation timers. A joined lock-owner/activation/hydration contract asserts that a forced relaunch opens exactly one window and that the renderer promoted inside the serve process launches zero agent resumes, creates no replacement tab or startup command, and leaves every surviving session record untouched. The Electron journey independently verifies headless promotion retains owner/runtime/daemon/PTY identity and terminal I/O.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/cli/runtime/launch.test.ts src/main/updater/serve-update-handoff.test.ts src/main/updater/updater.headless-serve-install.test.ts src/main/updater/mac-install/updater.mac-install.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/cli/runtime/serve-signal-exit-diagnostic.test.ts", @@ -3767,14 +3767,14 @@ "providers": ["local", "daemon", "ssh", "paired-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": ["local", "daemon", "paired-runtime"], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. The whole main-window attach-services suite is removed: runtime/window attach, notifier wiring, reload/teardown and permission-handler mock orchestration alarms are intentionally lost. Prune its exact deleted command selectors, testFiles entries and notifier assertionRef where present; complete prior selectors/ref are archived. Unrelated dated receipts remain exact and retain their original scope; no fresh reduced-selector, native-window or remote execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Deterministic lifecycle, IPC, window-notification, and runtime ownership tests cover generation replacement, stale callbacks, same-frame old-document publication, blocked external navigation, cancelled renderer navigation, concurrent provisional starts, process loss, timeout cleanup, headless fallback, and competing windows. Isolated macOS Electron validation covers cancelled navigation, forced renderer recovery, and headless serve promotion with live daemon-backed PTY identity and I/O. Linux, Windows, WSL, SSH promotion, and mixed-version desktop/server live runs remain uncollected; the renderer generation stays local to Electron IPC and does not change the remote wire. The renderer scheduling fixture now retains in-flight coalescing only, and payload partitioning retains publication-failure and main-dropped-worktree retry. Immediate publication, stale SSH cleanup, reconnect resync and isolated partition membership alarms were removed; other lifecycle evidence does not establish equivalent coverage. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1400: src/renderer/src/runtime/sync-runtime-graph-scheduling.test.ts removed. Removes whole coalescing call-count suite; no newest-state delivery or replacement claim. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.\nWindow folder cleanup retires the mock-call-only navigation fence suite and the per-generation warning-count case. Retained sender results and existing rendered checks keep their own original scope; no equivalent replacement or fresh execution is claimed here.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. The whole main-window attach-services suite is removed: runtime/window attach, notifier wiring, reload/teardown and permission-handler mock orchestration alarms are intentionally lost. Prune its exact deleted command selectors, testFiles entries and notifier assertionRef where present; complete prior selectors/ref are archived. Unrelated dated receipts remain exact and retain their original scope; no fresh reduced-selector, native-window or remote execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Deterministic lifecycle, IPC, window-notification, and runtime ownership tests cover generation replacement, stale callbacks, same-frame old-document publication, blocked external navigation, cancelled renderer navigation, concurrent provisional starts, process loss, timeout cleanup, headless fallback, and competing windows. Isolated macOS Electron validation covers cancelled navigation, forced renderer recovery, and headless serve promotion with live daemon-backed PTY identity and I/O. Linux, Windows, WSL, SSH promotion, and mixed-version desktop/server live runs remain uncollected; the renderer generation stays local to Electron IPC and does not change the remote wire. The renderer scheduling fixture now retains in-flight coalescing only, and payload partitioning retains publication-failure and main-dropped-worktree retry. Immediate publication, stale SSH cleanup, reconnect resync and isolated partition membership alarms were removed; other lifecycle evidence does not establish equivalent coverage. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1400: src/renderer/src/runtime/sync-runtime-graph-scheduling.test.ts removed. Removes whole coalescing call-count suite; no newest-state delivery or replacement claim. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.\nWindow folder cleanup retires the mock-call-only navigation fence suite and the per-generation warning-count case. Retained sender results and existing rendered checks keep their own original scope; no equivalent replacement or fresh execution is claimed here.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/14066", "https://linear.app/stably/issue/STA-4016", "https://github.com/stablyai/orca/pull/14070" ], "invariant": "Every genuine renderer graph reload revision settles exactly once, while a blocked external navigation that preserves the current document never starts a revision. A cancelled renderer-document navigation restores authority only when that same renderer survived; cancellation after an earlier document committed remains fenced until publication or timeout. Replacing a reload cancels only the prior revision; its timer and callbacks cannot settle a newer revision. A desktop reload succeeds only from a publication carrying a new renderer-document generation, while a failed headless promotion may recover only from its originating window. Timeout, renderer loss, and close reach a terminal graph state, and successful desktop publication permanently retires headless sentinel fallback until a new headless runtime is established.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. The whole main-window attach-services suite is removed: runtime/window attach, notifier wiring, reload/teardown and permission-handler mock orchestration alarms are intentionally lost. Prune its exact deleted command selectors, testFiles entries and notifier assertionRef where present; complete prior selectors/ref are archived. Unrelated dated receipts remain exact and retain their original scope; no fresh reduced-selector, native-window or remote execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Start a blocked external main-frame navigation and require the current renderer graph and notification path to remain authoritative. Start a same-renderer navigation, cancel it before commit, and require the renderer canary and ready authority to survive; coalesce overlapping provisional starts into one fence, and keep an earlier committed reload fenced if a later navigation is cancelled. Drive two genuine reloads half a timeout apart and require the first deadline to leave the second revision reloading while the second deadline alone makes it unavailable. During the second revision publish from the prior renderer document through the still-current main frame and require rejection without graph mutation or timeout cancellation. Restore headless authority after failed promotion, publish from a competing window, and require rejection while the sentinel remains authoritative. Then force a real renderer crash and separately promote an isolated headless serve owner; require runtime graph readiness, stable runtime/main/daemon/PTY identities, recovered visible terminal output, and post-recovery input/output.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. The whole main-window attach-services suite is removed: runtime/window attach, notifier wiring, reload/teardown and permission-handler mock orchestration alarms are intentionally lost. Prune its exact deleted command selectors, testFiles entries and notifier assertionRef where present; complete prior selectors/ref are archived. Unrelated dated receipts remain exact and retain their original scope; no fresh reduced-selector, native-window or remote execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Start a blocked external main-frame navigation and require the current renderer graph and notification path to remain authoritative. Start a same-renderer navigation, cancel it before commit, and require the renderer canary and ready authority to survive; coalesce overlapping provisional starts into one fence, and keep an earlier committed reload fenced if a later navigation is cancelled. Drive two genuine reloads half a timeout apart and require the first deadline to leave the second revision reloading while the second deadline alone makes it unavailable. During the second revision publish from the prior renderer document through the still-current main frame and require rejection without graph mutation or timeout cancellation. Restore headless authority after failed promotion, publish from a competing window, and require rejection while the sentinel remains authoritative. Then force a real renderer crash and separately promote an isolated headless serve owner; require runtime graph readiness, stable runtime/main/daemon/PTY identities, recovered visible terminal output, and post-recovery input/output.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/session-graph/runtime-graph-reload-lifecycle.test.ts src/main/window/renderer-relays/runtime-renderer-notification-sender.test.ts src/main/ipc/runtime.test.ts src/main/runtime/construction/orca-runtime.test.ts src/renderer/src/runtime/sync-runtime-graph/sync-runtime-graph-payload-partition.test.ts", "pnpm exec electron-vite build --mode e2e", @@ -4308,7 +4308,7 @@ "providers": ["remote-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": [], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. The complete mocked session-close routing suite is removed, including the explicit cross-worktree cleanup alarm. Remove its active file, command token and assertion reference; historical selectors and receipts retain their original scope. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3342 retires the complete mobile editor/diff tab-publication projection suite: editor/diff serialization, workspace-document privacy withholding, pending/focus and capability/options projections are intentionally lost without equivalent replacement. The surviving focused selectors are unexecuted in this source composition. Six complete compound macOS receipts whose command fields select the changed source are retired verbatim as history; their other selected tests receive no promoted evidence. Other dated receipts keep their original scope; no fresh runtime/UI/mobile/mixed-version or cross-platform validation is claimed. Audit3335 retires the complete three-case browser-tabs snapshot assembly suite: canonical snapshot replay/placement/focus, local host-page adoption and unstamped host echo leaving a local page untouched alarms are intentionally lost without equivalent replacement. The surviving focused selector is unexecuted in this source composition. Unrelated dated receipts keep their original scope; no fresh runtime/network, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Audit 3301 removes the caller-chat-target precedence over host selection example from web-session-tabs-sync-focus-intent.test.ts. The snapshot-before-RPC replay and split-sibling-first exact-leaf focus cases remain unchanged and match the existing assertion reference. Preserve current selectors, assertion references, unrelated active receipts and complete archived history. These mocked state-composition survivors provide no equivalent replacement, rendered, native, network or fresh execution proof. Audit3108: doc-preview-grants.test.ts now retains only the stale grant-rejection race: a late rejected grant request must not evict a newer grant registry entry, witnessed by later revocation of grant-2. Retire stable minted-location and outside-workspace POSIX/Windows request-projection alarms without replacement. The direct nonempty grant selector remains unchanged; no real grant-server/file authorization, SSH/HTML delivery or native-focus result is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2043: Remove the registration-wait, webview-security, preview-authorization, guest-lifecycle and grant-IPC unit suites and the sibling-directory assertion reference. Runtime and browser capability consumers also lose removed imported examples. Remaining document-policy, file-reader and grant-registry assertions do not replace those alarms. Preserve the 27 independent dated receipts at their original unchanged selectors; reduced prospective unit selectors have no fresh result or equivalent replacement claim. Historical prior account (preserved verbatim): Audit1973: Delete the browser RPC forwarding/schema/scalar suite and its prospective selector. Preserve all27 unrelated dated receipts; no actual browser operation alarm is replaced. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): The complete terminal-file-link-actions suite is removed. Lose supplied local/remote menu-row inventories, browser label/order/download/reveal callback forwarding, platform-copy and source-group pinning examples. No OS reveal/download, real remote preview or browser navigation was exercised; the other real document-preview sources are not equivalent replacements. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1899 replays sealed Audit1895: workspace-doc-history.test.ts is entirely removed from its current command and testFiles. Supplied recency/dedupe/cap/title/identity examples and the10000-row getter-read budget alarm are intentionally lost. No current assertionRef or active receipt selects that file. Unrelated active receipts and all prior gap literals remain verbatim; no replacement history/index-work coverage or new browser/paired-preview gate execution is claimed. Historical coverage account before Audit1899: Audit1853 reduces LinkActionPopover.test.tsx and archives its complete 2026-09-06 mixed-selector receipt. The lost copy/open-state and Settings callback-order/omission alarms are accepted without equivalent replacement coverage. No fresh execution of this gate is credited. Audit1709 removes low-value test definitions and retires affected assertion or execution-receipt claims without equivalent replacement coverage. Per-source reviews bound retained behavior and injected/native/remote limits. Unchanged assertions and receipts keep their original scope; gates lacking current active evidence are downgraded. Historical scope before this audit: Audit1687 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1630 removes low-value Git, GitHub/GitLab and IPC projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1571 removes mock forwarding, capability/schema inventories, private predicates, supplied error/result replays, and filesystem-mocked persistence checks without equivalent replacement claims. Retained changed tests also exercise real local permission-file reread, registry proxy rollback/credential retirement, site display recovery, and pending sender/credential/document admission with injected Electron/UI producers. Retained network tests exercise owned stream credit/cleanup, generation and retirement fences, real loopback proxy/SOCKS bytes and file-backed binding state. SSH/WSL/paired runtime, native sessions, authority producers and storage clears remain injected; simulated relaunch uses filesystem mocks. The unchanged profile-retirement suite was statically reviewed and not rerun. No actual SSH/WSL runtime, native Chromium jar deletion, durable process restart, remote DNS or external site proof. Receipts selecting changed tests are retired and unaffected receipts remain unchanged. Historical scope before this audit: Audit1568 removes API inventories, supplied mock-value/forwarding assertions, implementation-shape tables and negative/setup cases that do not reach their intended boundary, without equivalent replacement claims. Retained changed definitions exercise owned manager lifetime/queue/refcount/clamp and real synthetic process/files/staging boundaries. Seventeen unchanged native Electron definitions across this combined audit are statically reviewed and not rerun. Changed native suites exercise local popup wire/cookie/session and clean/native process-identity behavior; known popup-admission flaws remain unfixed. Unit native/event/session/registry inputs remain injected. No actual OS focus, registry/host execution, CRX verification or durable restart proof. Receipts selecting changed tests are retired and unaffected receipts remain unchanged. Historical scope before this audit: Audit1564 retires copied cookie key recipes, mock bridge/preview forwarding and flag tables, source-spelling/script inventories and setup-only alarms without equivalent replacement coverage. Retained changed cases exercise actual scoped SQLite merge/rollback/schema refusal, owned cancellation and grant authority/canonical containment, ranged response consistency, replacement guest ownership and executed guest-script promise/DOM cleanup behavior. Session, host response, geometry/observer and transport producers remain injected where documented. One unchanged native extension definition is not rerun locally; no actual remote filesystem, native layout/focus or authentication claim. Receipts selecting modified tests are retired; unaffected exact-selector receipts remain unchanged. Historical scope before this audit: Audit1412 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audit1386 changes referenced sources; no new execution result validates reduced selectors. Retained unchanged receipts apply only to unchanged commands. Historical coverage: Audit1308 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): A macOS desktop host and a separate paired Electron client exercise the Explorer preview action end to end: the document renders inside the client-local orca-preview guest, the client gains exactly one browser workspace and it is the document one, the host's own page registry and tab snapshot gain nothing, the split does not take focus from the source editor, an explicit click activates the preview, and closing it with its X leaves the editor and terminal intact. Both host oracles are read in a run where an ordinary URL browser tab from the same client does arrive in them, because a host that cannot project browser pages at all would otherwise answer the absence question the same way. A second journey quits that client with a document tab open and relaunches it on the same profile, which is the restore the user's original report turned on: the same workspace row comes back blank and named by the document, renders again, and does so over a grant URL that differs from the one that was quit. The same journey pins the guest URL to the orca-preview scheme, so a regression back to file:// or to a host browser page is visible. The rewritten journey caught a live gap the old one could not see: since previews became browser pages they had no chrome to hand focus to their guest, so main refused every reported link click as coming from an unfocused guest and the one route out of a preview was dead. Main's own log answered it — registered true, grant live, focused false — after the embedder reported the anchor really taking six real presses. The pane now hands focus over when it is the reader's surface, and the journey routes. The preview's own components moved out of the editor folder into browser-pane/workspace-doc/ with this change, since a preview is a browser pane and no longer an editor one; their i18n keys keep the historical auto.components.editor.HtmlDocPreview prefix rather than dropping thirty translated strings across four locales for a tidier key path. Seven gate commands named those files by their old paths and were re-issued at the new ones, and the nine dated runs behind them were retired rather than left naming files that are no longer there, on the same principle this gate already applied twice: vitest treats a missing named file as a filter and exits 0, so such a command reports green while silently running only the half that still exists. Each retired record's finding is carried into the replacement run recorded below it, including the red half of the entry/subresource pair, which was re-measured by re-applying its mutant at the new path rather than being dropped. The fence over the page-management channels was decided channel by channel before it was built, and that decision record is: of the 23 channels that can name a page, 6 are allowed for a workspace-document page — the tool channels the reader drives, which reach it through the one authority that reads both halves of the registry — 9 refuse (download accept and cancel, devtools, certificate proceed, client page metadata publish, viewport override, WebAuthn account response, registration probe, registration repair), 3 cannot name a page at all because they take a profile or a worktree instead, and the rest are the registration and activation doors, refused on membership. What shipped is not the per-channel guard that classification implies: eleven call sites read the browsing map directly, so a guard per channel is exactly the caller-flag seam that has to be remembered at every future call site. The registry was split in two instead, which makes the classification structural — a document guest is absent from the browsing map, so every refusing channel refuses by absence rather than by a guard, and a channel added later inherits the refusal without being classified at all. Unit contracts cover grant scoping and path containment, per-owner read routing, refusal of a truncated or over-cap read, the old-server binary degradation, scheme admission on will-attach-webview, the guest navigation policy, the plan layer's local/SSH/paired/out-of-worktree routing, and the distinct sentence the reader sees for each failure reason. The paired browser-creation surfaces this gate still owns — capability rejection cleanup, canonical page identity, reconciliation rollback, close convergence — keep their existing headed and headless journeys. Linux and Windows live journeys remain gaps. A refused subresource is a notice on a rendered document rather than a failure panel, since only the entry document's failure leaves the reader with nothing; the entry document is served as text by every owner and cannot be refused for its format at all. Grants die with the renderer document that minted them, at window creation and on every reload, because the renderer holds the only record of which preview owns which grant. The mint and revoke channels answer only the trusted renderer, and a preview tab is never republished to mobile clients, which have no grant to read it with. External URLs leave the preview only via a trusted user click on an anchor while the preview is focused; script-driven navigation and popups reach nothing. The guest's navigation sinks are deny-only and route no URL at all, and the guest carries a preview-only preload, pinned by will-attach-webview and denied to every other attach path, whose capture-phase click handler reports an anchor's URL to main; main answers only a report from a preview guest with a live bound grant that is the focused contents, and only for http(s). Two earlier reader-facing records from 2026-08-26, which measured a shape where a subresource failure was filtered out and the panel still carried an unsupported-asset sentence, were retired with that shape rather than left contradicting the 10/10 runs of the command they share. Two more were retired on the same principle when the external-link design changed: a record showing that removing a recent-input gate let the unattended document open two browser tabs, and a record showing that dropping the webview's allowpopups attribute left a target=_blank press with nothing to route. Both measured machinery that no longer exists — the input gate could not tell a script's navigation from the click it followed, which is the case now recorded below, and the preload intercepts the press before Chromium considers a popup at all, so allowpopups was removed. WebRTC is fenced by the guest's IP-handling policy alone: this Chromium answers a webrtc content-security-policy directive as unrecognized and gathers candidates anyway, so the directive is deliberately absent from the served policy and a unit assertion keeps it out. A standalone Electron probe on the same version gathered 7 candidates including a STUN reflexive one without the policy and 0 with it. Two command paths this gate used to carry no longer exist, and their structured evidence was retired with them rather than left naming files that are no longer there: vitest treats a missing named file as a filter and exits 0, so such a command reports green while silently running only the half that still exists. The first is src/renderer/src/lib/file-preview-capability-cleanup.test.ts, deleted in STA-5557 when HTML previews stopped creating paired browser tabs: on 2026-08-12 its five-assertion STA-4063 oracle ran red 5/5 on scan e4c278eb52 and on main bc2e30000b, partially green 2/5 on pre-fix PR #14100, and green 5/5 on candidate 14b5877a75; the surviving half of that oracle is web-runtime-browser-capability-cleanup.test.ts, still commanded above. The second is tests/e2e/paired-remote-html-browser-focus.spec.ts, renamed in STA-5557 to paired-remote-html-preview-local-render.spec.ts because the journey no longer proves anything about browser focus: its 2026-08-12 runs established the owning-runtime routing and no-focus-steal behavior of the then-current browser-tab preview (red on main 346e59c879 and a90a18d43d, green on candidates d05217c40d and b7de9c0961, red again with only the side-preview focus or routing seam disabled), and its 2026-08-26 runs are recorded below under the current path. An adversarial pass after the journey landed found four coverage gaps rather than shipping defects, and all four are pinned here. The page url had a third door: the navigation commit wrote whatever url was reported, unfenced, and while nothing reaches it for a document page today it sat one report away from giving a document page a URL that persistence, the publish boundary and the address bar all read. The group projection was the load-bearing half of the mobile publish boundary that no test had: removing it alone left the whole suite green, because no test in it had tab groups, while the phone payload gained a group whose order and active tab named a document tab it was never sent. The focus handoff was covered only in the narrowing direction, so widening it — dropping the page-activity check, or either half of the reader-surface check — survived both suites. And the handoff itself was offered once and never again, so another app taking the front left the preview's one route out shut until the pane remounted, which is a desktop gap rather than a test-only one. The standalone capability-cleanup and browser-placement ledger suites were removed, including their mixed-version rejection, cleanup transfer, capacity and partition alarms. web-runtime-session.test.ts no longer supplies browser creation, rollback or recovery assertions. The grouped workspace-document exclusion remains; the separate no-group publication example was removed. Existing live/browser-store evidence does not establish equivalent replacement alarms. Earlier descriptions of the removed fixtures and their run results are historical. The client-owned-placement and layout-groups suites are removed. The broad mirror fixture no longer supplies preview placement/reservation/move assertions. Browser rows retain pre-response replay and real local-workspace reuse; focus intent retains response ordering and exact-leaf sibling ordering. Supplied browser-layout and placement/focus projection alarms are lost; retained evidence is narrower and not equivalent. The test-value audit retires renderer grant-release mock ledgers, direct creation/title/session-field copies, notification/onboarding calls and several placement/per-page projections. Canonical-ID rejection, document reopen/restore and preview URL fences remain; they do not prove close-to-registry revocation or replace every lost alarm. Historical execution evidence is unchanged. The test-value audit removed the direct legacy editor-preview hydration example from tabs-hydration.test.ts. Dropping legacy preview chrome while preserving the same-document ordinary editor no longer has that alarm. The surviving corrupt-id deduplication case does not cover that migration, and retained preview journeys are not equivalent replacement coverage. Historical run counts and migration mutation findings describe the earlier suite. Audit1308: src/renderer/src/components/editor/EditorPanelShell.header.test.tsx removed. Header visibility and mocked editor-surface inventory alarms disappear. No equivalent replacement coverage is claimed. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1386: src/renderer/src/lib/doc-preview-grants.test.ts reduced. Repeated-mount mint count, ordinary release/remint and failed-mint cache retry standalone alarms removed; no actual grant server/access test. No equivalent replacement coverage is claimed. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1412: src/main/browser/guest-lifecycle/manager/browser-manager-guest-policy-profile.test.ts removed. Loses workspace-document in-grant navigation/popup refusal, cleanup/re-attach, cross-registry guest authorization and browsing-map separation alarms. No equivalent browser policy coverage is claimed. No equivalent replacement coverage is claimed. Audit1421 removes browser-address-bar-suggestions.test.ts. Document suggestion title/path matching, recency merging and path-prefix ranking alarms are lost. No equivalent replacement coverage is claimed. Historical receipts remain scoped to unchanged selectors; the reduced command has no new execution result. Audit2452 removes the whole fake-effect browser-close routing suite. Active selectors and assertion references no longer select it. Its requested-worktree default close, sibling-page close and explicit missing-page typed reply alarms are intentionally retired without equivalent replacement. Prior dated receipts remain unchanged and scoped to their original sources; no fresh execution of the reduced gate, rendered browser, live remote, mobile or cross-platform validation is claimed.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. The complete mocked session-close routing suite is removed, including the explicit cross-worktree cleanup alarm. Remove its active file, command token and assertion reference; historical selectors and receipts retain their original scope. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3342 retires the complete mobile editor/diff tab-publication projection suite: editor/diff serialization, workspace-document privacy withholding, pending/focus and capability/options projections are intentionally lost without equivalent replacement. The surviving focused selectors are unexecuted in this source composition. Six complete compound macOS receipts whose command fields select the changed source are retired verbatim as history; their other selected tests receive no promoted evidence. Other dated receipts keep their original scope; no fresh runtime/UI/mobile/mixed-version or cross-platform validation is claimed. Audit3335 retires the complete three-case browser-tabs snapshot assembly suite: canonical snapshot replay/placement/focus, local host-page adoption and unstamped host echo leaving a local page untouched alarms are intentionally lost without equivalent replacement. The surviving focused selector is unexecuted in this source composition. Unrelated dated receipts keep their original scope; no fresh runtime/network, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Audit 3301 removes the caller-chat-target precedence over host selection example from web-session-tabs-sync-focus-intent.test.ts. The snapshot-before-RPC replay and split-sibling-first exact-leaf focus cases remain unchanged and match the existing assertion reference. Preserve current selectors, assertion references, unrelated active receipts and complete archived history. These mocked state-composition survivors provide no equivalent replacement, rendered, native, network or fresh execution proof. Audit3108: doc-preview-grants.test.ts now retains only the stale grant-rejection race: a late rejected grant request must not evict a newer grant registry entry, witnessed by later revocation of grant-2. Retire stable minted-location and outside-workspace POSIX/Windows request-projection alarms without replacement. The direct nonempty grant selector remains unchanged; no real grant-server/file authorization, SSH/HTML delivery or native-focus result is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2043: Remove the registration-wait, webview-security, preview-authorization, guest-lifecycle and grant-IPC unit suites and the sibling-directory assertion reference. Runtime and browser capability consumers also lose removed imported examples. Remaining document-policy, file-reader and grant-registry assertions do not replace those alarms. Preserve the 27 independent dated receipts at their original unchanged selectors; reduced prospective unit selectors have no fresh result or equivalent replacement claim. Historical prior account (preserved verbatim): Audit1973: Delete the browser RPC forwarding/schema/scalar suite and its prospective selector. Preserve all27 unrelated dated receipts; no actual browser operation alarm is replaced. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): The complete terminal-file-link-actions suite is removed. Lose supplied local/remote menu-row inventories, browser label/order/download/reveal callback forwarding, platform-copy and source-group pinning examples. No OS reveal/download, real remote preview or browser navigation was exercised; the other real document-preview sources are not equivalent replacements. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1899 replays sealed Audit1895: workspace-doc-history.test.ts is entirely removed from its current command and testFiles. Supplied recency/dedupe/cap/title/identity examples and the10000-row getter-read budget alarm are intentionally lost. No current assertionRef or active receipt selects that file. Unrelated active receipts and all prior gap literals remain verbatim; no replacement history/index-work coverage or new browser/paired-preview gate execution is claimed. Historical coverage account before Audit1899: Audit1853 reduces LinkActionPopover.test.tsx and archives its complete 2026-09-06 mixed-selector receipt. The lost copy/open-state and Settings callback-order/omission alarms are accepted without equivalent replacement coverage. No fresh execution of this gate is credited. Audit1709 removes low-value test definitions and retires affected assertion or execution-receipt claims without equivalent replacement coverage. Per-source reviews bound retained behavior and injected/native/remote limits. Unchanged assertions and receipts keep their original scope; gates lacking current active evidence are downgraded. Historical scope before this audit: Audit1687 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1630 removes low-value Git, GitHub/GitLab and IPC projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1571 removes mock forwarding, capability/schema inventories, private predicates, supplied error/result replays, and filesystem-mocked persistence checks without equivalent replacement claims. Retained changed tests also exercise real local permission-file reread, registry proxy rollback/credential retirement, site display recovery, and pending sender/credential/document admission with injected Electron/UI producers. Retained network tests exercise owned stream credit/cleanup, generation and retirement fences, real loopback proxy/SOCKS bytes and file-backed binding state. SSH/WSL/paired runtime, native sessions, authority producers and storage clears remain injected; simulated relaunch uses filesystem mocks. The unchanged profile-retirement suite was statically reviewed and not rerun. No actual SSH/WSL runtime, native Chromium jar deletion, durable process restart, remote DNS or external site proof. Receipts selecting changed tests are retired and unaffected receipts remain unchanged. Historical scope before this audit: Audit1568 removes API inventories, supplied mock-value/forwarding assertions, implementation-shape tables and negative/setup cases that do not reach their intended boundary, without equivalent replacement claims. Retained changed definitions exercise owned manager lifetime/queue/refcount/clamp and real synthetic process/files/staging boundaries. Seventeen unchanged native Electron definitions across this combined audit are statically reviewed and not rerun. Changed native suites exercise local popup wire/cookie/session and clean/native process-identity behavior; known popup-admission flaws remain unfixed. Unit native/event/session/registry inputs remain injected. No actual OS focus, registry/host execution, CRX verification or durable restart proof. Receipts selecting changed tests are retired and unaffected receipts remain unchanged. Historical scope before this audit: Audit1564 retires copied cookie key recipes, mock bridge/preview forwarding and flag tables, source-spelling/script inventories and setup-only alarms without equivalent replacement coverage. Retained changed cases exercise actual scoped SQLite merge/rollback/schema refusal, owned cancellation and grant authority/canonical containment, ranged response consistency, replacement guest ownership and executed guest-script promise/DOM cleanup behavior. Session, host response, geometry/observer and transport producers remain injected where documented. One unchanged native extension definition is not rerun locally; no actual remote filesystem, native layout/focus or authentication claim. Receipts selecting modified tests are retired; unaffected exact-selector receipts remain unchanged. Historical scope before this audit: Audit1412 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audit1386 changes referenced sources; no new execution result validates reduced selectors. Retained unchanged receipts apply only to unchanged commands. Historical coverage: Audit1308 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): A macOS desktop host and a separate paired Electron client exercise the Explorer preview action end to end: the document renders inside the client-local orca-preview guest, the client gains exactly one browser workspace and it is the document one, the host's own page registry and tab snapshot gain nothing, the split does not take focus from the source editor, an explicit click activates the preview, and closing it with its X leaves the editor and terminal intact. Both host oracles are read in a run where an ordinary URL browser tab from the same client does arrive in them, because a host that cannot project browser pages at all would otherwise answer the absence question the same way. A second journey quits that client with a document tab open and relaunches it on the same profile, which is the restore the user's original report turned on: the same workspace row comes back blank and named by the document, renders again, and does so over a grant URL that differs from the one that was quit. The same journey pins the guest URL to the orca-preview scheme, so a regression back to file:// or to a host browser page is visible. The rewritten journey caught a live gap the old one could not see: since previews became browser pages they had no chrome to hand focus to their guest, so main refused every reported link click as coming from an unfocused guest and the one route out of a preview was dead. Main's own log answered it — registered true, grant live, focused false — after the embedder reported the anchor really taking six real presses. The pane now hands focus over when it is the reader's surface, and the journey routes. The preview's own components moved out of the editor folder into browser-pane/workspace-doc/ with this change, since a preview is a browser pane and no longer an editor one; their i18n keys keep the historical auto.components.editor.HtmlDocPreview prefix rather than dropping thirty translated strings across four locales for a tidier key path. Seven gate commands named those files by their old paths and were re-issued at the new ones, and the nine dated runs behind them were retired rather than left naming files that are no longer there, on the same principle this gate already applied twice: vitest treats a missing named file as a filter and exits 0, so such a command reports green while silently running only the half that still exists. Each retired record's finding is carried into the replacement run recorded below it, including the red half of the entry/subresource pair, which was re-measured by re-applying its mutant at the new path rather than being dropped. The fence over the page-management channels was decided channel by channel before it was built, and that decision record is: of the 23 channels that can name a page, 6 are allowed for a workspace-document page — the tool channels the reader drives, which reach it through the one authority that reads both halves of the registry — 9 refuse (download accept and cancel, devtools, certificate proceed, client page metadata publish, viewport override, WebAuthn account response, registration probe, registration repair), 3 cannot name a page at all because they take a profile or a worktree instead, and the rest are the registration and activation doors, refused on membership. What shipped is not the per-channel guard that classification implies: eleven call sites read the browsing map directly, so a guard per channel is exactly the caller-flag seam that has to be remembered at every future call site. The registry was split in two instead, which makes the classification structural — a document guest is absent from the browsing map, so every refusing channel refuses by absence rather than by a guard, and a channel added later inherits the refusal without being classified at all. Unit contracts cover grant scoping and path containment, per-owner read routing, refusal of a truncated or over-cap read, the old-server binary degradation, scheme admission on will-attach-webview, the guest navigation policy, the plan layer's local/SSH/paired/out-of-worktree routing, and the distinct sentence the reader sees for each failure reason. The paired browser-creation surfaces this gate still owns — capability rejection cleanup, canonical page identity, reconciliation rollback, close convergence — keep their existing headed and headless journeys. Linux and Windows live journeys remain gaps. A refused subresource is a notice on a rendered document rather than a failure panel, since only the entry document's failure leaves the reader with nothing; the entry document is served as text by every owner and cannot be refused for its format at all. Grants die with the renderer document that minted them, at window creation and on every reload, because the renderer holds the only record of which preview owns which grant. The mint and revoke channels answer only the trusted renderer, and a preview tab is never republished to mobile clients, which have no grant to read it with. External URLs leave the preview only via a trusted user click on an anchor while the preview is focused; script-driven navigation and popups reach nothing. The guest's navigation sinks are deny-only and route no URL at all, and the guest carries a preview-only preload, pinned by will-attach-webview and denied to every other attach path, whose capture-phase click handler reports an anchor's URL to main; main answers only a report from a preview guest with a live bound grant that is the focused contents, and only for http(s). Two earlier reader-facing records from 2026-08-26, which measured a shape where a subresource failure was filtered out and the panel still carried an unsupported-asset sentence, were retired with that shape rather than left contradicting the 10/10 runs of the command they share. Two more were retired on the same principle when the external-link design changed: a record showing that removing a recent-input gate let the unattended document open two browser tabs, and a record showing that dropping the webview's allowpopups attribute left a target=_blank press with nothing to route. Both measured machinery that no longer exists — the input gate could not tell a script's navigation from the click it followed, which is the case now recorded below, and the preload intercepts the press before Chromium considers a popup at all, so allowpopups was removed. WebRTC is fenced by the guest's IP-handling policy alone: this Chromium answers a webrtc content-security-policy directive as unrecognized and gathers candidates anyway, so the directive is deliberately absent from the served policy and a unit assertion keeps it out. A standalone Electron probe on the same version gathered 7 candidates including a STUN reflexive one without the policy and 0 with it. Two command paths this gate used to carry no longer exist, and their structured evidence was retired with them rather than left naming files that are no longer there: vitest treats a missing named file as a filter and exits 0, so such a command reports green while silently running only the half that still exists. The first is src/renderer/src/lib/file-preview-capability-cleanup.test.ts, deleted in STA-5557 when HTML previews stopped creating paired browser tabs: on 2026-08-12 its five-assertion STA-4063 oracle ran red 5/5 on scan e4c278eb52 and on main bc2e30000b, partially green 2/5 on pre-fix PR #14100, and green 5/5 on candidate 14b5877a75; the surviving half of that oracle is web-runtime-browser-capability-cleanup.test.ts, still commanded above. The second is tests/e2e/paired-remote-html-browser-focus.spec.ts, renamed in STA-5557 to paired-remote-html-preview-local-render.spec.ts because the journey no longer proves anything about browser focus: its 2026-08-12 runs established the owning-runtime routing and no-focus-steal behavior of the then-current browser-tab preview (red on main 346e59c879 and a90a18d43d, green on candidates d05217c40d and b7de9c0961, red again with only the side-preview focus or routing seam disabled), and its 2026-08-26 runs are recorded below under the current path. An adversarial pass after the journey landed found four coverage gaps rather than shipping defects, and all four are pinned here. The page url had a third door: the navigation commit wrote whatever url was reported, unfenced, and while nothing reaches it for a document page today it sat one report away from giving a document page a URL that persistence, the publish boundary and the address bar all read. The group projection was the load-bearing half of the mobile publish boundary that no test had: removing it alone left the whole suite green, because no test in it had tab groups, while the phone payload gained a group whose order and active tab named a document tab it was never sent. The focus handoff was covered only in the narrowing direction, so widening it — dropping the page-activity check, or either half of the reader-surface check — survived both suites. And the handoff itself was offered once and never again, so another app taking the front left the preview's one route out shut until the pane remounted, which is a desktop gap rather than a test-only one. The standalone capability-cleanup and browser-placement ledger suites were removed, including their mixed-version rejection, cleanup transfer, capacity and partition alarms. web-runtime-session.test.ts no longer supplies browser creation, rollback or recovery assertions. The grouped workspace-document exclusion remains; the separate no-group publication example was removed. Existing live/browser-store evidence does not establish equivalent replacement alarms. Earlier descriptions of the removed fixtures and their run results are historical. The client-owned-placement and layout-groups suites are removed. The broad mirror fixture no longer supplies preview placement/reservation/move assertions. Browser rows retain pre-response replay and real local-workspace reuse; focus intent retains response ordering and exact-leaf sibling ordering. Supplied browser-layout and placement/focus projection alarms are lost; retained evidence is narrower and not equivalent. The test-value audit retires renderer grant-release mock ledgers, direct creation/title/session-field copies, notification/onboarding calls and several placement/per-page projections. Canonical-ID rejection, document reopen/restore and preview URL fences remain; they do not prove close-to-registry revocation or replace every lost alarm. Historical execution evidence is unchanged. The test-value audit removed the direct legacy editor-preview hydration example from tabs-hydration.test.ts. Dropping legacy preview chrome while preserving the same-document ordinary editor no longer has that alarm. The surviving corrupt-id deduplication case does not cover that migration, and retained preview journeys are not equivalent replacement coverage. Historical run counts and migration mutation findings describe the earlier suite. Audit1308: src/renderer/src/components/editor/EditorPanelShell.header.test.tsx removed. Header visibility and mocked editor-surface inventory alarms disappear. No equivalent replacement coverage is claimed. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1386: src/renderer/src/lib/doc-preview-grants.test.ts reduced. Repeated-mount mint count, ordinary release/remint and failed-mint cache retry standalone alarms removed; no actual grant server/access test. No equivalent replacement coverage is claimed. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1412: src/main/browser/guest-lifecycle/manager/browser-manager-guest-policy-profile.test.ts removed. Loses workspace-document in-grant navigation/popup refusal, cleanup/re-attach, cross-registry guest authorization and browsing-map separation alarms. No equivalent browser policy coverage is claimed. No equivalent replacement coverage is claimed. Audit1421 removes browser-address-bar-suggestions.test.ts. Document suggestion title/path matching, recency merging and path-prefix ranking alarms are lost. No equivalent replacement coverage is claimed. Historical receipts remain scoped to unchanged selectors; the reduced command has no new execution result. Audit2452 removes the whole fake-effect browser-close routing suite. Active selectors and assertion references no longer select it. Its requested-worktree default close, sibling-page close and explicit missing-page typed reply alarms are intentionally retired without equivalent replacement. Prior dated receipts remain unchanged and scoped to their original sources; no fresh execution of the reduced gate, rendered browser, live remote, mobile or cross-platform validation is claimed.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-5557/remote-html-preview-render-workspace-files-locally-over-the-file", "https://linear.app/stably/issue/STA-3872/unable-to-open-html-files-in-remote-server", @@ -4319,7 +4319,7 @@ "https://github.com/stablyai/orca/pull/13876" ], "invariant": "Opening one HTML preview from a paired client renders the workspace document in exactly one client-local browser tab, located by that document and served over the orca-preview scheme. The client gains exactly that one browser workspace and it is the document one — blank where a URL page carries a URL, named by the document, with the chip naming the file — while the host gains no browser page at all, neither in its own page registry nor in the tab snapshot its clients publish into. The preview occupies its own split without taking focus from the source editor; an explicit click activates it, and closing it removes only the preview. Following a document from a file link is the other half of that switch and does move the reader to it, tab group included, whether the preview is new or already open, because opening a file is a request to look at it. A document tab quit with the client comes back as the same row on a grant the relaunched client mints afresh. A preview is named by the browser page it is open in, not by a namespace of its own, and the page registry has two halves: a workspace-document guest is registered in its own map and is absent from the browsing one entirely. That absence is the fence. Page, session and profile management, agent tab enumeration and command targeting, download routing and certificate attribution all read the browsing map directly, in more places than a per-channel guard could be remembered in, so none of them can name a document page and none of them carries a guard. Browser tools the reader drives (element grab, hover describe, selection capture, the annotation viewport bridge) are the one operation that legitimately spans the halves, and they go through the single authority that reads both, keyed by the page and its hosting renderer. The halves are disjoint in both directions: browsing registration refuses a page the document half already holds, and minting a grant refuses a page the browsing half already holds, so one id can never name a surface in both. The headless backend acts on that refusal by destroying the window it had already opened rather than leaving a policy-less page behind an id nothing can drive, keeping nothing under that id for its own shutdown to hand back. Registration refuses on the same terms when the guest it was asked about is already gone. The exit door is guarded in both its halves: a preview withdraws by revoking its grant and never through the unregister channel, so a page the document half holds arriving there is refused before either the registration teardown or the grab-state disposal beside it, which would otherwise drop the intent an in-flight preview grab compares by identity and leave that grab answering ok without ever arming its guest. A bridge request whose guest does not resolve is refused without tearing down the page it named, so a misaddressed request cannot cancel a healthy page's in-flight downloads and grabs. The annotation viewport bridge resolves its guest when its serialized op actually runs rather than when the request arrived, so a cross-process navigation while it waited cannot leave the bridge installed in a retired guest while the reader looks at a new one. State main keys by a preview's page is disposed when that page's grant is revoked, which is the only signal a preview's surface is gone. A tool asking for a page whose guest has not attached yet waits for that registration and arms when it arrives, rather than answering not-ready at the reader; that wait resolves only the request already naming this page, never the worktree-wide or any-tab waits the CLI and agents use to ask for a browser tab to drive. Handing the previewed document to the reader's own machine routes on the owners its grant was minted against — the file's own connection owner and the worktree's own runtime owner, neither read from the tab's stored fields. Only a document proven to live on this machine reaches the client OS; one with a resolved remote owner is downloaded first; and one whose owner cannot be resolved at all, workspace root included, is refused with a message naming that, because the download route would otherwise read the same absolute path on the client and hand back a same-named local file under the remote document's name. A runtime-owned path that falls outside its worktree root is refused by that route itself and surfaces as a failure toast rather than a download. Nothing the document does writes a file to this machine either: the preview partition denies downloads outright instead of routing them through the browser download flow, which has no page to attribute a preview's bytes to and would otherwise reserve a name in this desktop's Downloads folder and write them there unprompted. That refusal is visible to the reader and invisible to the document: the preview's shell carries a fixed sentence saying downloads are off, published at most once per preview per interval so a document asking in a loop cannot fill Orca's chrome, while the page itself gets back exactly what it got before, which is nothing. The sentence names no file, because the document chooses the name it offers; and a refusal never takes the document away the way an entry document's own failure does, whatever it names. A preview is a browser tab, not an editor tab in a preview mode: it is named the way a browser tab is named — by the document it shows when that document declares a title, and by the file it shows when it does not — while the chip goes on naming the file and the host whatever the document calls itself. A title is refused on the same terms the url is: a document that declares none has Chromium report the grant URL as its title, and that title is stored, mirrored onto the tab and written to disk, so anything carrying the scheme falls back to the file instead. It is created by the preview action as a page located by its document, it carries the workspace-relative path copy the editor's path header owned, and closing it revokes the grant that made the document readable while a URL tab closing beside it revokes nothing. Chrome persisted by builds that made previews editor tabs is dropped on restore rather than coming back naming a surface no restore can produce, and the ordinary editor tab for the same document is left alone. A document tab is held back at the mobile publish boundary — no client holds its grant, and the wire has no tab kind for it — while an ordinary browser tab beside it still publishes. It is held back from the group projection that publishes tab order, recency and group activity as well as from the tab list itself, so no published group names a tab the phone is never sent. A browser page can be located by a workspace document instead of a URL, and the document is the whole of its stored identity. The grant and the orca-preview URL that document is served over are minted when the page mounts and replaced by a hard reload, so neither is ever written to the page's url, mirrored onto its tab, persisted or published: such a page's url is the blank URL from creation through restore, including when a session written elsewhere carries a grant URL in, and what the session carries is the worktree and path a restored page mints afresh against today's owners. Every door onto a page's url holds that line — creation, the title update, and the navigation commit alike — so a report about a document page cannot give it a URL it never had, and the title fallback and the loading affordance follow the url each door actually wrote. The mirror carries the document too, so a tab entry cannot go on naming a document its active page has left. Every guest in the app is policy-attached through one door: a workspace document takes a restricted profile there rather than a separate installer beside it, so the attachment bookkeeping that door owns — what registration refuses, and what teardown frees — covers a preview on the same terms as a browsing page, and a preview takes none of the browsing machinery that door installs. That authority answers from the moment the embedder hands the guest over rather than only after a later navigation: the guest binds to the grant it is already showing, so the tools reach the document the reader opened and not just one they navigated to. A read the host reports as truncated or over-cap is refused rather than served partially, and a document outside the paired worktree is refused with a message naming that boundary instead of a bare read failure. The rendered document reaches nothing off-machine on its own: every served response carries a self-only content security policy, the preview session cancels any request that is not in-document, subframes cannot navigate outside the grant, a guest no document has yet bound to a grant may not navigate at all, the guest gathers no ICE candidates, and an SSH path that canonicalizes outside the grant root is refused before it is read. The one route out is a link the reader presses: a trusted click on an anchor, reported by the preview's own preload from a guest still bound to a live grant, leaves through the configured browser destination and captured source route — and only after the reader confirms the exact destination URL, so a document cannot spend a single stray press exfiltrating what it can read into a link it authored. The preview hands its guest that focus itself whenever it is the surface the reader is in — a browsing page gets it from the chrome around it, and a preview has no chrome to get it from — and it does so only then, so a preview mounted behind a terminal or an editor never takes the keyboard from what the reader is actually in. It offers again when the window itself takes focus back and nothing in the embedder has claimed that focus, because another app coming to the front lands focus on the embedder rather than the guest and the route out would otherwise stay shut until something remounted the pane — while the same window focus also arrives when the reader presses a tab, that being the guest's own blur returning, and taking focus back from there would fight the reader for their own click. Nothing else does. A navigation or popup the document starts by itself is swallowed whatever else is happening, including immediately after a genuine press elsewhere in the document, so a page that can read its grant cannot hand it to a browser tab; a middle click opens nothing; and a fragment link is answered inside the document. A preview attach carries the preview preload and no renderer-supplied one, and no other attach path can acquire it. A subresource the workspace will not send degrades the document to a notice naming that file, never to a failure panel over a page that rendered. A grant outlives neither the tab that owns it nor the renderer document that minted it, and only the trusted renderer can mint or revoke one. For the browser creations this gate still owns, owner-pinned creation returns the canonical host page identity before navigation readiness; delayed navigation cannot turn a created page into an unidentifiable failure or a duplicate retry. Capability rejection before host mutation must preserve the original error, issue no RPC, surface a failure toast, and remove only a caller-declared newly-created empty split. Post-create reconciliation failure requires exact rollback; ambiguous rollback rejects without local fallback.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. The complete mocked session-close routing suite is removed, including the explicit cross-worktree cleanup alarm. Remove its active file, command token and assertion reference; historical selectors and receipts retain their original scope. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3342 retires the complete mobile editor/diff tab-publication projection suite: editor/diff serialization, workspace-document privacy withholding, pending/focus and capability/options projections are intentionally lost without equivalent replacement. The surviving focused selectors are unexecuted in this source composition. Six complete compound macOS receipts whose command fields select the changed source are retired verbatim as history; their other selected tests receive no promoted evidence. Other dated receipts keep their original scope; no fresh runtime/UI/mobile/mixed-version or cross-platform validation is claimed. Audit3335 retires the complete three-case browser-tabs snapshot assembly suite: canonical snapshot replay/placement/focus, local host-page adoption and unstamped host echo leaving a local page untouched alarms are intentionally lost without equivalent replacement. The surviving focused selector is unexecuted in this source composition. Unrelated dated receipts keep their original scope; no fresh runtime/network, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Audit 3301 removes the caller-chat-target precedence over host selection example from web-session-tabs-sync-focus-intent.test.ts. The snapshot-before-RPC replay and split-sibling-first exact-leaf focus cases remain unchanged and match the existing assertion reference. Preserve current selectors, assertion references, unrelated active receipts and complete archived history. These mocked state-composition survivors provide no equivalent replacement, rendered, native, network or fresh execution proof. Audit3108: doc-preview-grants.test.ts now retains only the stale grant-rejection race: a late rejected grant request must not evict a newer grant registry entry, witnessed by later revocation of grant-2. Retire stable minted-location and outside-workspace POSIX/Windows request-projection alarms without replacement. The direct nonempty grant selector remains unchanged; no real grant-server/file authorization, SSH/HTML delivery or native-focus result is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2043: Remove the registration-wait, webview-security, preview-authorization, guest-lifecycle and grant-IPC unit suites and the sibling-directory assertion reference. Runtime and browser capability consumers also lose removed imported examples. Remaining document-policy, file-reader and grant-registry assertions do not replace those alarms. Preserve the 27 independent dated receipts at their original unchanged selectors; reduced prospective unit selectors have no fresh result or equivalent replacement claim. Historical prior account (preserved verbatim): Audit1973: Delete the browser RPC forwarding/schema/scalar suite and its prospective selector. Preserve all27 unrelated dated receipts; no actual browser operation alarm is replaced. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): The complete terminal-file-link-actions suite is removed. Lose supplied local/remote menu-row inventories, browser label/order/download/reveal callback forwarding, platform-copy and source-group pinning examples. No OS reveal/download, real remote preview or browser navigation was exercised; the other real document-preview sources are not equivalent replacements. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1709 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1687 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1421 narrows renderer unit coverage as recorded in coverageNotes. Prior oracle descriptions are historical for the changed suite. The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): Retained unchanged-selector receipts are the only current execution evidence. Reduced-source assertions await validation. Historical prior oracle: The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): In paired Electron, write an HTML fixture that declares its own title on the host, invoke the Explorer preview action on the client, and require the document text to be readable out of the orca-preview guest before judging any absence. With that presence established, require the client to hold exactly one browser workspace more than its baseline and that workspace to be the document one: page and tab url blank, the document path mirrored onto both, the tab named by the document's title, the chip naming the file, no editor row of the retired preview species anywhere, and the guest URL carrying the orca-preview scheme. Ask the host through its own page registry as well as through the tab snapshot, and in the same run open an ordinary URL browser tab from the same client and require that one to arrive in both — the presence precondition without which “the host gained nothing” is satisfied just as well by an oracle that cannot see browser pages at all. Require the preview to sit in a group other than the source editor's while the active group and tab remain the source editor's. Then click away to the terminal, click the preview tab, require it to reactivate and still render, close it with its own X, and require the document tab to be gone while the host still holds only the URL tab and the source group, source editor and terminal survive. Quit the client with a document tab open and relaunch it on the same profile: require the same workspace row to come back, blank and named by the document, rendering the document again over a grant URL that differs from the one that was quit, with no preview-scheme or document-named page anywhere in what the host holds. Prove the halves are live by flipping one product property at a time and requiring the run to fail: publish document workspaces to the host like ordinary ones, and stop mirroring the document onto the workspace row. Have the fixture document attempt its own egress on every load — an unattended window.open and location.href to an off-machine URL, plus an inline ICE gathering probe — and require the same baseline counts and a candidate count of zero, so the document's own attempts are measured rather than assumed. Then, as a separate phase after the close oracle has already run, bring the client window to the front, press the document's heading with a real mouse event, and require the document to report that the same press drove it to attempt a second window.open and location.href while both browser counts stay at that phase's baseline and nothing routes — the case a recent-input gate cannot distinguish from the press's own effect. Only then press the target=_blank link with a real mouse event and require both a recorded routing call that returned success and a browser count above that baseline, with the preview tab still open. Drive the preload's click policy as a unit oracle over a real document: a dispatched click, a trusted press on an external anchor, an anchor reached through what it wraps, an SVG animated href, a sibling preview link, fragment and percent-encoded fragment targets, a bare hash, and a middle click. Create a browser page located by a workspace document, handing creation a live grant URL, and require its stored url, its mirrored tab url and the written session payload all to be blank with no orca-preview string anywhere in what was written, while an ordinary page created the same way keeps the URL it was given and asks for the address bar the document page never does. Parse the written page and tab through the session schema and require the document to survive both halves. Hydrate them back and require the document page to return blank and still named — including when its page row was salvaged away and only the tab's own copy remains, and when a foreign session carried a grant URL into both rows. Drive the mirror across a page switch out of the document and back, and across a repair in which the document is the only mirrored field that differs. Name a document page from its document and require the tab to take that name, name it with an empty title and require the file, name it with a live grant URL and require the file again with no preview scheme anywhere in the written session, and require an ordinary blank browser tab beside it to still be called New Tab. Dispatch a title update out of a rendered preview's own guest and require it to reach the page state while the identity chip still reads the document's workspace-relative path. Attach a browsing guest and a workspace-document guest through the same method in one run and require the browsing one to take clicked-link routing, popup handling and auth-identity detach tracking while the document guest takes none of them, stays inside the grant it is showing, denies every window it asks for, and is dropped from the page-keyed document registry by the same teardown that frees its id for a later attach. Register a browsing guest and attach a workspace-document guest in one run against the real manager, require the one door to answer each page with the guest of its own half, and require the document page to be absent from the browsing map and from its enumeration. Drive both browsing registration entry points with a page the document half already holds and require them to register nothing, and drive the mint channel with a page the browsing half already holds and require it to refuse; and drive the offscreen one with a guest that is missing and with one already destroyed, requiring the same refusal. Arm a grab on a live preview target, drive the unregister channel at that same target in the window before the queued operation runs, and require the grab to reach the guest anyway — then drive the same sequence for an ordinary browser page and require its grab state to be disposed after all. Hold one viewport-bridge op open, queue a second behind it, swap the page's guest while that second op waits, and require the injection to land in the guest the page has then. Revoke a grant after a tool has run against its page and require that page's grab state to be cancelled and disposed. Ask a tool for a document page whose guest has not attached, require the request to park in the registration wait, attach the guest, and require the same request to arm on it; require a page nothing ever renders to answer not-ready once that wait elapses. With a document open, ask a tool for a browsing page id and require it to be answered by the browsing half or not at all, with the same channel reaching the document guest under the page it really renders. Drive open-externally for a document whose per-file owner is remote while the workspace-scoped owner is unresolved, for a runtime-owned worktree whose preview tab carries no runtime id of its own, and for a worktree that resolves no runtime owner while the tab still carries one, requiring the download route in each; and for an owner that cannot be resolved at all, and for an unknown workspace root while nothing names another host, requiring a refusal that neither opens nor downloads. Drive the headless backend with a page the document half already holds and require it to reject, destroy the window, and unregister nothing — then shut the backend down and require it still to have unregistered nothing. Navigate a bound preview guest at a second grant through both latch events and require it to stay on the grant it bound to. Mount a preview while a renderer drag is already in flight and require its guest to be click-through at the moment it is appended, not a turn later. Render the editor panel shell in each remaining tab mode and require the path header exactly where the surface does not already name itself. Drive the preview action and require a browser tab located by the document rather than an editor tab, require a second open of the same document to activate the tab it is already in, and require closing that tab to revoke its grant while a URL tab closed beside it revokes none. Publish a worktree holding a document tab and a URL tab and require only the URL tab to reach the mobile snapshot. Install the shared partition policies for a preview partition and for an ordinary browsing partition in the same run, fire each one's own will-download listener, and require the preview's to cancel while the browsing one still reaches the download router — then require the preview protocol installer to be what asks for that deny. In the same run, require the cancelled download to raise a reader-facing notice and the routed one to raise none. require the guest registry to name the bound grant for a live preview guest and nothing for a contents that is not one, has committed no document, or is gone. Drive the shell with a refusal and require one fixed sentence, still one row after three more refusals, standing beside an asset failure rather than being counted with it, gone behind the failure panel, and ignored when it names another preview's grant. Drive the main-side report gate directly for a sender that is no preview guest, a guest with no bound or a revoked grant, a genuine press Electron's webview focus flag misreports as unfocused, and non-web URLs; drive the reader-facing confirmation for accept, cancel, and a confirmed tab the browser refuses; and drive will-attach-webview in both preload directions. Run the per-owner reader, grant-containment, scheme-admission, guest-policy, and plan-routing contracts as unit oracles, including a host-reported truncation, an over-cap binary, and an out-of-worktree paired path. Drive the reader-facing component with the payloads the reader can actually produce — the entry document fails only as truncated or unreadable, a subresource additionally as a refused format — and require the asset case to leave the guest mounted. Drive the closed-tab cleanup hook, the window installer, and the grant IPC handlers directly, requiring the grant to be released when the preview tab closes, cleared at window creation and on a cross-document main-frame navigation, and refused to any sender that is not the trusted renderer. For the browser creations this gate still owns, run the unchanged contract oracle for direct create and side-preview callers with absent status, unknown capabilities, and a mixed-version host, requiring the original unsupported error or visible toast, zero RPCs, and no retained new split; hold a real navigation response beyond the 15-second client deadline after host creation and require the first RPC to return the exact host inventory page ID, one host page, and no retry; repeat reconciliation faults against headless serve and retain the separate exact-page reconciliation rollback oracle. Audit2452 removes the whole fake-effect browser-close routing suite. Active selectors and assertion references no longer select it. Its requested-worktree default close, sibling-page close and explicit missing-page typed reply alarms are intentionally retired without equivalent replacement. Prior dated receipts remain unchanged and scoped to their original sources; no fresh execution of the reduced gate, rendered browser, live remote, mobile or cross-platform validation is claimed.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. The complete mocked session-close routing suite is removed, including the explicit cross-worktree cleanup alarm. Remove its active file, command token and assertion reference; historical selectors and receipts retain their original scope. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3342 retires the complete mobile editor/diff tab-publication projection suite: editor/diff serialization, workspace-document privacy withholding, pending/focus and capability/options projections are intentionally lost without equivalent replacement. The surviving focused selectors are unexecuted in this source composition. Six complete compound macOS receipts whose command fields select the changed source are retired verbatim as history; their other selected tests receive no promoted evidence. Other dated receipts keep their original scope; no fresh runtime/UI/mobile/mixed-version or cross-platform validation is claimed. Audit3335 retires the complete three-case browser-tabs snapshot assembly suite: canonical snapshot replay/placement/focus, local host-page adoption and unstamped host echo leaving a local page untouched alarms are intentionally lost without equivalent replacement. The surviving focused selector is unexecuted in this source composition. Unrelated dated receipts keep their original scope; no fresh runtime/network, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Audit 3301 removes the caller-chat-target precedence over host selection example from web-session-tabs-sync-focus-intent.test.ts. The snapshot-before-RPC replay and split-sibling-first exact-leaf focus cases remain unchanged and match the existing assertion reference. Preserve current selectors, assertion references, unrelated active receipts and complete archived history. These mocked state-composition survivors provide no equivalent replacement, rendered, native, network or fresh execution proof. Audit3108: doc-preview-grants.test.ts now retains only the stale grant-rejection race: a late rejected grant request must not evict a newer grant registry entry, witnessed by later revocation of grant-2. Retire stable minted-location and outside-workspace POSIX/Windows request-projection alarms without replacement. The direct nonempty grant selector remains unchanged; no real grant-server/file authorization, SSH/HTML delivery or native-focus result is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2043: Remove the registration-wait, webview-security, preview-authorization, guest-lifecycle and grant-IPC unit suites and the sibling-directory assertion reference. Runtime and browser capability consumers also lose removed imported examples. Remaining document-policy, file-reader and grant-registry assertions do not replace those alarms. Preserve the 27 independent dated receipts at their original unchanged selectors; reduced prospective unit selectors have no fresh result or equivalent replacement claim. Historical prior account (preserved verbatim): Audit1973: Delete the browser RPC forwarding/schema/scalar suite and its prospective selector. Preserve all27 unrelated dated receipts; no actual browser operation alarm is replaced. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): The complete terminal-file-link-actions suite is removed. Lose supplied local/remote menu-row inventories, browser label/order/download/reveal callback forwarding, platform-copy and source-group pinning examples. No OS reveal/download, real remote preview or browser navigation was exercised; the other real document-preview sources are not equivalent replacements. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1709 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1687 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1421 narrows renderer unit coverage as recorded in coverageNotes. Prior oracle descriptions are historical for the changed suite. The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): Retained unchanged-selector receipts are the only current execution evidence. Reduced-source assertions await validation. Historical prior oracle: The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): In paired Electron, write an HTML fixture that declares its own title on the host, invoke the Explorer preview action on the client, and require the document text to be readable out of the orca-preview guest before judging any absence. With that presence established, require the client to hold exactly one browser workspace more than its baseline and that workspace to be the document one: page and tab url blank, the document path mirrored onto both, the tab named by the document's title, the chip naming the file, no editor row of the retired preview species anywhere, and the guest URL carrying the orca-preview scheme. Ask the host through its own page registry as well as through the tab snapshot, and in the same run open an ordinary URL browser tab from the same client and require that one to arrive in both — the presence precondition without which “the host gained nothing” is satisfied just as well by an oracle that cannot see browser pages at all. Require the preview to sit in a group other than the source editor's while the active group and tab remain the source editor's. Then click away to the terminal, click the preview tab, require it to reactivate and still render, close it with its own X, and require the document tab to be gone while the host still holds only the URL tab and the source group, source editor and terminal survive. Quit the client with a document tab open and relaunch it on the same profile: require the same workspace row to come back, blank and named by the document, rendering the document again over a grant URL that differs from the one that was quit, with no preview-scheme or document-named page anywhere in what the host holds. Prove the halves are live by flipping one product property at a time and requiring the run to fail: publish document workspaces to the host like ordinary ones, and stop mirroring the document onto the workspace row. Have the fixture document attempt its own egress on every load — an unattended window.open and location.href to an off-machine URL, plus an inline ICE gathering probe — and require the same baseline counts and a candidate count of zero, so the document's own attempts are measured rather than assumed. Then, as a separate phase after the close oracle has already run, bring the client window to the front, press the document's heading with a real mouse event, and require the document to report that the same press drove it to attempt a second window.open and location.href while both browser counts stay at that phase's baseline and nothing routes — the case a recent-input gate cannot distinguish from the press's own effect. Only then press the target=_blank link with a real mouse event and require both a recorded routing call that returned success and a browser count above that baseline, with the preview tab still open. Drive the preload's click policy as a unit oracle over a real document: a dispatched click, a trusted press on an external anchor, an anchor reached through what it wraps, an SVG animated href, a sibling preview link, fragment and percent-encoded fragment targets, a bare hash, and a middle click. Create a browser page located by a workspace document, handing creation a live grant URL, and require its stored url, its mirrored tab url and the written session payload all to be blank with no orca-preview string anywhere in what was written, while an ordinary page created the same way keeps the URL it was given and asks for the address bar the document page never does. Parse the written page and tab through the session schema and require the document to survive both halves. Hydrate them back and require the document page to return blank and still named — including when its page row was salvaged away and only the tab's own copy remains, and when a foreign session carried a grant URL into both rows. Drive the mirror across a page switch out of the document and back, and across a repair in which the document is the only mirrored field that differs. Name a document page from its document and require the tab to take that name, name it with an empty title and require the file, name it with a live grant URL and require the file again with no preview scheme anywhere in the written session, and require an ordinary blank browser tab beside it to still be called New Tab. Dispatch a title update out of a rendered preview's own guest and require it to reach the page state while the identity chip still reads the document's workspace-relative path. Attach a browsing guest and a workspace-document guest through the same method in one run and require the browsing one to take clicked-link routing, popup handling and auth-identity detach tracking while the document guest takes none of them, stays inside the grant it is showing, denies every window it asks for, and is dropped from the page-keyed document registry by the same teardown that frees its id for a later attach. Register a browsing guest and attach a workspace-document guest in one run against the real manager, require the one door to answer each page with the guest of its own half, and require the document page to be absent from the browsing map and from its enumeration. Drive both browsing registration entry points with a page the document half already holds and require them to register nothing, and drive the mint channel with a page the browsing half already holds and require it to refuse; and drive the offscreen one with a guest that is missing and with one already destroyed, requiring the same refusal. Arm a grab on a live preview target, drive the unregister channel at that same target in the window before the queued operation runs, and require the grab to reach the guest anyway — then drive the same sequence for an ordinary browser page and require its grab state to be disposed after all. Hold one viewport-bridge op open, queue a second behind it, swap the page's guest while that second op waits, and require the injection to land in the guest the page has then. Revoke a grant after a tool has run against its page and require that page's grab state to be cancelled and disposed. Ask a tool for a document page whose guest has not attached, require the request to park in the registration wait, attach the guest, and require the same request to arm on it; require a page nothing ever renders to answer not-ready once that wait elapses. With a document open, ask a tool for a browsing page id and require it to be answered by the browsing half or not at all, with the same channel reaching the document guest under the page it really renders. Drive open-externally for a document whose per-file owner is remote while the workspace-scoped owner is unresolved, for a runtime-owned worktree whose preview tab carries no runtime id of its own, and for a worktree that resolves no runtime owner while the tab still carries one, requiring the download route in each; and for an owner that cannot be resolved at all, and for an unknown workspace root while nothing names another host, requiring a refusal that neither opens nor downloads. Drive the headless backend with a page the document half already holds and require it to reject, destroy the window, and unregister nothing — then shut the backend down and require it still to have unregistered nothing. Navigate a bound preview guest at a second grant through both latch events and require it to stay on the grant it bound to. Mount a preview while a renderer drag is already in flight and require its guest to be click-through at the moment it is appended, not a turn later. Render the editor panel shell in each remaining tab mode and require the path header exactly where the surface does not already name itself. Drive the preview action and require a browser tab located by the document rather than an editor tab, require a second open of the same document to activate the tab it is already in, and require closing that tab to revoke its grant while a URL tab closed beside it revokes none. Publish a worktree holding a document tab and a URL tab and require only the URL tab to reach the mobile snapshot. Install the shared partition policies for a preview partition and for an ordinary browsing partition in the same run, fire each one's own will-download listener, and require the preview's to cancel while the browsing one still reaches the download router — then require the preview protocol installer to be what asks for that deny. In the same run, require the cancelled download to raise a reader-facing notice and the routed one to raise none. require the guest registry to name the bound grant for a live preview guest and nothing for a contents that is not one, has committed no document, or is gone. Drive the shell with a refusal and require one fixed sentence, still one row after three more refusals, standing beside an asset failure rather than being counted with it, gone behind the failure panel, and ignored when it names another preview's grant. Drive the main-side report gate directly for a sender that is no preview guest, a guest with no bound or a revoked grant, a genuine press Electron's webview focus flag misreports as unfocused, and non-web URLs; drive the reader-facing confirmation for accept, cancel, and a confirmed tab the browser refuses; and drive will-attach-webview in both preload directions. Run the per-owner reader, grant-containment, scheme-admission, guest-policy, and plan-routing contracts as unit oracles, including a host-reported truncation, an over-cap binary, and an out-of-worktree paired path. Drive the reader-facing component with the payloads the reader can actually produce — the entry document fails only as truncated or unreadable, a subresource additionally as a refused format — and require the asset case to leave the guest mounted. Drive the closed-tab cleanup hook, the window installer, and the grant IPC handlers directly, requiring the grant to be released when the preview tab closes, cleared at window creation and on a cross-document main-frame navigation, and refused to any sender that is not the trusted renderer. For the browser creations this gate still owns, run the unchanged contract oracle for direct create and side-preview callers with absent status, unknown capabilities, and a mixed-version host, requiring the original unsupported error or visible toast, zero RPCs, and no retained new split; hold a real navigation response beyond the 15-second client deadline after host creation and require the first RPC to return the exact host inventory page ID, one host page, and no retry; repeat reconciliation faults against headless serve and retain the separate exact-page reconciliation rollback oracle. Audit2452 removes the whole fake-effect browser-close routing suite. Active selectors and assertion references no longer select it. Its requested-worktree default close, sibling-page close and explicit missing-page typed reply alarms are intentionally retired without equivalent replacement. Prior dated receipts remain unchanged and scoped to their original sources; no fresh execution of the reduced gate, rendered browser, live remote, mobile or cross-platform validation is claimed.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/browser/client-pages/orca-runtime-browser.test.ts src/renderer/src/runtime/web-session-tabs-sync/navigation/web-session-tabs-sync-focus-intent.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/document-preview/doc-preview-grant-registry.test.ts src/main/browser/document-preview/doc-preview-file-reader.test.ts src/main/browser/document-preview/doc-preview-guest-policy.test.ts src/main/browser/document-preview/doc-preview-failure-notice.test.ts src/shared/document-preview/doc-preview-scheme.test.ts src/renderer/src/lib/browser/documents/doc-preview-grants.test.ts src/renderer/src/components/browser-pane/workspace-doc/doc-preview-external-link-confirmation.test.ts", @@ -5777,10 +5777,10 @@ "providers": ["local", "daemon", "ssh", "remote-runtime", "wsl"], "coveredPlatforms": ["macos"], "coveredProviders": ["local", "daemon", "remote-runtime"], - "coverageNotes": "Audit4345 deletes the foreground agent-process suite (8 authored cases): nested/stopped POSIX shells, recycled/query-denied Windows anchors and console-attachment filtering/refusal alarms are deliberately lost without equivalent replacement. Reduced selectors have no fresh gate execution; unrelated historical receipts retain their own commands. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3984 retires all nine mobile-presence and imported subscribe callbacks plus their orphan store fixture. Accepted losses include desktop/phone restore geometry, echo filtering, held reconnect/reclaim notifications, and the real headless snapshot sequence race. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial mobile-presence suite retains only five authored callbacks: admitted preview-paste completion across phone takeover, desktop take-back superseding mobile input, desktop viewport write/restore geometry, and held-fit reclaim convergence. Other supplied floor/rollback/selection/viewport/grace/disconnect/reconnect/orphan-state alarms are lost. No per-source assertion object exists to reassign. The one affected dated mobile-view-authority receipt is retired whole into historical notes with its full original/currentCommand/raw data preserved; it is not active corroboration for reduced candidate bytes. Imported mobile-subscribe registrations and retained callbacks remain bounded to their actual source obligations, without equivalent replacement or physical-phone/native-PTY claims. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1709 removes low-value test definitions and retires affected assertion or execution-receipt claims without equivalent replacement coverage. Per-source reviews bound retained behavior and injected/native/remote limits. Unchanged assertions and receipts keep their original scope; gates lacking current active evidence are downgraded. Historical scope before this audit: Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally deletes src/renderer/src/components/terminal-pane/pty-connection-hidden-tui-snapshot-replay.spec.ts: Loses rich hidden snapshot sequence replay/grid resize and forced repaint after hidden snapshot alarms, including live-trigger deduplication. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1320 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Deterministic host/parser, process-classifier, snapshot-publication, mixed-version, renderer, and macOS Electron coverage proves the local and shared paired-runtime contracts. Direct SSH intentionally publishes no shell proof and immediately preserves incumbent behavior. WSL, Windows ConPTY, Linux desktop, and paired headed/headless journeys remain gaps. The audit removed the isolated terminal-multiplex pending-escape-tail forwarding test. Its direct RPC snapshot field-copy alarm is lost; retained parser, snapshot and lifecycle suites do not establish equivalent RPC field coverage. Audit1320: src/renderer/src/components/terminal-pane/remote-runtime-pty-snapshot-escape-tail.test.ts removed. Snapshot pending escape/alternate-screen/shell metadata forwarding and negative app-store-read alarm disappears. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1391: src/renderer/src/components/terminal-pane/pty-connection-remote-pause-drop-notice.spec.ts reduced. Lose repeated quiet switches avoiding snapshots and legacy host without drop-report capability requesting a snapshot. Retained imported registrar uses fake transport/xterm writes, exercising real gap/restore lifecycle rather than actual terminal rendering. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1402: src/renderer/src/components/terminal-pane/pty-connection-park-reveal-model-snapshot.spec.ts reduced. Loses basic paired serialize/initial replay forwarding check; hidden alt-frame grid/precedence policy remains. No real terminal renderer claim. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1412: src/main/daemon/terminal-host-concurrent-create.test.ts reduced. Loses race deduplication, attach during unsettled ownership proof, retry after failed spawn, distinct-session parallelism, disposal waiting and shutdown retry-fencing alarms bundled with removed internal-call counts or real-time waits. No replacement-equivalence claim. src/main/daemon/terminal-checkpoint-serializer.test.ts reduced. Loses bounded native conversion/no-whole-candidate materialization and snapshot getter single-read alarms. Retained byte-size outcomes do not establish CPU/allocation bounds. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4345 deletes the foreground agent-process suite (8 authored cases): nested/stopped POSIX shells, recycled/query-denied Windows anchors and console-attachment filtering/refusal alarms are deliberately lost without equivalent replacement. Reduced selectors have no fresh gate execution; unrelated historical receipts retain their own commands. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3984 retires all nine mobile-presence and imported subscribe callbacks plus their orphan store fixture. Accepted losses include desktop/phone restore geometry, echo filtering, held reconnect/reclaim notifications, and the real headless snapshot sequence race. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial mobile-presence suite retains only five authored callbacks: admitted preview-paste completion across phone takeover, desktop take-back superseding mobile input, desktop viewport write/restore geometry, and held-fit reclaim convergence. Other supplied floor/rollback/selection/viewport/grace/disconnect/reconnect/orphan-state alarms are lost. No per-source assertion object exists to reassign. The one affected dated mobile-view-authority receipt is retired whole into historical notes with its full original/currentCommand/raw data preserved; it is not active corroboration for reduced candidate bytes. Imported mobile-subscribe registrations and retained callbacks remain bounded to their actual source obligations, without equivalent replacement or physical-phone/native-PTY claims. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1709 removes low-value test definitions and retires affected assertion or execution-receipt claims without equivalent replacement coverage. Per-source reviews bound retained behavior and injected/native/remote limits. Unchanged assertions and receipts keep their original scope; gates lacking current active evidence are downgraded. Historical scope before this audit: Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally deletes src/renderer/src/components/terminal-pane/pty-connection-hidden-tui-snapshot-replay.spec.ts: Loses rich hidden snapshot sequence replay/grid resize and forced repaint after hidden snapshot alarms, including live-trigger deduplication. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1320 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Deterministic host/parser, process-classifier, snapshot-publication, mixed-version, renderer, and macOS Electron coverage proves the local and shared paired-runtime contracts. Direct SSH intentionally publishes no shell proof and immediately preserves incumbent behavior. WSL, Windows ConPTY, Linux desktop, and paired headed/headless journeys remain gaps. The audit removed the isolated terminal-multiplex pending-escape-tail forwarding test. Its direct RPC snapshot field-copy alarm is lost; retained parser, snapshot and lifecycle suites do not establish equivalent RPC field coverage. Audit1320: src/renderer/src/components/terminal-pane/remote-runtime-pty-snapshot-escape-tail.test.ts removed. Snapshot pending escape/alternate-screen/shell metadata forwarding and negative app-store-read alarm disappears. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1391: src/renderer/src/components/terminal-pane/pty-connection-remote-pause-drop-notice.spec.ts reduced. Lose repeated quiet switches avoiding snapshots and legacy host without drop-report capability requesting a snapshot. Retained imported registrar uses fake transport/xterm writes, exercising real gap/restore lifecycle rather than actual terminal rendering. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1402: src/renderer/src/components/terminal-pane/pty-connection-park-reveal-model-snapshot.spec.ts reduced. Loses basic paired serialize/initial replay forwarding check; hidden alt-frame grid/precedence policy remains. No real terminal renderer claim. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1412: src/main/daemon/terminal-host-concurrent-create.test.ts reduced. Loses race deduplication, attach during unsettled ownership proof, retry after failed spawn, distinct-session parallelism, disposal waiting and shutdown retry-fencing alarms bundled with removed internal-call counts or real-time waits. No replacement-equivalence claim. src/main/daemon/terminal-checkpoint-serializer.test.ts reduced. Loses bounded native conversion/no-whole-candidate materialization and snapshot getter single-read alarms. Retained byte-size outcomes do not establish CPU/allocation bounds. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": ["https://github.com/stablyai/orca/pull/16379"], "invariant": "After a TUI exits or is killed, reveal, reattach, snapshot replay, or renderer remount must not deliver terminal-owned mouse or alternate-screen protocol bytes to the surviving shell. Recovery is an ordered output barrier in the daemon session data path: an OSC 133;D completing while the alternate screen is still active pauses the stream at that exact byte boundary, a fresh execution-host process inspection proves shell ownership, and on proof a mode reset is injected as in-stream output so every consumer converges by parsing the same bytes and the queued post-boundary shell output (the prompt) lands on the normal buffer. Snapshots are pure reads. Any failure — refuted proof, timeout, queue overflow, session death, disposal — flushes the queue unmodified, preserving incumbent behavior; later command or mode bytes revoke proof. Clean alternate-screen exits prove ownership asynchronously without pausing.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial mobile-presence suite retains only five authored callbacks: admitted preview-paste completion across phone takeover, desktop take-back superseding mobile input, desktop viewport write/restore geometry, and held-fit reclaim convergence. Other supplied floor/rollback/selection/viewport/grace/disconnect/reconnect/orphan-state alarms are lost. No per-source assertion object exists to reassign. The one affected dated mobile-view-authority receipt is retired whole into historical notes with its full original/currentCommand/raw data preserved; it is not active corroboration for reduced candidate bytes. Imported mobile-subscribe registrations and retained callbacks remain bounded to their actual source obligations, without equivalent replacement or physical-phone/native-PTY claims. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1709 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Historical pre-audit oracle account for changed sources: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): Run one fixed child-TUI journey for normal exit and cleanup-free SIGKILL. Assert renderer and host normal-buffer/non-mouse state, host snapshot terminalOwner metadata, exact PTY writes with no post-exit mouse report, unrelated-pane survival, post-boundary prompt output preserved (normal exit), ordered proof invalidation, bounded settlement and bail-out flush, one inspection per unclean episode with zero scans for ordinary output, split-escape safety at every chunk boundary, and old/new client-host fallback parity.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial mobile-presence suite retains only five authored callbacks: admitted preview-paste completion across phone takeover, desktop take-back superseding mobile input, desktop viewport write/restore geometry, and held-fit reclaim convergence. Other supplied floor/rollback/selection/viewport/grace/disconnect/reconnect/orphan-state alarms are lost. No per-source assertion object exists to reassign. The one affected dated mobile-view-authority receipt is retired whole into historical notes with its full original/currentCommand/raw data preserved; it is not active corroboration for reduced candidate bytes. Imported mobile-subscribe registrations and retained callbacks remain bounded to their actual source obligations, without equivalent replacement or physical-phone/native-PTY claims. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1709 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Historical pre-audit oracle account for changed sources: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): Run one fixed child-TUI journey for normal exit and cleanup-free SIGKILL. Assert renderer and host normal-buffer/non-mouse state, host snapshot terminalOwner metadata, exact PTY writes with no post-exit mouse report, unrelated-pane survival, post-boundary prompt output preserved (normal exit), ordered proof invalidation, bounded settlement and bail-out flush, one inspection per unclean episode with zero scans for ordinary output, split-escape safety at every chunk boundary, and old/new client-host fallback parity.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/daemon/shell-readiness/testing/terminal-shell-recovery-barrier.test.ts src/main/daemon/sessions/testing/session-shell-recovery.test.ts src/main/daemon/terminal-host/testing/terminal-host-concurrent-create.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/history/restoration/testing/daemon-restore-scrollback-depth.test.ts src/main/daemon/history/restoration/testing/terminal-checkpoint-serializer.test.ts src/main/runtime/construction/orca-runtime.test.ts src/shared/terminal/protocol/escape/terminal-partial-escape-tail.test.ts src/shared/terminal/protocol/escape/terminal-partial-escape-tail.fuzz.test.ts --reporter=dot", "pnpm exec vitest run --config config/vitest.config.ts tests/e2e/cross-version-wire/cross-version-terminal-wire.unit.test.ts --reporter=dot", @@ -6165,7 +6165,7 @@ "providers": ["local", "daemon", "ssh", "runtime"], "coveredPlatforms": ["macos"], "coveredProviders": ["local", "daemon", "ssh", "runtime"], - "coverageNotes": "Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1674 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1630 removes low-value Git, GitHub/GitLab and IPC projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Live macOS Electron tests prove exact local PTY disappearance after parked-tab close and detached-pgid descendant death after agent close. A deterministic final-pane interleaving now proves durable tab retirement commits before externally owned PTY teardown can publish graph loss, and preserves an unrelated live canary through duplicate exit retirement. Deterministic tests also cover final-pane CLI close durability and same-incarnation handle continuity; persisted-empty restart suppression remains historical Electron evidence, alongside replacement and hard graph-loss stale fencing, preallocated-handle supersession, daemon and SSH routing, local/daemon pending-snapshot ownership across natural exit, stale-root descendant-signal suppression, graceful-to-immediate kill upgrades, duplicate-kill completion sharing, locale-stable bounded/fresh/coalesced process-table reads, deadline-safe successor scans, cycle-safe linear descendant traversal, target-only escalation indexing, 32-wide bulk teardown, source-scan timestamp identity, same-second PID ambiguity, ordinary runtime close ownership, unified-only hydration, split ownership, pane detach transfer, restart alias hydration, and late-hook suppression; live Linux, Windows, WSL, SSH, and remote-runtime process evidence remains pending. A paired macOS Electron journey (real attached-window desktop host plus a separate real paired desktop client, isolated profiles) covers the inverse authority direction for host-created terminals: a renderer graph sync must not retire a tab that `orca terminal create` made. The retained paired journey covers the incident shape the real CLI produces - a repo's FIRST host-created terminal, since src/cli/handlers/terminal.ts sends no clientMutationId. The repeated target-after-unrelated-terminal journey was retired; its separate ordering alarm is no longer provided. An earlier reading of this gate called a preceding host-created terminal and a `clientMutationId` protective; main-process instrumentation refuted that. The renderer's stale session write deleted the host tab from persistence in every variant, and only the delete's timing relative to the measured sync differed, so those rows were false negatives of a single-sync oracle. The defect was in persistence: persistPtyBinding did not raise the repo's terminal topology fence for the first host-admitted tab, leaving the renderer's pre-create tab list free to replay over it. Elapsed time and paired-client mirroring never decided anything. A windowless `orca serve` parity arm runs the identical oracle and passes on both the merge-base and the branch, confirming that topology does not exhibit the defect (the gate it turns on is window-conditioned) and that making the host binding unconditional did not regress it. All of this is macOS-local only. The reported incident occurred on a physical Windows host; we did not run against one. The claim that the mechanism is platform-independent rests on the authoritative-window gate and publication-epoch inheritance being OS-agnostic, not on Windows evidence, so no ConPTY, Windows window-lifecycle, update, or sleep behavior is covered here. The synthetic default-tabs unit suite was retired: its explicit-empty/default-tab assertions no longer supply unit evidence. This retirement supplies no new persisted-restart, runtime-routing, cross-platform, or mobile evidence; existing historical runs and counts remain historical. The test-value audit removes the isolated re-retirement-token/epoch, activity-cutoff, idle-reattach and sibling-restore examples. The cited exact retirement and chained-detach cases remain unchanged, alongside closed-tab revival fencing and alias churn bounds. Their retained evidence is not equivalent coverage for every removed permutation; historical execution evidence is unchanged. The test-value audit removed shared-owner snapshot/cold-restore reference examples, a direct absent-tab PTY-index update, caller-supplied host-owned teardown boolean/no-dispatch, and a single local-provider rejection example. It also removed hand-built detach-retention comparator examples for different runs, handles, provider identities, and handle-less rows without independent identity ingress. Their specific snapshot, missing-tab, host-decision, rejection-count, and comparator alarms are lost. Current exact authority/ordering and chained-detach cases remain retained; they do not establish equivalent coverage for the removed examples. Historical commands, counts, results, and red-green narratives remain historical. The test-value audit retires terminals-explicit-empty-hydration.test.ts in full: its direct git/folder empty-tombstone assertions and literal-flag auto-create helper check are lost. The store test never mounted the initial-terminal effect or performed a persisted restart. The audit also removes terminal-tab-retirement.test.ts pure ownership/alias/routing plan inventories and sleeping-record map assertions; its former 100-tab scan-count case was subsequently retired too; no assertion remains in that suite. These retained and historical cases do not establish equivalent tombstone, default-create, provider teardown, cross-platform, or mobile coverage. Historical commands, results, counts, and red-green narratives remain historical. The subsequent test-value audit deletes the remaining terminal-tab-retirement.test.ts suite. Its 100-tab case asserted only returned plan count and one outer scan per ownership store; it never inspected per-tab plans or provider teardown. Those direct cardinality/work-count alarms are intentionally lost. Historical executed commands, results, counts and red-green narratives remain historical; no equivalent replacement or new runtime, platform or mobile evidence is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1392: src/main/agent-hooks/server-pane-authority.test.ts reduced. Retires one duplicate-transfer no-op listener count and latest pty/timestamp callback fixture. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1674 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1630 removes low-value Git, GitHub/GitLab and IPC projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Live macOS Electron tests prove exact local PTY disappearance after parked-tab close and detached-pgid descendant death after agent close. A deterministic final-pane interleaving now proves durable tab retirement commits before externally owned PTY teardown can publish graph loss, and preserves an unrelated live canary through duplicate exit retirement. Deterministic tests also cover final-pane CLI close durability and same-incarnation handle continuity; persisted-empty restart suppression remains historical Electron evidence, alongside replacement and hard graph-loss stale fencing, preallocated-handle supersession, daemon and SSH routing, local/daemon pending-snapshot ownership across natural exit, stale-root descendant-signal suppression, graceful-to-immediate kill upgrades, duplicate-kill completion sharing, locale-stable bounded/fresh/coalesced process-table reads, deadline-safe successor scans, cycle-safe linear descendant traversal, target-only escalation indexing, 32-wide bulk teardown, source-scan timestamp identity, same-second PID ambiguity, ordinary runtime close ownership, unified-only hydration, split ownership, pane detach transfer, restart alias hydration, and late-hook suppression; live Linux, Windows, WSL, SSH, and remote-runtime process evidence remains pending. A paired macOS Electron journey (real attached-window desktop host plus a separate real paired desktop client, isolated profiles) covers the inverse authority direction for host-created terminals: a renderer graph sync must not retire a tab that `orca terminal create` made. The retained paired journey covers the incident shape the real CLI produces - a repo's FIRST host-created terminal, since src/cli/handlers/terminal.ts sends no clientMutationId. The repeated target-after-unrelated-terminal journey was retired; its separate ordering alarm is no longer provided. An earlier reading of this gate called a preceding host-created terminal and a `clientMutationId` protective; main-process instrumentation refuted that. The renderer's stale session write deleted the host tab from persistence in every variant, and only the delete's timing relative to the measured sync differed, so those rows were false negatives of a single-sync oracle. The defect was in persistence: persistPtyBinding did not raise the repo's terminal topology fence for the first host-admitted tab, leaving the renderer's pre-create tab list free to replay over it. Elapsed time and paired-client mirroring never decided anything. A windowless `orca serve` parity arm runs the identical oracle and passes on both the merge-base and the branch, confirming that topology does not exhibit the defect (the gate it turns on is window-conditioned) and that making the host binding unconditional did not regress it. All of this is macOS-local only. The reported incident occurred on a physical Windows host; we did not run against one. The claim that the mechanism is platform-independent rests on the authoritative-window gate and publication-epoch inheritance being OS-agnostic, not on Windows evidence, so no ConPTY, Windows window-lifecycle, update, or sleep behavior is covered here. The synthetic default-tabs unit suite was retired: its explicit-empty/default-tab assertions no longer supply unit evidence. This retirement supplies no new persisted-restart, runtime-routing, cross-platform, or mobile evidence; existing historical runs and counts remain historical. The test-value audit removes the isolated re-retirement-token/epoch, activity-cutoff, idle-reattach and sibling-restore examples. The cited exact retirement and chained-detach cases remain unchanged, alongside closed-tab revival fencing and alias churn bounds. Their retained evidence is not equivalent coverage for every removed permutation; historical execution evidence is unchanged. The test-value audit removed shared-owner snapshot/cold-restore reference examples, a direct absent-tab PTY-index update, caller-supplied host-owned teardown boolean/no-dispatch, and a single local-provider rejection example. It also removed hand-built detach-retention comparator examples for different runs, handles, provider identities, and handle-less rows without independent identity ingress. Their specific snapshot, missing-tab, host-decision, rejection-count, and comparator alarms are lost. Current exact authority/ordering and chained-detach cases remain retained; they do not establish equivalent coverage for the removed examples. Historical commands, counts, results, and red-green narratives remain historical. The test-value audit retires terminals-explicit-empty-hydration.test.ts in full: its direct git/folder empty-tombstone assertions and literal-flag auto-create helper check are lost. The store test never mounted the initial-terminal effect or performed a persisted restart. The audit also removes terminal-tab-retirement.test.ts pure ownership/alias/routing plan inventories and sleeping-record map assertions; its former 100-tab scan-count case was subsequently retired too; no assertion remains in that suite. These retained and historical cases do not establish equivalent tombstone, default-create, provider teardown, cross-platform, or mobile coverage. Historical commands, results, counts, and red-green narratives remain historical. The subsequent test-value audit deletes the remaining terminal-tab-retirement.test.ts suite. Its 100-tab case asserted only returned plan count and one outer scan per ownership store; it never inspected per-tab plans or provider teardown. Those direct cardinality/work-count alarms are intentionally lost. Historical executed commands, results, counts and red-green narratives remain historical; no equivalent replacement or new runtime, platform or mobile evidence is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1392: src/main/agent-hooks/server-pane-authority.test.ts reduced. Retires one duplicate-transfer no-op listener count and latest pty/timestamp callback fixture. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-4327", "https://github.com/stablyai/orca/pull/14590", @@ -6174,7 +6174,7 @@ "https://github.com/stablyai/orca/issues/14524" ], "invariant": "Close permanently removes the owned provider session, agent descendants, persisted tab/layout authority, and resume authority even when no TerminalPane is mounted; final-pane CLI close commits durable tab retirement before PTY stop can publish graph loss, does not acknowledge before that retirement, and treats the later exit retirement as idempotent. Workspace-wide CLI close applies that same durable contract to every terminal in exactly one worktree, including pinned and persisted-only surfaces, without touching sibling-worktree PTYs or resume records. A persisted explicit-empty state prevents initial-tab fallback from repopulating the workspace after reload or restart. A handle remains valid while the same provider-attested PTY incarnation survives renderer reload or re-key, and becomes stale without adopting a replacement incarnation. A terminating id remains reserved through natural exit, duplicate callers await the same completion, and immediate teardown upgrades any graceful request without signalling a recycled PID or a descendant tree after root ownership is lost; process-table work is locale-stable, bounded, fresh for each post-start request, same-turn coalesced, and begins within the requesting caller's deadline, including bulk worktree cleanup; detach and park preserve ownership; aliases prevent a detached agent's immutable physical pane key from being retired with its former tab.", - "oracle": "Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1674 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Capture exact tab, pane, PTY, handle, incarnation, and persisted layout identities. Final-pane terminal.close must invoke one durability-acknowledged tab retirement before exact PTY teardown; controlled PTY exit and graph removal afterward must remain idempotent, omit the target from persistence and provider inventory, and leave an unrelated canary handle live. terminal.closeAll must force-retire every pinned and unpinned target surface, clear its persisted agent-resume and incarnation records, await authoritative PTY exit, preserve a sibling worktree's tab, PTY, and resume authority, and return an unverifiable failure instead of claiming exit when the owning host does not confirm a stop. The ordinary path stays pending until the persisted row is removed and never falls back to the fire-and-forget pane event; reload/restart must keep the explicitly emptied worktree at zero terminal tabs and provider sessions. Reloading the renderer with the same tab/pane/PTY/incarnation must keep the handle readable; changing only the incarnation behind the same PTY id, losing the authoritative graph, or superseding the renderer handle with a preallocated handle must reject the old handle with terminal_handle_stale. Parked-close tests prove the exact PTY disappears; descendant/process tests keep natural exits reserved, upgrade teardown safely, bound/coalesce process-table work, and protect recycled identities.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1674 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Capture exact tab, pane, PTY, handle, incarnation, and persisted layout identities. Final-pane terminal.close must invoke one durability-acknowledged tab retirement before exact PTY teardown; controlled PTY exit and graph removal afterward must remain idempotent, omit the target from persistence and provider inventory, and leave an unrelated canary handle live. terminal.closeAll must force-retire every pinned and unpinned target surface, clear its persisted agent-resume and incarnation records, await authoritative PTY exit, preserve a sibling worktree's tab, PTY, and resume authority, and return an unverifiable failure instead of claiming exit when the owning host does not confirm a stop. The ordinary path stays pending until the persisted row is removed and never falls back to the fire-and-forget pane event; reload/restart must keep the explicitly emptied worktree at zero terminal tabs and provider sessions. Reloading the renderer with the same tab/pane/PTY/incarnation must keep the handle readable; changing only the incarnation behind the same PTY id, losing the authoritative graph, or superseding the renderer handle with a preallocated handle must reject the old handle with terminal_handle_stale. Parked-close tests prove the exact PTY disappears; descendant/process tests keep natural exits reserved, upgrade teardown safely, bound/coalesce process-table work, and protect recycled identities.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/terminal/surface/orca-runtime-terminal-close-continuity.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts", @@ -6385,7 +6385,7 @@ "providers": ["daemon", "runtime", "ssh", "wsl"], "coveredPlatforms": ["linux", "macos", "windows"], "coveredProviders": ["daemon", "runtime"], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3881 intentionally removes the entire legacy E2EE channel and mobile socket-wiring test suites and the v2 additive runtime-capability refusal callback, without equivalent replacement. Those deleted suites leave current selectors and that v2 assertion alias is retired. Retained v2 replay/queue-counter cases do not establish the removed capability refusal. Reduced test bytes/selectors have no fresh execution in this static packet; independent dated daemon E2E receipts retain their complete original scope. Historical prior account (preserved verbatim): Git owner cleanup retires the mixed request-connection suite under repository test policy, including its encrypted request cancellation and cached paired-desktop authentication alarms, without equivalent replacement. Current commands and assertion references omit the retired suite; dated evidence retains its original scope and does not validate this reduced set. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2153: Reduced sources intentionally lose the forwarding smoke and retire an unrelated current selector; historical runs do not validate changed source bytes. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2020: The CLI WebSocket source retains old-protocol refusal only; its paired-open, capability, timeout and admission projection examples are removed. No daemon-generation coverage is transferred to that callback. Both independent daemon E2E receipts remain unchanged at their original commands; the reduced unit selector has no fresh result. Historical prior account (preserved verbatim): Audit1983: Delete the reasonless-close scalar suite and optional-capability socket forwarding alias. Delete the mocked close-attribution topology field-inventory suite and its aliases/selectors; preserve independent dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1959 (sources 1957:11, 1957:35, 1957:36): The fixture-supplied repository roundtrip, isolated capability metadata readback, parser/default-timer/size-threshold/internal failure-counter examples and standalone handshake-only replay example are removed. Source11 retains real encrypted multi-client event ordering, reconnect snapshot and multiplex delivery under synthetic runtime state, and does not establish the broad handshake-to-reasonless-close refusal alias; retire that whole assertion object. Source36 capability metadata readback alias is retired. Source35 retains the actual v2 runtime-only metadata/downgrade refusal callback and captured mutating-trace fresh-nonce rejection; its mapped mobile-v2 refusal alias remains within modeled credential lookup scope. Both independent daemon E2E dated receipts remain byte-exact with their original commands and results. No historical passed receipt is narrowed or transferred to changed source bytes. No fresh execution, equivalent replacement, physical provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed by this metadata packet. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Retain actual parked exit/disposal/cancel/leaf progression using injected exit and close adapters. The retained last-watcher case checks exact lifecyclePtyId, but no retained callback establishes the whole existing replacement-or-sibling-incarnation alias. Retire that broad assertion object rather than transfer partial support. Capability admission, static fields and duplicate routing examples are intentionally lost; unchanged daemon E2E receipts remain scoped to their own commands. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): The browser auth capability-advertisement case is removed in this audit; its direct encrypted-auth literal alarm is lost. Retained lifecycle evidence is not an equivalent replacement. Historical run commands and results remain historical. Recorded native Windows evidence covers the v21/v22/v23/v24/v25 named-pipe matrix. The deterministic daemon harness covers capable and legacy paired-runtime request shapes against live v25/v26 PTYs in separate worktrees, plus an unrelated control. Host/renderer tests cover old servers, missing liveness, stale publications, reused claims, split parents, explicit user intent, cross-profile isolation, remote runtime clients, SSH-provider routing, and WSL boundaries. A headed host paired to a separate live client, headless serve parity, Linux, SSH, and WSL remain explicit gaps. The audit removed private-router tests of mocked kill results and log fields. Their isolated success/failure attribution and pending-spawn tolerance alarms are no longer registered here; live lifecycle evidence does not establish equivalent log coverage. The isolated close-intent suite was removed. web-runtime-session.test.ts no longer supplies the lifecycle-close or old-server method-not-found assertions formerly referenced here. Their direct partition and RPC-shape alarms are lost; retained live daemon evidence does not establish equivalent coverage. The broad mirror fixture retains user-close suppression, refused-close reconciliation and optimistic reorder only. Its earlier reconnect/incarnation and projection examples no longer establish current daemon-generation coverage. Those narrow alarms are lost; other daemon evidence is not equivalent replacement coverage. Audit1318: src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts: Retain real local encrypted WebSocket regressions for pre-ready socket replacement preserving a request, close-before-ready logical subscription cleanup, unrelated RPC surviving another deadline, and pending setup abort leaving a sibling pending. Host server is synthetic and uses production crypto; kept scenarios also retain internal diagnostics/memory assertions unchanged. Remove happy routing, authority-envelope, unknown-ID/log, cleanup enum, binary, keepalive/liveness and production-budget/private socket inventories; intentionally lose those alarms, no physical host authorization, independent crypto or remote provider claim. Reduced selectors need fresh validation; historical full-suite receipts do not validate the changed coverage. No equivalent replacement or physical-provider claim. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3881 intentionally removes the entire legacy E2EE channel and mobile socket-wiring test suites and the v2 additive runtime-capability refusal callback, without equivalent replacement. Those deleted suites leave current selectors and that v2 assertion alias is retired. Retained v2 replay/queue-counter cases do not establish the removed capability refusal. Reduced test bytes/selectors have no fresh execution in this static packet; independent dated daemon E2E receipts retain their complete original scope. Historical prior account (preserved verbatim): Git owner cleanup retires the mixed request-connection suite under repository test policy, including its encrypted request cancellation and cached paired-desktop authentication alarms, without equivalent replacement. Current commands and assertion references omit the retired suite; dated evidence retains its original scope and does not validate this reduced set. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2153: Reduced sources intentionally lose the forwarding smoke and retire an unrelated current selector; historical runs do not validate changed source bytes. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2020: The CLI WebSocket source retains old-protocol refusal only; its paired-open, capability, timeout and admission projection examples are removed. No daemon-generation coverage is transferred to that callback. Both independent daemon E2E receipts remain unchanged at their original commands; the reduced unit selector has no fresh result. Historical prior account (preserved verbatim): Audit1983: Delete the reasonless-close scalar suite and optional-capability socket forwarding alias. Delete the mocked close-attribution topology field-inventory suite and its aliases/selectors; preserve independent dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1959 (sources 1957:11, 1957:35, 1957:36): The fixture-supplied repository roundtrip, isolated capability metadata readback, parser/default-timer/size-threshold/internal failure-counter examples and standalone handshake-only replay example are removed. Source11 retains real encrypted multi-client event ordering, reconnect snapshot and multiplex delivery under synthetic runtime state, and does not establish the broad handshake-to-reasonless-close refusal alias; retire that whole assertion object. Source36 capability metadata readback alias is retired. Source35 retains the actual v2 runtime-only metadata/downgrade refusal callback and captured mutating-trace fresh-nonce rejection; its mapped mobile-v2 refusal alias remains within modeled credential lookup scope. Both independent daemon E2E dated receipts remain byte-exact with their original commands and results. No historical passed receipt is narrowed or transferred to changed source bytes. No fresh execution, equivalent replacement, physical provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed by this metadata packet. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Retain actual parked exit/disposal/cancel/leaf progression using injected exit and close adapters. The retained last-watcher case checks exact lifecyclePtyId, but no retained callback establishes the whole existing replacement-or-sibling-incarnation alias. Retire that broad assertion object rather than transfer partial support. Capability admission, static fields and duplicate routing examples are intentionally lost; unchanged daemon E2E receipts remain scoped to their own commands. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): The browser auth capability-advertisement case is removed in this audit; its direct encrypted-auth literal alarm is lost. Retained lifecycle evidence is not an equivalent replacement. Historical run commands and results remain historical. Recorded native Windows evidence covers the v21/v22/v23/v24/v25 named-pipe matrix. The deterministic daemon harness covers capable and legacy paired-runtime request shapes against live v25/v26 PTYs in separate worktrees, plus an unrelated control. Host/renderer tests cover old servers, missing liveness, stale publications, reused claims, split parents, explicit user intent, cross-profile isolation, remote runtime clients, SSH-provider routing, and WSL boundaries. A headed host paired to a separate live client, headless serve parity, Linux, SSH, and WSL remain explicit gaps. The audit removed private-router tests of mocked kill results and log fields. Their isolated success/failure attribution and pending-spawn tolerance alarms are no longer registered here; live lifecycle evidence does not establish equivalent log coverage. The isolated close-intent suite was removed. web-runtime-session.test.ts no longer supplies the lifecycle-close or old-server method-not-found assertions formerly referenced here. Their direct partition and RPC-shape alarms are lost; retained live daemon evidence does not establish equivalent coverage. The broad mirror fixture retains user-close suppression, refused-close reconciliation and optimistic reorder only. Its earlier reconnect/incarnation and projection examples no longer establish current daemon-generation coverage. Those narrow alarms are lost; other daemon evidence is not equivalent replacement coverage. Audit1318: src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts: Retain real local encrypted WebSocket regressions for pre-ready socket replacement preserving a request, close-before-ready logical subscription cleanup, unrelated RPC surviving another deadline, and pending setup abort leaving a sibling pending. Host server is synthetic and uses production crypto; kept scenarios also retain internal diagnostics/memory assertions unchanged. Remove happy routing, authority-envelope, unknown-ID/log, cleanup enum, binary, keepalive/liveness and production-budget/private socket inventories; intentionally lose those alarms, no physical host authorization, independent crypto or remote provider claim. Reduced selectors need fresh validation; historical full-suite receipts do not validate the changed coverage. No equivalent replacement or physical-provider claim. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/9749", "https://github.com/stablyai/orca/issues/9949", @@ -6394,7 +6394,7 @@ "https://github.com/stablyai/orca/issues/9229" ], "invariant": "Reconnect, replay, or lifecycle observations from a viewer that negotiated explicit close intent must never kill a live PTY. A capable reasonless close must keep and republish; a legacy paired viewer must retain current-main behavior because its intentional close and cleanup echo are wire-identical. Lifecycle close requires the exact observed publication, terminal, environment, and authoritative liveness, never signals a process, and leaves renderer-owned or partial-split retirement to its owner. Legacy daemon hello and warm reattachment remain non-destructive.", - "oracle": "Audit3881 intentionally removes the entire legacy E2EE channel and mobile socket-wiring test suites and the v2 additive runtime-capability refusal callback, without equivalent replacement. Those deleted suites leave current selectors and that v2 assertion alias is retired. Retained v2 replay/queue-counter cases do not establish the removed capability refusal. Reduced test bytes/selectors have no fresh execution in this static packet; independent dated daemon E2E receipts retain their complete original scope. Historical prior account (preserved verbatim): Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3774 (independent peer expansion of3773): deliberately retire7authored/13potential expanded registrations across4whole suites: standalone truncated8-byte E2EE guard; open/empty readiness null fast-path; capability parser acceptance/atomic malformed-array bounds; phone/delegated-parent/desktop/in-process creator provenance projections. These are real guard/policy alarms accepted as low marginal value, not tautologies. No equivalent replacement, authenticated-refusal, observer delivery, persisted visibility, synchronous scheduling or fresh execution is claimed. Prior dated receipts retain their exact original scopes and do not validate the changed/reduced current selector. Independent daemon E2E receipts remain scoped to their own unchanged commands. Historical prior account (preserved verbatim): Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1983: Delete the reasonless-close scalar suite and optional-capability socket forwarding alias. Delete the mocked close-attribution topology field-inventory suite and its aliases/selectors; preserve independent dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1959 (sources 1957:11, 1957:35, 1957:36): The fixture-supplied repository roundtrip, isolated capability metadata readback, parser/default-timer/size-threshold/internal failure-counter examples and standalone handshake-only replay example are removed. Source11 retains real encrypted multi-client event ordering, reconnect snapshot and multiplex delivery under synthetic runtime state, and does not establish the broad handshake-to-reasonless-close refusal alias; retire that whole assertion object. Source36 capability metadata readback alias is retired. Source35 retains the actual v2 runtime-only metadata/downgrade refusal callback and captured mutating-trace fresh-nonce rejection; its mapped mobile-v2 refusal alias remains within modeled credential lookup scope. Both independent daemon E2E dated receipts remain byte-exact with their original commands and results. No historical passed receipt is narrowed or transferred to changed source bytes. No fresh execution, equivalent replacement, physical provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed by this metadata packet. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Retain actual parked exit/disposal/cancel/leaf progression using injected exit and close adapters. The retained last-watcher case checks exact lifecyclePtyId, but no retained callback establishes the whole existing replacement-or-sibling-incarnation alias. Retire that broad assertion object rather than transfer partial support. Capability admission, static fields and duplicate routing examples are intentionally lost; unchanged daemon E2E receipts remain scoped to their own commands. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Start isolated v25 and v26 daemon generations with one capable-viewer PTY and one legacy-viewer PTY per generation in four target worktrees plus an unaddressed control PTY in a fifth worktree. Route them through the production desktop scanner, runtime, RPC dispatcher, renderer-close relay, and daemon router. First issue sequential reasonless closes from an authenticated capable connection and require refusal, snapshot republish, zero shutdown calls, exact process survival, and post-close I/O. Then issue the byte-identical requests without the negotiated capability and require current-main behavior: two ordered immediate shutdowns, session-killed events, and exact root/descendant death, while capable and control PTYs survive. An observer lists all targets before, between, and after while issuing zero closes. Unit contracts also require in-process reasonless refusal, legacy runtime/mobile compatibility, explicit user closes, encrypted client-auth advertisement, and old-server lifecycle calls never to fall back.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3881 intentionally removes the entire legacy E2EE channel and mobile socket-wiring test suites and the v2 additive runtime-capability refusal callback, without equivalent replacement. Those deleted suites leave current selectors and that v2 assertion alias is retired. Retained v2 replay/queue-counter cases do not establish the removed capability refusal. Reduced test bytes/selectors have no fresh execution in this static packet; independent dated daemon E2E receipts retain their complete original scope. Historical prior account (preserved verbatim): Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3774 (independent peer expansion of3773): deliberately retire7authored/13potential expanded registrations across4whole suites: standalone truncated8-byte E2EE guard; open/empty readiness null fast-path; capability parser acceptance/atomic malformed-array bounds; phone/delegated-parent/desktop/in-process creator provenance projections. These are real guard/policy alarms accepted as low marginal value, not tautologies. No equivalent replacement, authenticated-refusal, observer delivery, persisted visibility, synchronous scheduling or fresh execution is claimed. Prior dated receipts retain their exact original scopes and do not validate the changed/reduced current selector. Independent daemon E2E receipts remain scoped to their own unchanged commands. Historical prior account (preserved verbatim): Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1983: Delete the reasonless-close scalar suite and optional-capability socket forwarding alias. Delete the mocked close-attribution topology field-inventory suite and its aliases/selectors; preserve independent dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1959 (sources 1957:11, 1957:35, 1957:36): The fixture-supplied repository roundtrip, isolated capability metadata readback, parser/default-timer/size-threshold/internal failure-counter examples and standalone handshake-only replay example are removed. Source11 retains real encrypted multi-client event ordering, reconnect snapshot and multiplex delivery under synthetic runtime state, and does not establish the broad handshake-to-reasonless-close refusal alias; retire that whole assertion object. Source36 capability metadata readback alias is retired. Source35 retains the actual v2 runtime-only metadata/downgrade refusal callback and captured mutating-trace fresh-nonce rejection; its mapped mobile-v2 refusal alias remains within modeled credential lookup scope. Both independent daemon E2E dated receipts remain byte-exact with their original commands and results. No historical passed receipt is narrowed or transferred to changed source bytes. No fresh execution, equivalent replacement, physical provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed by this metadata packet. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Retain actual parked exit/disposal/cancel/leaf progression using injected exit and close adapters. The retained last-watcher case checks exact lifecyclePtyId, but no retained callback establishes the whole existing replacement-or-sibling-incarnation alias. Retire that broad assertion object rather than transfer partial support. Capability admission, static fields and duplicate routing examples are intentionally lost; unchanged daemon E2E receipts remain scoped to their own commands. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Start isolated v25 and v26 daemon generations with one capable-viewer PTY and one legacy-viewer PTY per generation in four target worktrees plus an unaddressed control PTY in a fifth worktree. Route them through the production desktop scanner, runtime, RPC dispatcher, renderer-close relay, and daemon router. First issue sequential reasonless closes from an authenticated capable connection and require refusal, snapshot republish, zero shutdown calls, exact process survival, and post-close I/O. Then issue the byte-identical requests without the negotiated capability and require current-main behavior: two ordered immediate shutdowns, session-killed events, and exact root/descendant death, while capable and control PTYs survive. An observer lists all targets before, between, and after while issuing zero closes. Unit contracts also require in-process reasonless refusal, legacy runtime/mobile compatibility, explicit user closes, encrypted client-auth advertisement, and old-server lifecycle calls never to fall back.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts src/shared/remote-runtime/remote-runtime-client.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/cli/runtime/websocket-transport.test.ts src/renderer/src/web/web-runtime-client.test.ts src/renderer/src/components/terminal/terminal-tab-actions.test.ts src/renderer/src/components/terminal/terminal-close-incarnation.test.ts src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts", "pnpm exec playwright test tests/e2e/daemon-generation-reconnect-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1", @@ -7040,7 +7040,7 @@ "providers": ["local", "daemon", "ssh", "wsl", "remote-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": ["local", "daemon", "ssh", "wsl", "remote-runtime"], - "coverageNotes": "Audit4352 deletes the whole mocked daemon router, session-owner resolver, degraded-provider, terminal-host CWD-readability and foreground-inspection suites plus the native-process-CWD fixture suite; recovery retains only public multi-pane proactive socket-death notification/remount. Intentionally lose retirement admission, stale-inventory/fenced-kill authority, attach-only re-resolution/absence, fast-exit routing, sleep CWD checkpoint/wake, diagnostic cancellation/shutdown and foreground census/session-change alarms. No equivalent replacement or fresh gate execution is claimed. Historical prior account (preserved verbatim): Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. The Codex restart suite and exact command/testFiles/assertionRef are removed; its mock account-change/remote-pane restart alarms are intentionally lost. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3368 retires the whole host-session-launch unit source, including host-authority/create/ensure/legacy/Kimi/refusal/draft/options/prompt/cancel/kill/shared-disconnect and passive-sleep output callback alarms, without equivalent replacement. Its unit command token and test-file entry retire. Five unrelated macOS receipts remain verbatim and keep only their original scope; no fresh changed-selector/runtime/provider/process/UI/mobile/mixed-version/cross-platform evidence is claimed. Audit3359 retires the whole create-handoff suite. Supplied disposition, late completion, close-callback and metadata-bookkeeping alarms are intentionally lost without equivalent replacement; the compound unit selector and test-file entry narrow accordingly. Five unrelated macOS receipts retain their original scope. No fresh runtime, process-deduplication, physical provider/network, historical-import, mobile, mixed-version or cross-platform validation is claimed. Audit 3301 removes the caller-chat-target precedence over host selection example from web-session-tabs-sync-focus-intent.test.ts. The snapshot-before-RPC replay and split-sibling-first exact-leaf focus cases remain unchanged and match the existing assertion reference. Preserve current selectors, assertion references, unrelated active receipts and complete archived history. These mocked state-composition survivors provide no equivalent replacement, rendered, native, network or fresh execution proof. Audit2694 source-only review: Remove14 authored/14 expanded cases in the renderer completion wrapper family, including the complete bell/query-reply/stale-input factory, without replacement. Retain29 authored/34 expanded cases with unchanged callback bodies. Three removals are the existing pending2203 overlap and must be adopted/count-reconciled once; eleven are fresh removals. No current-source runtime or gate execution is supplied by this packet; dated evidence retains its original source scope and does not validate the reduced factory bytes. No equivalent replacement, physical provider, mixed-version, mobile, rendered UI, cross-platform, soak or measured-performance result is claimed. The existing command and testFiles selector retain tests/pty-connection-completion-and-notification.test.ts; no wrapper-specific assertionRef is removed. Historical selector results do not validate its changed reachable factories. Historical prior account (preserved verbatim): Audit2709 removes src/renderer/src/runtime/web-session-tabs-sync/inventory/remote-runtime-session-tabs-inflight.test.ts: intentionally retire waiting out an older inventory and sharing a fresh post-operation request among concurrent callers. No equivalent replacement or fresh execution is claimed. Dated receipts remain historical evidence for their unchanged selectors only; they do not validate the reduced commands or full current gate. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2003: Delete the complete pure orphan-topology suite and its file membership; preserve the other selectors from its combined command after archiving the original command string. Lost alarms are synthetic current-layout preservation, one-anchor grafting and ambiguous-anchor appending examples. Do not transfer their ownership/topology scope to sibling suites. Preserve all5 independent historical receipts exactly. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1983: Delete the mocked agent-session suite and its prospective selector and aliases. Retire all3 whole dated changed-selector receipts, preserving their original raw objects in historical gaps without transferring counts or durations. Preserve5 unrelated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial agent-operation service suite retains only concurrent retry joining with changed-fingerprint conflict. Retire its complete three-title assertion object: old-owner legacy fallback before side effects, nested SSH Pi transcript refusal and completed/ambiguous physical-commit replay fences are no longer backed. Other probe/launch option/ledger/failure/remote-inventory examples disappear as detailed below. No actual provider spawn, live SSH or physical commit proof is retained by this source. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1911: The complete provider PTY-inspection mock suite is removed: missing-provider rejection, stale-PTY renderer unverifiable mapping, preserved supplied renderer failures, and supplied transport-loss mapping alarms are intentionally lost. No actual terminal teardown, daemon error or remote transport was exercised. Only the deleted explicit source tokens, testFile and whole assertionRef are removed. Meaningful remaining source selectors/assertions and unaffected dated receipts retain their original scope. No equivalent replacement, fresh reduced-gate, native/platform/provider, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1495 intentionally deletes the orphan-owner suite: explicit owner/host predicate examples are retired; no equivalent replacement coverage or fresh result is claimed. Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally deletes src/renderer/src/components/terminal-pane/agent-completion-coordinator-pending-title-inspection.test.ts: Loses obsolete pending title process races, inspection failure suppression, explicit completion precedence and delayed remote inspection alarms. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1342 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audits1301 and1308 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Deterministic macOS tests cover controller claims, daemon and SSH/relay operation replay, mixed-version selection, runtime ownership, exact provisional handoff, durable terminal retirement, two independent viewer mirrors, guarded adoption of legacy live PTYs, and completion classification when either the outer remote transport or authoritative host/provider process inspection becomes unreachable. The adoption harness models v1.4.150 agent/setup/shell tabs, current-generation restart and reconnect, exact handle/incarnation/worktree/host checks, topology CAS, competing clients, split-pane/group restoration, WSL ownership, and SSH owner rejection. The secondary parity repro runs independent clients against one headless remote Orca runtime over encrypted pairing and a real daemon-backed PTY, with tokened fixture-process identity separated from unrelated Codex app-server startup probes. The automated primary topology runs an isolated headed macOS Orca desktop server plus a separate paired web client and proves viewer-local fresh/resume focus, exact legacy placement, writable PTYs, unrelated-terminal survival, and host/client cleanup. SSH coverage is provider/relay contract and fault-injection coverage only; it does not substitute for paired-server coverage. Live Windows, Linux, WSL, SSH, and physical paired-Linux hosts remain gaps. The isolated intent-owner ledger suite was removed. web-runtime-session.test.ts retains exact post-create handoff confirmation only; its previous fresh/resume presentation and publication-replay assertions are no longer current references. Those direct owner-partition, cap and RPC/focus alarms are lost; other agent-session evidence does not establish equivalence. Structured handoff and adopted-leaf focus references now point to their dedicated surviving fixtures. Mixed-version recovery retains actual old-host recovery followed by a new CLI tab; its unsupported/missing-evidence tables are removed. Orphan recovery retains in-flight adoption-conflict serialization only; previous topology/pruning and absence/adoption examples are removed. These removed projection, capability and adoption-shape alarms are lost; retained cases do not replace them. Audit1301: src/renderer/src/runtime/remote-runtime-session-tabs-inflight.test.ts reduced. Direct dedupe eviction/count and equal receipt-position sample loses authored alarm. No equivalent replacement coverage is claimed. Audit1308: src/renderer/src/runtime/web-session-tabs-sync-agent-handoff.test.ts removed. Exact provisional replacement and post-create exit confirmation handoff pipeline alarms disappear. No equivalent replacement coverage is claimed. Audit1313: src/main/runtime/terminal-orphan-topology.test.ts: Retain actual topology merge algorithms preserving disjoint subtrees and grafted/ambiguous shared anchors with independent expected trees. Remove duplicate leaf validator and empty/no-change passthrough examples. Lose basic validation and no-group/noop characterization; no renderer layout proof. Reduced selectors require fresh validation; prior full-suite receipts do not validate removed or changed coverage. No equivalent replacement or physical-provider claim. Audit1342: src/renderer/src/components/terminal-pane/pty-connection-completion-and-notification.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4352 deletes the whole mocked daemon router, session-owner resolver, degraded-provider, terminal-host CWD-readability and foreground-inspection suites plus the native-process-CWD fixture suite; recovery retains only public multi-pane proactive socket-death notification/remount. Intentionally lose retirement admission, stale-inventory/fenced-kill authority, attach-only re-resolution/absence, fast-exit routing, sleep CWD checkpoint/wake, diagnostic cancellation/shutdown and foreground census/session-change alarms. No equivalent replacement or fresh gate execution is claimed. Historical prior account (preserved verbatim): Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. The Codex restart suite and exact command/testFiles/assertionRef are removed; its mock account-change/remote-pane restart alarms are intentionally lost. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3368 retires the whole host-session-launch unit source, including host-authority/create/ensure/legacy/Kimi/refusal/draft/options/prompt/cancel/kill/shared-disconnect and passive-sleep output callback alarms, without equivalent replacement. Its unit command token and test-file entry retire. Five unrelated macOS receipts remain verbatim and keep only their original scope; no fresh changed-selector/runtime/provider/process/UI/mobile/mixed-version/cross-platform evidence is claimed. Audit3359 retires the whole create-handoff suite. Supplied disposition, late completion, close-callback and metadata-bookkeeping alarms are intentionally lost without equivalent replacement; the compound unit selector and test-file entry narrow accordingly. Five unrelated macOS receipts retain their original scope. No fresh runtime, process-deduplication, physical provider/network, historical-import, mobile, mixed-version or cross-platform validation is claimed. Audit 3301 removes the caller-chat-target precedence over host selection example from web-session-tabs-sync-focus-intent.test.ts. The snapshot-before-RPC replay and split-sibling-first exact-leaf focus cases remain unchanged and match the existing assertion reference. Preserve current selectors, assertion references, unrelated active receipts and complete archived history. These mocked state-composition survivors provide no equivalent replacement, rendered, native, network or fresh execution proof. Audit2694 source-only review: Remove14 authored/14 expanded cases in the renderer completion wrapper family, including the complete bell/query-reply/stale-input factory, without replacement. Retain29 authored/34 expanded cases with unchanged callback bodies. Three removals are the existing pending2203 overlap and must be adopted/count-reconciled once; eleven are fresh removals. No current-source runtime or gate execution is supplied by this packet; dated evidence retains its original source scope and does not validate the reduced factory bytes. No equivalent replacement, physical provider, mixed-version, mobile, rendered UI, cross-platform, soak or measured-performance result is claimed. The existing command and testFiles selector retain tests/pty-connection-completion-and-notification.test.ts; no wrapper-specific assertionRef is removed. Historical selector results do not validate its changed reachable factories. Historical prior account (preserved verbatim): Audit2709 removes src/renderer/src/runtime/web-session-tabs-sync/inventory/remote-runtime-session-tabs-inflight.test.ts: intentionally retire waiting out an older inventory and sharing a fresh post-operation request among concurrent callers. No equivalent replacement or fresh execution is claimed. Dated receipts remain historical evidence for their unchanged selectors only; they do not validate the reduced commands or full current gate. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2003: Delete the complete pure orphan-topology suite and its file membership; preserve the other selectors from its combined command after archiving the original command string. Lost alarms are synthetic current-layout preservation, one-anchor grafting and ambiguous-anchor appending examples. Do not transfer their ownership/topology scope to sibling suites. Preserve all5 independent historical receipts exactly. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1983: Delete the mocked agent-session suite and its prospective selector and aliases. Retire all3 whole dated changed-selector receipts, preserving their original raw objects in historical gaps without transferring counts or durations. Preserve5 unrelated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial agent-operation service suite retains only concurrent retry joining with changed-fingerprint conflict. Retire its complete three-title assertion object: old-owner legacy fallback before side effects, nested SSH Pi transcript refusal and completed/ambiguous physical-commit replay fences are no longer backed. Other probe/launch option/ledger/failure/remote-inventory examples disappear as detailed below. No actual provider spawn, live SSH or physical commit proof is retained by this source. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1911: The complete provider PTY-inspection mock suite is removed: missing-provider rejection, stale-PTY renderer unverifiable mapping, preserved supplied renderer failures, and supplied transport-loss mapping alarms are intentionally lost. No actual terminal teardown, daemon error or remote transport was exercised. Only the deleted explicit source tokens, testFile and whole assertionRef are removed. Meaningful remaining source selectors/assertions and unaffected dated receipts retain their original scope. No equivalent replacement, fresh reduced-gate, native/platform/provider, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1495 intentionally deletes the orphan-owner suite: explicit owner/host predicate examples are retired; no equivalent replacement coverage or fresh result is claimed. Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally deletes src/renderer/src/components/terminal-pane/agent-completion-coordinator-pending-title-inspection.test.ts: Loses obsolete pending title process races, inspection failure suppression, explicit completion precedence and delayed remote inspection alarms. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1342 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audits1301 and1308 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Deterministic macOS tests cover controller claims, daemon and SSH/relay operation replay, mixed-version selection, runtime ownership, exact provisional handoff, durable terminal retirement, two independent viewer mirrors, guarded adoption of legacy live PTYs, and completion classification when either the outer remote transport or authoritative host/provider process inspection becomes unreachable. The adoption harness models v1.4.150 agent/setup/shell tabs, current-generation restart and reconnect, exact handle/incarnation/worktree/host checks, topology CAS, competing clients, split-pane/group restoration, WSL ownership, and SSH owner rejection. The secondary parity repro runs independent clients against one headless remote Orca runtime over encrypted pairing and a real daemon-backed PTY, with tokened fixture-process identity separated from unrelated Codex app-server startup probes. The automated primary topology runs an isolated headed macOS Orca desktop server plus a separate paired web client and proves viewer-local fresh/resume focus, exact legacy placement, writable PTYs, unrelated-terminal survival, and host/client cleanup. SSH coverage is provider/relay contract and fault-injection coverage only; it does not substitute for paired-server coverage. Live Windows, Linux, WSL, SSH, and physical paired-Linux hosts remain gaps. The isolated intent-owner ledger suite was removed. web-runtime-session.test.ts retains exact post-create handoff confirmation only; its previous fresh/resume presentation and publication-replay assertions are no longer current references. Those direct owner-partition, cap and RPC/focus alarms are lost; other agent-session evidence does not establish equivalence. Structured handoff and adopted-leaf focus references now point to their dedicated surviving fixtures. Mixed-version recovery retains actual old-host recovery followed by a new CLI tab; its unsupported/missing-evidence tables are removed. Orphan recovery retains in-flight adoption-conflict serialization only; previous topology/pruning and absence/adoption examples are removed. These removed projection, capability and adoption-shape alarms are lost; retained cases do not replace them. Audit1301: src/renderer/src/runtime/remote-runtime-session-tabs-inflight.test.ts reduced. Direct dedupe eviction/count and equal receipt-position sample loses authored alarm. No equivalent replacement coverage is claimed. Audit1308: src/renderer/src/runtime/web-session-tabs-sync-agent-handoff.test.ts removed. Exact provisional replacement and post-create exit confirmation handoff pipeline alarms disappear. No equivalent replacement coverage is claimed. Audit1313: src/main/runtime/terminal-orphan-topology.test.ts: Retain actual topology merge algorithms preserving disjoint subtrees and grafted/ambiguous shared anchors with independent expected trees. Remove duplicate leaf validator and empty/no-change passthrough examples. Lose basic validation and no-group/noop characterization; no renderer layout proof. Reduced selectors require fresh validation; prior full-suite receipts do not validate removed or changed coverage. No equivalent replacement or physical-provider claim. Audit1342: src/renderer/src/components/terminal-pane/pty-connection-completion-and-notification.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/8878", "https://github.com/stablyai/orca/issues/9151", @@ -7050,7 +7050,7 @@ "https://github.com/stablyai/orca/pull/10193" ], "invariant": "For every claim-capable execution route, one provider-session identity has at most one live PTY owner and one canonical host surface across concurrent clients, retries, reconnects, and stale publications. For paired structured fresh and resume requests, the authenticated owning runtime creates in background without a renderer window; activate=true focuses the exact requested leaf only on the requesting viewer, while activate=false changes no viewer focus. A live orphan may be adopted only when the controller proves its exact handle and incarnation, its worktree and host owner match, no competing visual owner exists, and a host topology CAS wins. A viewer may classify completion only from successful host/provider inspection or explicit lifecycle evidence; transport, handle, or provider unavailability remains unknown and breaks any consecutive-idle proof. A physical exit retires that exact incarnation durably so stale client state and host restart cannot recreate it. Mixed-version routes select the unchanged legacy request before any authority side effect or execution-owner-local filesystem access.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. The Codex restart suite and exact command/testFiles/assertionRef are removed; its mock account-change/remote-pane restart alarms are intentionally lost. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3368 retires the whole host-session-launch unit source, including host-authority/create/ensure/legacy/Kimi/refusal/draft/options/prompt/cancel/kill/shared-disconnect and passive-sleep output callback alarms, without equivalent replacement. Its unit command token and test-file entry retire. Five unrelated macOS receipts remain verbatim and keep only their original scope; no fresh changed-selector/runtime/provider/process/UI/mobile/mixed-version/cross-platform evidence is claimed. Audit3359 retires the whole create-handoff suite. Supplied disposition, late completion, close-callback and metadata-bookkeeping alarms are intentionally lost without equivalent replacement; the compound unit selector and test-file entry narrow accordingly. Five unrelated macOS receipts retain their original scope. No fresh runtime, process-deduplication, physical provider/network, historical-import, mobile, mixed-version or cross-platform validation is claimed. Audit 3301 removes the caller-chat-target precedence over host selection example from web-session-tabs-sync-focus-intent.test.ts. The snapshot-before-RPC replay and split-sibling-first exact-leaf focus cases remain unchanged and match the existing assertion reference. Preserve current selectors, assertion references, unrelated active receipts and complete archived history. These mocked state-composition survivors provide no equivalent replacement, rendered, native, network or fresh execution proof. Audit2694 source-only review: Remove14 authored/14 expanded cases in the renderer completion wrapper family, including the complete bell/query-reply/stale-input factory, without replacement. Retain29 authored/34 expanded cases with unchanged callback bodies. Three removals are the existing pending2203 overlap and must be adopted/count-reconciled once; eleven are fresh removals. No current-source runtime or gate execution is supplied by this packet; dated evidence retains its original source scope and does not validate the reduced factory bytes. No equivalent replacement, physical provider, mixed-version, mobile, rendered UI, cross-platform, soak or measured-performance result is claimed. The existing command and testFiles selector retain tests/pty-connection-completion-and-notification.test.ts; no wrapper-specific assertionRef is removed. Historical selector results do not validate its changed reachable factories. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2003: Delete the complete pure orphan-topology suite and its file membership; preserve the other selectors from its combined command after archiving the original command string. Lost alarms are synthetic current-layout preservation, one-anchor grafting and ambiguous-anchor appending examples. Do not transfer their ownership/topology scope to sibling suites. Preserve all5 independent historical receipts exactly. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1983: Delete the mocked agent-session suite and its prospective selector and aliases. Retire all3 whole dated changed-selector receipts, preserving their original raw objects in historical gaps without transferring counts or durations. Preserve5 unrelated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial agent-operation service suite retains only concurrent retry joining with changed-fingerprint conflict. Retire its complete three-title assertion object: old-owner legacy fallback before side effects, nested SSH Pi transcript refusal and completed/ambiguous physical-commit replay fences are no longer backed. Other probe/launch option/ledger/failure/remote-inventory examples disappear as detailed below. No actual provider spawn, live SSH or physical commit proof is retained by this source. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1911: The complete provider PTY-inspection mock suite is removed: missing-provider rejection, stale-PTY renderer unverifiable mapping, preserved supplied renderer failures, and supplied transport-loss mapping alarms are intentionally lost. No actual terminal teardown, daemon error or remote transport was exercised. Only the deleted explicit source tokens, testFile and whole assertionRef are removed. Meaningful remaining source selectors/assertions and unaffected dated receipts retain their original scope. No equivalent replacement, fresh reduced-gate, native/platform/provider, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Historical pre-audit oracle account for changed sources: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): Race independent clients and repeated operation IDs, then assert one physical spawn and one canonical PTY/surface; inject exit-before-reply, provider disconnect, conflicting claim scope, old daemon/relay capabilities, reused handles, stale incarnations, owner mismatch, and topology revision conflict; assert safe adoption or explicit failure without a second spawn or wrong-process attachment. Run fresh/resume with activate true/false against an isolated headed desktop host and a separate paired client, then against isolated headless serve: assert host presentation stays background, only the requesting viewer focuses the exact leaf, inactive calls preserve client/DOM focus, a same-version publication replay cannot lose focus intent, and sibling-first split publication cannot consume exact-leaf intent. Restore legacy split panes and groups beside a newer host-owned tab, preserving exact predecessor/new/successor order, output, input, resize, titles, tab/leaf identity, active group, and multi-client convergence. For completion, drive a known running agent through outer transport loss, authoritative provider rejection, reconnect, explicit stop, real exit status, and successful hook completion; assert unavailable evidence never dispatches completion and two fresh authoritative idle samples are required after the gap. After exact exit, assert terminal and tab listings omit the surface, a stale publication cannot restore it, restart cannot resurrect it, exact tokened fixture PIDs are dead, and unrelated tabs/processes survive until scoped cleanup.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. The Codex restart suite and exact command/testFiles/assertionRef are removed; its mock account-change/remote-pane restart alarms are intentionally lost. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3368 retires the whole host-session-launch unit source, including host-authority/create/ensure/legacy/Kimi/refusal/draft/options/prompt/cancel/kill/shared-disconnect and passive-sleep output callback alarms, without equivalent replacement. Its unit command token and test-file entry retire. Five unrelated macOS receipts remain verbatim and keep only their original scope; no fresh changed-selector/runtime/provider/process/UI/mobile/mixed-version/cross-platform evidence is claimed. Audit3359 retires the whole create-handoff suite. Supplied disposition, late completion, close-callback and metadata-bookkeeping alarms are intentionally lost without equivalent replacement; the compound unit selector and test-file entry narrow accordingly. Five unrelated macOS receipts retain their original scope. No fresh runtime, process-deduplication, physical provider/network, historical-import, mobile, mixed-version or cross-platform validation is claimed. Audit 3301 removes the caller-chat-target precedence over host selection example from web-session-tabs-sync-focus-intent.test.ts. The snapshot-before-RPC replay and split-sibling-first exact-leaf focus cases remain unchanged and match the existing assertion reference. Preserve current selectors, assertion references, unrelated active receipts and complete archived history. These mocked state-composition survivors provide no equivalent replacement, rendered, native, network or fresh execution proof. Audit2694 source-only review: Remove14 authored/14 expanded cases in the renderer completion wrapper family, including the complete bell/query-reply/stale-input factory, without replacement. Retain29 authored/34 expanded cases with unchanged callback bodies. Three removals are the existing pending2203 overlap and must be adopted/count-reconciled once; eleven are fresh removals. No current-source runtime or gate execution is supplied by this packet; dated evidence retains its original source scope and does not validate the reduced factory bytes. No equivalent replacement, physical provider, mixed-version, mobile, rendered UI, cross-platform, soak or measured-performance result is claimed. The existing command and testFiles selector retain tests/pty-connection-completion-and-notification.test.ts; no wrapper-specific assertionRef is removed. Historical selector results do not validate its changed reachable factories. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2003: Delete the complete pure orphan-topology suite and its file membership; preserve the other selectors from its combined command after archiving the original command string. Lost alarms are synthetic current-layout preservation, one-anchor grafting and ambiguous-anchor appending examples. Do not transfer their ownership/topology scope to sibling suites. Preserve all5 independent historical receipts exactly. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1983: Delete the mocked agent-session suite and its prospective selector and aliases. Retire all3 whole dated changed-selector receipts, preserving their original raw objects in historical gaps without transferring counts or durations. Preserve5 unrelated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial agent-operation service suite retains only concurrent retry joining with changed-fingerprint conflict. Retire its complete three-title assertion object: old-owner legacy fallback before side effects, nested SSH Pi transcript refusal and completed/ambiguous physical-commit replay fences are no longer backed. Other probe/launch option/ledger/failure/remote-inventory examples disappear as detailed below. No actual provider spawn, live SSH or physical commit proof is retained by this source. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1911: The complete provider PTY-inspection mock suite is removed: missing-provider rejection, stale-PTY renderer unverifiable mapping, preserved supplied renderer failures, and supplied transport-loss mapping alarms are intentionally lost. No actual terminal teardown, daemon error or remote transport was exercised. Only the deleted explicit source tokens, testFile and whole assertionRef are removed. Meaningful remaining source selectors/assertions and unaffected dated receipts retain their original scope. No equivalent replacement, fresh reduced-gate, native/platform/provider, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Historical pre-audit oracle account for changed sources: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): Race independent clients and repeated operation IDs, then assert one physical spawn and one canonical PTY/surface; inject exit-before-reply, provider disconnect, conflicting claim scope, old daemon/relay capabilities, reused handles, stale incarnations, owner mismatch, and topology revision conflict; assert safe adoption or explicit failure without a second spawn or wrong-process attachment. Run fresh/resume with activate true/false against an isolated headed desktop host and a separate paired client, then against isolated headless serve: assert host presentation stays background, only the requesting viewer focuses the exact leaf, inactive calls preserve client/DOM focus, a same-version publication replay cannot lose focus intent, and sibling-first split publication cannot consume exact-leaf intent. Restore legacy split panes and groups beside a newer host-owned tab, preserving exact predecessor/new/successor order, output, input, resize, titles, tab/leaf identity, active group, and multi-client convergence. For completion, drive a known running agent through outer transport loss, authoritative provider rejection, reconnect, explicit stop, real exit status, and successful hook completion; assert unavailable evidence never dispatches completion and two fresh authoritative idle samples are required after the gap. After exact exit, assert terminal and tab listings omit the surface, a stale publication cannot restore it, restart cannot resurrect it, exact tokened fixture PIDs are dead, and unrelated tabs/processes survive until scoped cleanup.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/runtime/web-session-tabs-sync/navigation/web-session-tabs-sync-focus-intent.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/shared/claimed-agent-pty-owner.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/runtime/session-construction/agent-session/orca-runtime-agent-session-operation.test.ts src/main/runtime/session-construction/agent-session/remote-agent-session-host-authority.integration.test.ts src/main/runtime/terminal/surface/orca-runtime-terminal-retirement.test.ts", @@ -8210,13 +8210,13 @@ "providers": ["paired-runtime", "ssh"], "coveredPlatforms": ["macos"], "coveredProviders": [], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. The decorative fanout suite retains only real OSC9999 ingestion through runtime/title heartbeat/viewer past the stale boundary, preserving stateStartedAt and clearing expired prompt metadata. Spinner volume/zero-work inventories, Pi/Grok frame tables, rich-status and foreground-reset examples are retired. Existing receipts for separate paired-terminal sources retain their original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3342 removes three supplied activation/inventory fallback and same-handle retry examples; their fallback/retry alarms are intentionally lost without equivalent replacement. The exact mounted-HUB pending old-stream-end versus replacement-snapshot registration and its assertion remain. Unrelated dated macOS receipts retain only their original scope; no fresh runtime, provider, performance, mobile, mixed-version or cross-platform validation is claimed. NEXT46 terminal-owner layout and whole-case policy: src/renderer/src/components/terminal-pane/pty-connection/tests/pty-connection-hidden-replay.test.ts, src/renderer/src/components/terminal-pane/pty-connection/tests/pty-connection-visibility-and-exit.test.ts, src/renderer/src/runtime/hosted-terminals/streams/remote-runtime-terminal-stall-recovery.test.ts. Complete retired/reduced selectors lose their listed alarms; retained cases have no transferred historical pass or equivalent replacement coverage. Unaffected dated receipts retain only their original exact scopes. This metadata edit is not fresh execution evidence. Historical prior account (preserved verbatim): Audit3326 adopts reconciled source audit3324 and intentionally retires these low-value examples and their regression alarms: restarts only the stream whose renderer delivery credit never settles — An unreturned renderer delivery credit stalls only its stream, leaving healthy sibling/shared transport alive. Fake-clock deadline plus real multiplexer unsubscribe isolates recovery ownership.; probes then restarts a stream when an entered command receives no frames — Entered command produces no output; actual multiplexer must probe then close only its stalled stream after probe timeout. Distinct response liveness from parser credit.; establishes a probe baseline before recovering an unsequenced stream — 142-line manual five-probe baseline/regression sequence derives request IDs from encoded mock traffic and checks repeated no-close/close choreography. Oversized algorithm/state-machine example; no actual renderer/host command or recovery delivery. Retires unsequenced/unchanged/regressed baseline classification alarms.. Retained tests do not replace these alarms. Dated unrelated evidence remains scoped to its original execution; no historical source/import-closure equivalence, fresh execution, real network/provider/SSH, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Audit 3318 reduces remote-runtime-pty-transport-unpublished-host-graph.test.ts, retiring its direct published-empty-graph omission-to-pane-exit alarm. The unchanged bounded unpublished-frame/pushed-recovery and stale-handle park/manual-retry cases are narrower, not equivalent replacements. Both complete three-case Linux receipts dated 2026-11-04 are archived as historical, including source heads; they do not validate this reduced selector. Remaining fourteen active receipts are macOS only. No fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. The direct side-effect-facts selector retains six cases: replay exclusion, live sequence handling, stale disposer ownership, one handoff drain, drain-before-snapshot, and stale replacement-snapshot refusal. Successful current-entry snapshot application is not witnessed by the retained snapshot case. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2162: Reduced inventory-authority source intentionally loses the examples recorded in knownGaps; historical execution is not validation of changed bytes. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2003: Remove SSH/absent-inventory attach refusal, later-subscriber retry, no-view adoption/takeback, provider-tail/unknown-read projections, failed-keystroke retry and simpler loss reattach examples. Keep the existing-subscriber inventory-ready retry alias only at its retained callback, plus retained held-input cancellation/replacement and overlapping attach/loss fences. The inventory driver also loses the whole empty-worktree-publication registration and folder-host/unverifiable scalar examples. Retire the entire dated10-case attach receipt; retain16 independent receipts only in their original scopes. No retained-memory or real-daemon measurement follows from supplied provider inventories. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1991: Remove initial-snapshot sequence-domain, limited-tail/truncation and unknown-output-sequence aliases whose callbacks are removed. The source retains partial-chunk replay and requested-snapshot retry/admission at supplied snapshot/data seams, not equivalent replacement or measured retained-memory proof. Preserve the existing stream/pending-wait capacity aliases only to their retained owning multiplex callbacks with supplied PTY resolutions. Removing the sibling-PTY heartbeat dedup work-count sample does not establish a new decorative fanout budget; the unrelated-PTY cancellation fence survives. Preserve17 unrelated dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1983: Remove the reasonless-close selector and narrow census aliases to actual retained runtime and injected-inventory callbacks. The removed synthetic census-dedup and isolated pre-boundary timing cases receive no replacement credit; preserve17 unrelated dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Delete the synthetic watcher-synchronization and partial-reconciliation suites. Retire all affected host-fact, capability-owner, activation-residue and retention-cap aliases because the remaining callbacks do not establish those full claims. Keep exact local hook/timer, byte-stream side effects, exit/disposal, scroll-intent cleanup and deterministic local rank/TTL progression callbacks within their mocked-host scope. No physical paired-host retention, resource budget or equivalent replacement evidence is assigned. The generic broad test command remains a runnable discovery command, never a dated execution receipt. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Delete the complete hook-agent-status projection suite and its two behavioral assertion aliases. Supplied stored hook/title/resume/staleness/rename/question/generation/PTY-retirement/snapshot-version projections are intentionally lost. The separate osc-status-scan gate mentions this source only in existing historical notes and receives no mutation. No measured hidden-output retention, CPU/RSS or resource budget result is claimed by the deleted source or this audit. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Module ownership cleanup retires the modeled cold-park private watcher/render/work-count suites under the current test policy. Their assertions are no longer active evidence. Retained lifecycle tests and headed/headless journeys do not establish equivalent coverage for those removed harness alarms. Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally deletes src/renderer/src/components/terminal-pane/pty-connection-hidden-tui-snapshot-replay.spec.ts: Loses rich hidden snapshot sequence replay/grid resize and forced repaint after hidden snapshot alarms, including live-trigger deduplication. deletes src/renderer/src/components/terminal-pane/terminal-cold-park-verdict-loop.test.tsx: Loses modeled React oscillation alarm; real pane/watchers are not exercised. deletes src/renderer/src/runtime/remote-runtime-terminal-parse-backpressure.test.ts: Loses delayed credit until parsing, malformed frame/unknown stream handling, discarded transformed output recovery and failed ACK rearm alarms. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1412 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): This audit removes the browser response-after-manual-disconnect example. Its distinct in-flight response fencing alarm is lost; retained queued-call disconnect tests are not an equivalent replacement. Historical commands and results remain historical. Deterministic headed macOS runs launch an isolated Orca desktop server and a separate paired web client. A byte-identical headless run uses an isolated `orca serve` host. Both create six real paired host PTYs with bounded high-output scrollback, prove sustained output while all six client xterms are warm-mounted but hidden causes zero renderer scheduler work, then drive 180 timed real-PTY spinner frames through a second production `session.tabs.subscribeAll` transport and the real Zustand store. Each worktree must publish and apply only its exact initial-working and final-idle titles. Both topologies ordinary-park five xterms without enabling the lossy retention budget, assert bounded cells/heap/timer lag, then restore the bounded authoritative tail exactly once on the original PTY including output produced while parked and continued input/output. The headed oracle additionally keeps every remote target workspace and terminal unmounted in the host renderer before and after client recovery. Production-parity React contracts reproduce and remove the independent cold-park watcher/title feedback loop, including memo-cache discard, StrictMode cleanup replay, detached split-PTY replacement, and the zero-park fast path. A deterministic cross-boundary contract drives 24 real PTY trackers in distinct worktrees through 2,880 decorative spinner and bare-native redraw chunks and counts host publications, serialized bytes, renderer apply calls, and store mutations while preserving exact per-PTY raw chunks and per-worktree semantic transitions. Fake-clock continuation beyond the 30-minute stale boundary requires one deduplicated refresh per worktree every 15 minutes, globally spaced by at least 50 ms, with every mirrored working status still fresh. Exact-identical Pi plus rotating normalized Pi/Grok frames renew coarse working/permission state; rich prompt/tool/question fields retain their original 30-minute lease, and `stateStartedAt`/acknowledgement identity remain stable across title/hook source swaps. A separate headed desktop-client journey relaunches the host main process while retaining the daemon and a daemon-backgrounded PTY, then requires first-observation foreground sync and continued input/output on the same PTY. Unit coverage keeps layout versions out of snapshot output high-waters when a restored snapshot has no output sequence and retries an existing subscriber only after authoritative provider inventory proves the retained PTY exists. Separate headed/headless ACK-starvation tests recover one stalled stream without replacing its PTY. A headed output-drop oracle proves a successful snapshot probe cannot certify a stale live stream when the authoritative PTY sequence advanced beyond the client high-water. Unit coverage requires an unsequenced client to establish a first-probe baseline, remain attached at the same sequence, and recover only after a later probe advances. A capability-disabled run proves legacy hosts retain the prior lossy limit/TTL fallback. Unit tests cover exact-owner capability routing, raw-stream release, singleton side-effect facts with timed handoff cleanup, 64 simultaneous synthetic-title sources with zero decorative client events after convergence, per-client late-subscribe recovery, semantic bell/title transitions, local animation-frame preservation, legacy no-output-pause delivery gating with exact snapshot restore, provider-authoritative snapshots, 128 active streams plus retry after a 129th-stream capacity rejection, capacity-pressure backoff, full split-leaf remint reconciliation, truncation, and manual-disconnect queue fencing. Linux, Windows, live SSH, and production-scale paired hosts remain gaps. The audit removed the E2E parking override/debug-handle suite. Override exposure guards, delay/retention independence, TTL filtering and debug-handle forwarding alarms are lost. Retained real parking journeys do not establish equivalent test-harness configuration coverage. The test-value audit removes separate responsive-stream, intervening-control-frame and capacity-error classification examples. Retained multiplexer checks use fake clocks and captured transport callbacks; their per-stream isolation, deadline and probe-baseline assertions do not prove an actual host restart or equivalent replacement coverage. Historical run commands and results remain historical. The test-value audit removes the standalone retained/live metadata projection and additive-preference negative example from runtime-client-events.test.ts. Its remaining case checks partial-authority event admission through a captured callback; it does not establish full-pair projection, actual peer delivery or equivalent replacement coverage. Historical commands and results remain historical. The isolated accepted-handle-events observer suite is removed. Its runtime/worktree/pane membership, once-only delivery and listener-release ledger alarms are lost. Live hidden-retention evidence does not establish equivalent observer coverage. Audit1292: The entire host-session mirror empty-inventory settle suite is removed. Live-unpublished versus genuinely-empty hydration, same-ID revision response/in-flight fencing and cleared-tracking pending-probe invalidation alarms are lost. The singleton store and inventories were mocked; no real host census or resume delivery or equivalent replacement coverage is claimed. Audit1302: src/main/runtime/session-tabs-inventory-publication.test.ts: Keep actual runtime public inventory authority across renderer epochs/resync, fallback, aborts and invalid graph publishers. Remove copied nonempty publication, private scan counters/collector and known-host replacement, fake refresh that performs the restoration itself, environment capability flag, and private resume-store characterization whose fixture supplies reservation/rollback ordering. Lose isolated refresh/churn/receiver/capability and copied-payload wiring alarms; no equivalent replacement. Reduced selectors require fresh validation; prior full-suite receipts do not validate removed or changed coverage. No equivalent replacement or physical-provider claim. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1318: src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts: Retain real local encrypted WebSocket regressions for pre-ready socket replacement preserving a request, close-before-ready logical subscription cleanup, unrelated RPC surviving another deadline, and pending setup abort leaving a sibling pending. Host server is synthetic and uses production crypto; kept scenarios also retain internal diagnostics/memory assertions unchanged. Remove happy routing, authority-envelope, unknown-ID/log, cleanup enum, binary, keepalive/liveness and production-budget/private socket inventories; intentionally lose those alarms, no physical host authorization, independent crypto or remote provider claim. Reduced selectors need fresh validation; historical full-suite receipts do not validate the changed coverage. No equivalent replacement or physical-provider claim. Audit1346 removes the entire private-state-injected session-tabs rich-status boundary suite. Its stale task/question, neutral-title preservation, owner-change and provider-generation alarms are intentionally lost. Retained real terminal journeys do not establish equivalent replacement coverage. The prior mixed 42-contract execution receipt is historical and must not validate the reduced current selector; fresh validation remains required. Audit1352: focused Linux unit selections validated on source head b638e07d395b76af3ba6b03761a1a0fe7e5e026e. Historical statements above retain their original scope. Intentional test losses are recorded in knownGaps; these runs claim no physical-provider, p95, rendered E2E or cross-platform validation. Audit1360: focused Linux unit selections validated on source head 415c9cffc2d10024131ec4b6721767a22195de6d. Historical statements above retain their original scope. Intentional test losses are recorded in knownGaps; these runs claim no physical-provider, p95, rendered E2E or cross-platform validation. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1391: src/renderer/src/components/terminal-pane/pty-connection-remote-pause-drop-notice.spec.ts reduced. Lose repeated quiet switches avoiding snapshots and legacy host without drop-report capability requesting a snapshot. Retained imported registrar uses fake transport/xterm writes, exercising real gap/restore lifecycle rather than actual terminal rendering. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1396: src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts reduced. Retire standalone reconcile set output and positive untouchedFreshSpawn/workspaceOwner copying alarms; no native watcher execution claim. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1402: src/renderer/src/components/terminal-pane/pty-connection-park-reveal-model-snapshot.spec.ts reduced. Loses basic paired serialize/initial replay forwarding check; hidden alt-frame grid/precedence policy remains. No real terminal renderer claim. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1412: src/main/runtime/mobile-session-tabs-agent-status-heartbeat.test.ts reduced. Loses FIFO fairness/spacing, semantic bypass/cancellation, delayed-boundary scheduling and teardown timer-cleanup alarms. Retained fixed emission cases do not replace those outcomes. No equivalent replacement coverage is claimed.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. The decorative fanout suite retains only real OSC9999 ingestion through runtime/title heartbeat/viewer past the stale boundary, preserving stateStartedAt and clearing expired prompt metadata. Spinner volume/zero-work inventories, Pi/Grok frame tables, rich-status and foreground-reset examples are retired. Existing receipts for separate paired-terminal sources retain their original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3342 removes three supplied activation/inventory fallback and same-handle retry examples; their fallback/retry alarms are intentionally lost without equivalent replacement. The exact mounted-HUB pending old-stream-end versus replacement-snapshot registration and its assertion remain. Unrelated dated macOS receipts retain only their original scope; no fresh runtime, provider, performance, mobile, mixed-version or cross-platform validation is claimed. NEXT46 terminal-owner layout and whole-case policy: src/renderer/src/components/terminal-pane/pty-connection/tests/pty-connection-hidden-replay.test.ts, src/renderer/src/components/terminal-pane/pty-connection/tests/pty-connection-visibility-and-exit.test.ts, src/renderer/src/runtime/hosted-terminals/streams/remote-runtime-terminal-stall-recovery.test.ts. Complete retired/reduced selectors lose their listed alarms; retained cases have no transferred historical pass or equivalent replacement coverage. Unaffected dated receipts retain only their original exact scopes. This metadata edit is not fresh execution evidence. Historical prior account (preserved verbatim): Audit3326 adopts reconciled source audit3324 and intentionally retires these low-value examples and their regression alarms: restarts only the stream whose renderer delivery credit never settles — An unreturned renderer delivery credit stalls only its stream, leaving healthy sibling/shared transport alive. Fake-clock deadline plus real multiplexer unsubscribe isolates recovery ownership.; probes then restarts a stream when an entered command receives no frames — Entered command produces no output; actual multiplexer must probe then close only its stalled stream after probe timeout. Distinct response liveness from parser credit.; establishes a probe baseline before recovering an unsequenced stream — 142-line manual five-probe baseline/regression sequence derives request IDs from encoded mock traffic and checks repeated no-close/close choreography. Oversized algorithm/state-machine example; no actual renderer/host command or recovery delivery. Retires unsequenced/unchanged/regressed baseline classification alarms.. Retained tests do not replace these alarms. Dated unrelated evidence remains scoped to its original execution; no historical source/import-closure equivalence, fresh execution, real network/provider/SSH, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Audit 3318 reduces remote-runtime-pty-transport-unpublished-host-graph.test.ts, retiring its direct published-empty-graph omission-to-pane-exit alarm. The unchanged bounded unpublished-frame/pushed-recovery and stale-handle park/manual-retry cases are narrower, not equivalent replacements. Both complete three-case Linux receipts dated 2026-11-04 are archived as historical, including source heads; they do not validate this reduced selector. Remaining fourteen active receipts are macOS only. No fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. The direct side-effect-facts selector retains six cases: replay exclusion, live sequence handling, stale disposer ownership, one handoff drain, drain-before-snapshot, and stale replacement-snapshot refusal. Successful current-entry snapshot application is not witnessed by the retained snapshot case. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2162: Reduced inventory-authority source intentionally loses the examples recorded in knownGaps; historical execution is not validation of changed bytes. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2003: Remove SSH/absent-inventory attach refusal, later-subscriber retry, no-view adoption/takeback, provider-tail/unknown-read projections, failed-keystroke retry and simpler loss reattach examples. Keep the existing-subscriber inventory-ready retry alias only at its retained callback, plus retained held-input cancellation/replacement and overlapping attach/loss fences. The inventory driver also loses the whole empty-worktree-publication registration and folder-host/unverifiable scalar examples. Retire the entire dated10-case attach receipt; retain16 independent receipts only in their original scopes. No retained-memory or real-daemon measurement follows from supplied provider inventories. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1991: Remove initial-snapshot sequence-domain, limited-tail/truncation and unknown-output-sequence aliases whose callbacks are removed. The source retains partial-chunk replay and requested-snapshot retry/admission at supplied snapshot/data seams, not equivalent replacement or measured retained-memory proof. Preserve the existing stream/pending-wait capacity aliases only to their retained owning multiplex callbacks with supplied PTY resolutions. Removing the sibling-PTY heartbeat dedup work-count sample does not establish a new decorative fanout budget; the unrelated-PTY cancellation fence survives. Preserve17 unrelated dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1983: Remove the reasonless-close selector and narrow census aliases to actual retained runtime and injected-inventory callbacks. The removed synthetic census-dedup and isolated pre-boundary timing cases receive no replacement credit; preserve17 unrelated dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Delete the synthetic watcher-synchronization and partial-reconciliation suites. Retire all affected host-fact, capability-owner, activation-residue and retention-cap aliases because the remaining callbacks do not establish those full claims. Keep exact local hook/timer, byte-stream side effects, exit/disposal, scroll-intent cleanup and deterministic local rank/TTL progression callbacks within their mocked-host scope. No physical paired-host retention, resource budget or equivalent replacement evidence is assigned. The generic broad test command remains a runnable discovery command, never a dated execution receipt. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Delete the complete hook-agent-status projection suite and its two behavioral assertion aliases. Supplied stored hook/title/resume/staleness/rename/question/generation/PTY-retirement/snapshot-version projections are intentionally lost. The separate osc-status-scan gate mentions this source only in existing historical notes and receives no mutation. No measured hidden-output retention, CPU/RSS or resource budget result is claimed by the deleted source or this audit. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Module ownership cleanup retires the modeled cold-park private watcher/render/work-count suites under the current test policy. Their assertions are no longer active evidence. Retained lifecycle tests and headed/headless journeys do not establish equivalent coverage for those removed harness alarms. Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally deletes src/renderer/src/components/terminal-pane/pty-connection-hidden-tui-snapshot-replay.spec.ts: Loses rich hidden snapshot sequence replay/grid resize and forced repaint after hidden snapshot alarms, including live-trigger deduplication. deletes src/renderer/src/components/terminal-pane/terminal-cold-park-verdict-loop.test.tsx: Loses modeled React oscillation alarm; real pane/watchers are not exercised. deletes src/renderer/src/runtime/remote-runtime-terminal-parse-backpressure.test.ts: Loses delayed credit until parsing, malformed frame/unknown stream handling, discarded transformed output recovery and failed ACK rearm alarms. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1412 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): This audit removes the browser response-after-manual-disconnect example. Its distinct in-flight response fencing alarm is lost; retained queued-call disconnect tests are not an equivalent replacement. Historical commands and results remain historical. Deterministic headed macOS runs launch an isolated Orca desktop server and a separate paired web client. A byte-identical headless run uses an isolated `orca serve` host. Both create six real paired host PTYs with bounded high-output scrollback, prove sustained output while all six client xterms are warm-mounted but hidden causes zero renderer scheduler work, then drive 180 timed real-PTY spinner frames through a second production `session.tabs.subscribeAll` transport and the real Zustand store. Each worktree must publish and apply only its exact initial-working and final-idle titles. Both topologies ordinary-park five xterms without enabling the lossy retention budget, assert bounded cells/heap/timer lag, then restore the bounded authoritative tail exactly once on the original PTY including output produced while parked and continued input/output. The headed oracle additionally keeps every remote target workspace and terminal unmounted in the host renderer before and after client recovery. Production-parity React contracts reproduce and remove the independent cold-park watcher/title feedback loop, including memo-cache discard, StrictMode cleanup replay, detached split-PTY replacement, and the zero-park fast path. A deterministic cross-boundary contract drives 24 real PTY trackers in distinct worktrees through 2,880 decorative spinner and bare-native redraw chunks and counts host publications, serialized bytes, renderer apply calls, and store mutations while preserving exact per-PTY raw chunks and per-worktree semantic transitions. Fake-clock continuation beyond the 30-minute stale boundary requires one deduplicated refresh per worktree every 15 minutes, globally spaced by at least 50 ms, with every mirrored working status still fresh. Exact-identical Pi plus rotating normalized Pi/Grok frames renew coarse working/permission state; rich prompt/tool/question fields retain their original 30-minute lease, and `stateStartedAt`/acknowledgement identity remain stable across title/hook source swaps. A separate headed desktop-client journey relaunches the host main process while retaining the daemon and a daemon-backgrounded PTY, then requires first-observation foreground sync and continued input/output on the same PTY. Unit coverage keeps layout versions out of snapshot output high-waters when a restored snapshot has no output sequence and retries an existing subscriber only after authoritative provider inventory proves the retained PTY exists. Separate headed/headless ACK-starvation tests recover one stalled stream without replacing its PTY. A headed output-drop oracle proves a successful snapshot probe cannot certify a stale live stream when the authoritative PTY sequence advanced beyond the client high-water. Unit coverage requires an unsequenced client to establish a first-probe baseline, remain attached at the same sequence, and recover only after a later probe advances. A capability-disabled run proves legacy hosts retain the prior lossy limit/TTL fallback. Unit tests cover exact-owner capability routing, raw-stream release, singleton side-effect facts with timed handoff cleanup, 64 simultaneous synthetic-title sources with zero decorative client events after convergence, per-client late-subscribe recovery, semantic bell/title transitions, local animation-frame preservation, legacy no-output-pause delivery gating with exact snapshot restore, provider-authoritative snapshots, 128 active streams plus retry after a 129th-stream capacity rejection, capacity-pressure backoff, full split-leaf remint reconciliation, truncation, and manual-disconnect queue fencing. Linux, Windows, live SSH, and production-scale paired hosts remain gaps. The audit removed the E2E parking override/debug-handle suite. Override exposure guards, delay/retention independence, TTL filtering and debug-handle forwarding alarms are lost. Retained real parking journeys do not establish equivalent test-harness configuration coverage. The test-value audit removes separate responsive-stream, intervening-control-frame and capacity-error classification examples. Retained multiplexer checks use fake clocks and captured transport callbacks; their per-stream isolation, deadline and probe-baseline assertions do not prove an actual host restart or equivalent replacement coverage. Historical run commands and results remain historical. The test-value audit removes the standalone retained/live metadata projection and additive-preference negative example from runtime-client-events.test.ts. Its remaining case checks partial-authority event admission through a captured callback; it does not establish full-pair projection, actual peer delivery or equivalent replacement coverage. Historical commands and results remain historical. The isolated accepted-handle-events observer suite is removed. Its runtime/worktree/pane membership, once-only delivery and listener-release ledger alarms are lost. Live hidden-retention evidence does not establish equivalent observer coverage. Audit1292: The entire host-session mirror empty-inventory settle suite is removed. Live-unpublished versus genuinely-empty hydration, same-ID revision response/in-flight fencing and cleared-tracking pending-probe invalidation alarms are lost. The singleton store and inventories were mocked; no real host census or resume delivery or equivalent replacement coverage is claimed. Audit1302: src/main/runtime/session-tabs-inventory-publication.test.ts: Keep actual runtime public inventory authority across renderer epochs/resync, fallback, aborts and invalid graph publishers. Remove copied nonempty publication, private scan counters/collector and known-host replacement, fake refresh that performs the restoration itself, environment capability flag, and private resume-store characterization whose fixture supplies reservation/rollback ordering. Lose isolated refresh/churn/receiver/capability and copied-payload wiring alarms; no equivalent replacement. Reduced selectors require fresh validation; prior full-suite receipts do not validate removed or changed coverage. No equivalent replacement or physical-provider claim. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1318: src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts: Retain real local encrypted WebSocket regressions for pre-ready socket replacement preserving a request, close-before-ready logical subscription cleanup, unrelated RPC surviving another deadline, and pending setup abort leaving a sibling pending. Host server is synthetic and uses production crypto; kept scenarios also retain internal diagnostics/memory assertions unchanged. Remove happy routing, authority-envelope, unknown-ID/log, cleanup enum, binary, keepalive/liveness and production-budget/private socket inventories; intentionally lose those alarms, no physical host authorization, independent crypto or remote provider claim. Reduced selectors need fresh validation; historical full-suite receipts do not validate the changed coverage. No equivalent replacement or physical-provider claim. Audit1346 removes the entire private-state-injected session-tabs rich-status boundary suite. Its stale task/question, neutral-title preservation, owner-change and provider-generation alarms are intentionally lost. Retained real terminal journeys do not establish equivalent replacement coverage. The prior mixed 42-contract execution receipt is historical and must not validate the reduced current selector; fresh validation remains required. Audit1352: focused Linux unit selections validated on source head b638e07d395b76af3ba6b03761a1a0fe7e5e026e. Historical statements above retain their original scope. Intentional test losses are recorded in knownGaps; these runs claim no physical-provider, p95, rendered E2E or cross-platform validation. Audit1360: focused Linux unit selections validated on source head 415c9cffc2d10024131ec4b6721767a22195de6d. Historical statements above retain their original scope. Intentional test losses are recorded in knownGaps; these runs claim no physical-provider, p95, rendered E2E or cross-platform validation. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1391: src/renderer/src/components/terminal-pane/pty-connection-remote-pause-drop-notice.spec.ts reduced. Lose repeated quiet switches avoiding snapshots and legacy host without drop-report capability requesting a snapshot. Retained imported registrar uses fake transport/xterm writes, exercising real gap/restore lifecycle rather than actual terminal rendering. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1396: src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts reduced. Retire standalone reconcile set output and positive untouchedFreshSpawn/workspaceOwner copying alarms; no native watcher execution claim. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1402: src/renderer/src/components/terminal-pane/pty-connection-park-reveal-model-snapshot.spec.ts reduced. Loses basic paired serialize/initial replay forwarding check; hidden alt-frame grid/precedence policy remains. No real terminal renderer claim. No equivalent replacement coverage is claimed. The existing primary-selector receipt and its claims remain scoped to their unchanged sources; the broad optional command has no new execution result. Audit1412: src/main/runtime/mobile-session-tabs-agent-status-heartbeat.test.ts reduced. Loses FIFO fairness/spacing, semantic bypass/cancellation, delayed-boundary scheduling and teardown timer-cleanup alarms. Retained fixed emission cases do not replace those outcomes. No equivalent replacement coverage is claimed.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/8652", "https://github.com/stablyai/orca/pull/10625" ], "invariant": "A host advertising terminal.paired-parking.v1 keeps the PTY and bounded authoritative history alive while an ordinary hidden-view park destroys the client xterm and releases its raw per-PTY stream. Reveal must restore up to the requested 5,000 rows, parked-time side effects/output, the same PTY identity, and continued input/output. Parked watcher synchronization compares parking semantics rather than fresh object identity, owns setup and cleanup across StrictMode replay, reconciles real PTY/layout changes, and performs no terminal-state scan when nothing is parked. After a host main-process relaunch, unknown local delivery-sync state must not be treated as proof that a surviving daemon PTY is already foregrounded. An empty headed session-tab inventory is authoritative only after the current renderer graph generation publishes a complete inventory and every execution host answers the PTY census; unpublished, resync-pending, transport-failed, and partially scoped inventories remain unverifiable, while genuinely authoritative empty and non-empty inventories settle and a scope-less legacy empty keeps its pre-authority best-effort settle so outdated hosts retain agent auto-resume. The unpublished-empty regression must keep the resume dispatch parked with exactly one daemon process/writer; restoring unconditional empty hydration must release one dispatch and create two live writers. If subscription precedes provider readiness, authoritative inventory must reconsider that existing subscriber without spawning, resizing, or changing reconnect state. A snapshot without an output high-water must keep it unknown rather than borrowing a layout version that can suppress newer live output. Hosts without the capability must gate hidden raw output before xterm scheduling and repaint from the authoritative snapshot on reveal while retaining the existing limit/TTL force-parking fallback. After the first semantic title state, decorative spinner frequency must add zero paired client-event frames and zero full session-tabs work between status-freshness leases regardless of hidden worktree count. Continuous working or permission evidence may refresh each affected worktree once per 15 minutes through one globally spaced FIFO so current and legacy viewers do not decay before 30 minutes; sibling PTYs share that worktree refresh, while local title animation, raw output, and semantic title, status, bell, completion, and query facts remain intact. A paired terminal stream whose delivery credits stop progressing must replace only that stream; command silence first probes authoritative state and replaces the stream if the probe times out or proves the PTY advanced beyond the client's delivered output sequence. When no comparable delivery high-water exists, the first sequenced probe establishes a baseline and only later advancement proves staleness. A same-sequence snapshot remains valid proof of a responsive silent command. A successful status probe may replace a pre-ready shared-control socket without rejecting or duplicating calls already waiting for that transport. Manual disconnect must retain pairing while preventing queued or passive calls and subscriptions from recreating transport until explicit Connect. A paired pane reconnecting to a relaunched host that has not yet published its window graph (an unpublished frame, bare or client-projected) must stay in recovery rather than retire, and the host's first published snapshot carrying the surface must reattach it; a published frame lacking the surface still retires it. The placeholder is only ever sent before the host's graph first publishes; afterwards a worktree with no tabs gets a real empty answer, pushed once to any client that asked during startup, so a paired client opening a never-opened host worktree still gets its first terminal.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. The decorative fanout suite retains only real OSC9999 ingestion through runtime/title heartbeat/viewer past the stale boundary, preserving stateStartedAt and clearing expired prompt metadata. Spinner volume/zero-work inventories, Pi/Grok frame tables, rich-status and foreground-reset examples are retired. Existing receipts for separate paired-terminal sources retain their original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3342 removes three supplied activation/inventory fallback and same-handle retry examples; their fallback/retry alarms are intentionally lost without equivalent replacement. The exact mounted-HUB pending old-stream-end versus replacement-snapshot registration and its assertion remain. Unrelated dated macOS receipts retain only their original scope; no fresh runtime, provider, performance, mobile, mixed-version or cross-platform validation is claimed. Audit3326 adopts reconciled source audit3324 and intentionally retires these low-value examples and their regression alarms: restarts only the stream whose renderer delivery credit never settles — An unreturned renderer delivery credit stalls only its stream, leaving healthy sibling/shared transport alive. Fake-clock deadline plus real multiplexer unsubscribe isolates recovery ownership.; probes then restarts a stream when an entered command receives no frames — Entered command produces no output; actual multiplexer must probe then close only its stalled stream after probe timeout. Distinct response liveness from parser credit.; establishes a probe baseline before recovering an unsequenced stream — 142-line manual five-probe baseline/regression sequence derives request IDs from encoded mock traffic and checks repeated no-close/close choreography. Oversized algorithm/state-machine example; no actual renderer/host command or recovery delivery. Retires unsequenced/unchanged/regressed baseline classification alarms.. Retained tests do not replace these alarms. Dated unrelated evidence remains scoped to its original execution; no historical source/import-closure equivalence, fresh execution, real network/provider/SSH, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Audit 3318 reduces remote-runtime-pty-transport-unpublished-host-graph.test.ts, retiring its direct published-empty-graph omission-to-pane-exit alarm. The unchanged bounded unpublished-frame/pushed-recovery and stale-handle park/manual-retry cases are narrower, not equivalent replacements. Both complete three-case Linux receipts dated 2026-11-04 are archived as historical, including source heads; they do not validate this reduced selector. Remaining fourteen active receipts are macOS only. No fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. The direct side-effect-facts selector retains six cases: replay exclusion, live sequence handling, stale disposer ownership, one handoff drain, drain-before-snapshot, and stale replacement-snapshot refusal. Successful current-entry snapshot application is not witnessed by the retained snapshot case. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2003: Remove SSH/absent-inventory attach refusal, later-subscriber retry, no-view adoption/takeback, provider-tail/unknown-read projections, failed-keystroke retry and simpler loss reattach examples. Keep the existing-subscriber inventory-ready retry alias only at its retained callback, plus retained held-input cancellation/replacement and overlapping attach/loss fences. The inventory driver also loses the whole empty-worktree-publication registration and folder-host/unverifiable scalar examples. Retire the entire dated10-case attach receipt; retain16 independent receipts only in their original scopes. No retained-memory or real-daemon measurement follows from supplied provider inventories. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1991: Remove initial-snapshot sequence-domain, limited-tail/truncation and unknown-output-sequence aliases whose callbacks are removed. The source retains partial-chunk replay and requested-snapshot retry/admission at supplied snapshot/data seams, not equivalent replacement or measured retained-memory proof. Preserve the existing stream/pending-wait capacity aliases only to their retained owning multiplex callbacks with supplied PTY resolutions. Removing the sibling-PTY heartbeat dedup work-count sample does not establish a new decorative fanout budget; the unrelated-PTY cancellation fence survives. Preserve17 unrelated dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1983: Remove the reasonless-close selector and narrow census aliases to actual retained runtime and injected-inventory callbacks. The removed synthetic census-dedup and isolated pre-boundary timing cases receive no replacement credit; preserve17 unrelated dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Delete the synthetic watcher-synchronization and partial-reconciliation suites. Retire all affected host-fact, capability-owner, activation-residue and retention-cap aliases because the remaining callbacks do not establish those full claims. Keep exact local hook/timer, byte-stream side effects, exit/disposal, scroll-intent cleanup and deterministic local rank/TTL progression callbacks within their mocked-host scope. No physical paired-host retention, resource budget or equivalent replacement evidence is assigned. The generic broad test command remains a runnable discovery command, never a dated execution receipt. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Delete the complete hook-agent-status projection suite and its two behavioral assertion aliases. Supplied stored hook/title/resume/staleness/rename/question/generation/PTY-retirement/snapshot-version projections are intentionally lost. The separate osc-status-scan gate mentions this source only in existing historical notes and receives no mutation. No measured hidden-output retention, CPU/RSS or resource budget result is claimed by the deleted source or this audit. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Historical pre-audit oracle account for changed sources: The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Run one byte-identical six-terminal oracle against an isolated headed desktop host and an isolated headless `orca serve` host. Stage at least 1,000,000 xterm cells, hide all six warm managers, emit an initial working title followed by 30 timed real-PTY spinner frames and a final idle title per worktree, and observe a second real session-tabs subscription plus the production renderer store. Require exactly two transport envelopes and two title mutations per worktree, no decorative title crossing either boundary, and positive decoded-envelope bytes. Enable ordinary parking with the lossy retention budget disabled, require exactly one mounted manager and five parked tabs, at most 45% retained cells, no more than 16 MiB heap growth, and under 500 ms timer drift. In headed mode, require the host renderer to remain on its original workspace with zero target terminal managers mounted throughout client park, reveal, and live I/O. While a tab is parked, require authoritative terminal.read to observe new PTY output; reveal it and require the original PTY, a marker within the requested 5,000-row history, the parked marker, and post-reveal input/output. Drive 24 real PTY title trackers in distinct worktrees through 40 decorative spinner frames each, then 40 spinner frames alternating with Cursor's bare native identity redraw, using fake clocks. Require all 2,880 raw chunks exactly and zero host session-tabs publications, serialized bytes, renderer apply calls, and renderer store mutations. Continue decorative evidence beyond 30 minutes, require exactly one refresh per worktree per 15-minute lease through a FIFO spaced by at least 50 ms, and require all 24 viewer statuses to remain working and fresh; then require one exact semantic idle transition and one visible-output chunk per PTY. Require ordinary and interior-Braille title changes to publish. Drive 64 host PTYs through ten 80 ms synthetic title frames, require zero paired client events after the first frame per PTY while all 640 local frames remain observable, attach a late client and require one current frame per PTY, then require semantic bell and idle transitions on both clients. Reconstruct a legacy subscribed stream with no outputPause capability, hide a chatty pane, require no hidden xterm writes, reveal it, and require one authoritative snapshot plus continued live output. Drop and acknowledge output only for one original paired-client stream, prove the fixture process consumed input while the host model advanced and the client stayed stale, then require the command snapshot probe to replace that stream, repaint exact fixture output, preserve authoritative PTY identity and target-tab cardinality, and resume live I/O. For a client without a delivered sequence, require the first numeric probe to establish a baseline without replacement, a same-sequence probe to remain attached, and a later advanced probe to replace only that stream. Withhold the first encrypted shared-control ready frame, start one RPC, trigger a status-probe refresh, and require two connections, one host delivery, successful response, and zero retained request bytes. Admit 128 active streams, reject the 129th as retryable, release one stream, then require the retry to attach and publish a snapshot without multiplying retained subscribers. Disable terminal.paired-parking.v1 and require the same oracle to fail before parking, while the legacy limit-one fallback separately passes. Also preserve truncated first paint, ACK-starved same-PTY recovery, responsive silent-command snapshot probes, dead-stream probe timeout recovery, and queued manual-disconnect fencing.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. The decorative fanout suite retains only real OSC9999 ingestion through runtime/title heartbeat/viewer past the stale boundary, preserving stateStartedAt and clearing expired prompt metadata. Spinner volume/zero-work inventories, Pi/Grok frame tables, rich-status and foreground-reset examples are retired. Existing receipts for separate paired-terminal sources retain their original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3342 removes three supplied activation/inventory fallback and same-handle retry examples; their fallback/retry alarms are intentionally lost without equivalent replacement. The exact mounted-HUB pending old-stream-end versus replacement-snapshot registration and its assertion remain. Unrelated dated macOS receipts retain only their original scope; no fresh runtime, provider, performance, mobile, mixed-version or cross-platform validation is claimed. Audit3326 adopts reconciled source audit3324 and intentionally retires these low-value examples and their regression alarms: restarts only the stream whose renderer delivery credit never settles — An unreturned renderer delivery credit stalls only its stream, leaving healthy sibling/shared transport alive. Fake-clock deadline plus real multiplexer unsubscribe isolates recovery ownership.; probes then restarts a stream when an entered command receives no frames — Entered command produces no output; actual multiplexer must probe then close only its stalled stream after probe timeout. Distinct response liveness from parser credit.; establishes a probe baseline before recovering an unsequenced stream — 142-line manual five-probe baseline/regression sequence derives request IDs from encoded mock traffic and checks repeated no-close/close choreography. Oversized algorithm/state-machine example; no actual renderer/host command or recovery delivery. Retires unsequenced/unchanged/regressed baseline classification alarms.. Retained tests do not replace these alarms. Dated unrelated evidence remains scoped to its original execution; no historical source/import-closure equivalence, fresh execution, real network/provider/SSH, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Audit 3318 reduces remote-runtime-pty-transport-unpublished-host-graph.test.ts, retiring its direct published-empty-graph omission-to-pane-exit alarm. The unchanged bounded unpublished-frame/pushed-recovery and stale-handle park/manual-retry cases are narrower, not equivalent replacements. Both complete three-case Linux receipts dated 2026-11-04 are archived as historical, including source heads; they do not validate this reduced selector. Remaining fourteen active receipts are macOS only. No fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. The direct side-effect-facts selector retains six cases: replay exclusion, live sequence handling, stale disposer ownership, one handoff drain, drain-before-snapshot, and stale replacement-snapshot refusal. Successful current-entry snapshot application is not witnessed by the retained snapshot case. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2003: Remove SSH/absent-inventory attach refusal, later-subscriber retry, no-view adoption/takeback, provider-tail/unknown-read projections, failed-keystroke retry and simpler loss reattach examples. Keep the existing-subscriber inventory-ready retry alias only at its retained callback, plus retained held-input cancellation/replacement and overlapping attach/loss fences. The inventory driver also loses the whole empty-worktree-publication registration and folder-host/unverifiable scalar examples. Retire the entire dated10-case attach receipt; retain16 independent receipts only in their original scopes. No retained-memory or real-daemon measurement follows from supplied provider inventories. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1991: Remove initial-snapshot sequence-domain, limited-tail/truncation and unknown-output-sequence aliases whose callbacks are removed. The source retains partial-chunk replay and requested-snapshot retry/admission at supplied snapshot/data seams, not equivalent replacement or measured retained-memory proof. Preserve the existing stream/pending-wait capacity aliases only to their retained owning multiplex callbacks with supplied PTY resolutions. Removing the sibling-PTY heartbeat dedup work-count sample does not establish a new decorative fanout budget; the unrelated-PTY cancellation fence survives. Preserve17 unrelated dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, physical provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1983: Remove the reasonless-close selector and narrow census aliases to actual retained runtime and injected-inventory callbacks. The removed synthetic census-dedup and isolated pre-boundary timing cases receive no replacement credit; preserve17 unrelated dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Delete the synthetic watcher-synchronization and partial-reconciliation suites. Retire all affected host-fact, capability-owner, activation-residue and retention-cap aliases because the remaining callbacks do not establish those full claims. Keep exact local hook/timer, byte-stream side effects, exit/disposal, scroll-intent cleanup and deterministic local rank/TTL progression callbacks within their mocked-host scope. No physical paired-host retention, resource budget or equivalent replacement evidence is assigned. The generic broad test command remains a runnable discovery command, never a dated execution receipt. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Delete the complete hook-agent-status projection suite and its two behavioral assertion aliases. Supplied stored hook/title/resume/staleness/rename/question/generation/PTY-retirement/snapshot-version projections are intentionally lost. The separate osc-status-scan gate mentions this source only in existing historical notes and receives no mutation. No measured hidden-output retention, CPU/RSS or resource budget result is claimed by the deleted source or this audit. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Historical pre-audit oracle account for changed sources: The retained unchanged-source receipts are the only current execution evidence; reduced-source assertions await validation. Historical oracle account (not a current claim for changed sources): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Run one byte-identical six-terminal oracle against an isolated headed desktop host and an isolated headless `orca serve` host. Stage at least 1,000,000 xterm cells, hide all six warm managers, emit an initial working title followed by 30 timed real-PTY spinner frames and a final idle title per worktree, and observe a second real session-tabs subscription plus the production renderer store. Require exactly two transport envelopes and two title mutations per worktree, no decorative title crossing either boundary, and positive decoded-envelope bytes. Enable ordinary parking with the lossy retention budget disabled, require exactly one mounted manager and five parked tabs, at most 45% retained cells, no more than 16 MiB heap growth, and under 500 ms timer drift. In headed mode, require the host renderer to remain on its original workspace with zero target terminal managers mounted throughout client park, reveal, and live I/O. While a tab is parked, require authoritative terminal.read to observe new PTY output; reveal it and require the original PTY, a marker within the requested 5,000-row history, the parked marker, and post-reveal input/output. Drive 24 real PTY title trackers in distinct worktrees through 40 decorative spinner frames each, then 40 spinner frames alternating with Cursor's bare native identity redraw, using fake clocks. Require all 2,880 raw chunks exactly and zero host session-tabs publications, serialized bytes, renderer apply calls, and renderer store mutations. Continue decorative evidence beyond 30 minutes, require exactly one refresh per worktree per 15-minute lease through a FIFO spaced by at least 50 ms, and require all 24 viewer statuses to remain working and fresh; then require one exact semantic idle transition and one visible-output chunk per PTY. Require ordinary and interior-Braille title changes to publish. Drive 64 host PTYs through ten 80 ms synthetic title frames, require zero paired client events after the first frame per PTY while all 640 local frames remain observable, attach a late client and require one current frame per PTY, then require semantic bell and idle transitions on both clients. Reconstruct a legacy subscribed stream with no outputPause capability, hide a chatty pane, require no hidden xterm writes, reveal it, and require one authoritative snapshot plus continued live output. Drop and acknowledge output only for one original paired-client stream, prove the fixture process consumed input while the host model advanced and the client stayed stale, then require the command snapshot probe to replace that stream, repaint exact fixture output, preserve authoritative PTY identity and target-tab cardinality, and resume live I/O. For a client without a delivered sequence, require the first numeric probe to establish a baseline without replacement, a same-sequence probe to remain attached, and a later advanced probe to replace only that stream. Withhold the first encrypted shared-control ready frame, start one RPC, trigger a status-probe refresh, and require two connections, one host delivery, successful response, and zero retained request bytes. Admit 128 active streams, reject the 129th as retryable, release one stream, then require the retry to attach and publish a snapshot without multiplying retained subscribers. Disable terminal.paired-parking.v1 and require the same oracle to fail before parking, while the legacy limit-one fallback separately passes. Also preserve truncated first paint, ACK-starved same-PTY recovery, responsive silent-command snapshot probes, dead-stream probe timeout recovery, and queued manual-disconnect fencing.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/remote-runtime-pty-transport-unpublished-host-graph.test.ts", "ORCA_BACKGROUND_LAUNCH=1 pnpm exec playwright test tests/e2e/paired-remote-terminal-host-quit-reconnect-input.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1", @@ -8843,7 +8843,7 @@ "providers": ["local", "daemon", "ssh", "remote-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": [], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous account (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous account (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous account (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous account (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous account (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous account (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous account (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous account (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous account (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous account (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous account (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous account (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous account (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous account (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous account (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous account (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Independently remove one authored/finite Playwright closed-gate app-boot inventory: accept loss of real renderer/preload/main refusal composition, saved-host row and Add/Move hiding/Remove visibility alarms. Keep the unchanged scripted-move rendered dialog lifecycle/reload case. It runs no real WSL migration, host connection, file copy or automation restoration. No selector or Playwright participation cleanup is required because the spec remains. Previous account (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Module layout SSH policy: Retire the multiplexer private-clock saturation registration, the writer saturation transition-count registration and the changed-source dated writer/backpressure receipt. Retained byte delivery, lane order, fairness, settlement and cancellation cases are unvalidated by that historical receipt. No equivalent replacement, new execution, measured performance, live SSH/WSL or cross-platform proof is claimed. The unchanged WSL protocol receipt retains only its original command scope. Historical prior account (preserved verbatim): Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2070 (sources2068/2069): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/hooks-and-hosted-review.spec'). src/main/ssh/ssh-multiplexer-transport-writer.test.ts: reports saturated epochs and their drain boundary exactly once. src/main/runtime/orca-runtime-tests/worktree-removal-archive-hook-gate.spec.ts: removes and records the waiver when the failure is explicitly overridden; removes without an override record when the hook succeeds; removes when the hook is configured but not requested; removes when no archive hook is configured; holds a desktop removal in the same repo until this one is accepted; joins a desktop removal of the same worktree accepted while this one waited. src/main/runtime/orca-runtime-tests/worktree-ps-agent-row-dismissal.spec.ts: drops the row as soon as the user dismisses it, without waiting for the PTY to exit; tells paired clients to republish on the transition and on the dismissal; rejoins a row through its %s handle after pane ownership clears. src/main/runtime/orca-runtime-tests/hooks-and-hosted-review.spec.ts: reports an error when the SSH filesystem provider is unavailable; reports an SSH orca.yaml as an error without reading any filesystem; reports ok for a local repo hook check; reads SSH issue commands as absent and drops writes without touching any filesystem; allows host integration slug helpers for SSH repos through provider-aware GitHub clients; routes runtime GitHub repo identity helpers through the selected WSL project runtime; routes runtime GitHub issue and work-item actions through the selected WSL project runtime; pins explicit origin preference on runtime open-by-number work item lookups; forwards check-details cancellation without local Git overrides. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. The historical receipt/currentCommand selecting src/main/ssh/ssh-multiplexer-transport-writer.test.ts remains historical and is not a passing result for the reduced current command. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Delete the complete tail-wait memo suite: its sole current authored callback checks supplied fromTail/blocked field values. Retire all three broader memoization/equivalence/eviction assertion aliases; they are not backed by retained source callbacks. No scan-work budget, sustained output, physical terminal or resource measurement is assigned to this removed test. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1674 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Local macOS evidence covers main-process pending-output and projection-admission caps, saturated-tail wait detection, direct-SSH source retention/accounting, always-on V1 negotiation with legacy peer fallback, exact provider-generation pause ownership, renderer exit/data ordering, decoder input bounds, bounded reconnect scheduling, and WSL stdio transport settlement through deterministic unit contracts. A joined main/runtime oracle exercises renderer-sourced headed semantics and headless-model snapshot semantics without launching a live paired server. Separately, a macOS-hosted Docker OpenSSH run exercises only the deployed Linux relay and direct SSH provider with an exact 256 KiB source-credit plateau, concurrent PTY typing, fixed-size filesystem frames, Git churn, and owner-lease reconnect. Neither deterministic topology labels nor Docker SSH constitute live headed desktop, headless orca serve, or physical WSL evidence; local/daemon, prior-version daemons, Windows named pipes/ConPTY, folder workspaces, and mixed-version clients also remain uncovered. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous account (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous account (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous account (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous account (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous account (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous account (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous account (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous account (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous account (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous account (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous account (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous account (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous account (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous account (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous account (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous account (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Independently remove one authored/finite Playwright closed-gate app-boot inventory: accept loss of real renderer/preload/main refusal composition, saved-host row and Add/Move hiding/Remove visibility alarms. Keep the unchanged scripted-move rendered dialog lifecycle/reload case. It runs no real WSL migration, host connection, file copy or automation restoration. No selector or Playwright participation cleanup is required because the spec remains. Previous account (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Module layout SSH policy: Retire the multiplexer private-clock saturation registration, the writer saturation transition-count registration and the changed-source dated writer/backpressure receipt. Retained byte delivery, lane order, fairness, settlement and cancellation cases are unvalidated by that historical receipt. No equivalent replacement, new execution, measured performance, live SSH/WSL or cross-platform proof is claimed. The unchanged WSL protocol receipt retains only its original command scope. Historical prior account (preserved verbatim): Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2070 (sources2068/2069): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/hooks-and-hosted-review.spec'). src/main/ssh/ssh-multiplexer-transport-writer.test.ts: reports saturated epochs and their drain boundary exactly once. src/main/runtime/orca-runtime-tests/worktree-removal-archive-hook-gate.spec.ts: removes and records the waiver when the failure is explicitly overridden; removes without an override record when the hook succeeds; removes when the hook is configured but not requested; removes when no archive hook is configured; holds a desktop removal in the same repo until this one is accepted; joins a desktop removal of the same worktree accepted while this one waited. src/main/runtime/orca-runtime-tests/worktree-ps-agent-row-dismissal.spec.ts: drops the row as soon as the user dismisses it, without waiting for the PTY to exit; tells paired clients to republish on the transition and on the dismissal; rejoins a row through its %s handle after pane ownership clears. src/main/runtime/orca-runtime-tests/hooks-and-hosted-review.spec.ts: reports an error when the SSH filesystem provider is unavailable; reports an SSH orca.yaml as an error without reading any filesystem; reports ok for a local repo hook check; reads SSH issue commands as absent and drops writes without touching any filesystem; allows host integration slug helpers for SSH repos through provider-aware GitHub clients; routes runtime GitHub repo identity helpers through the selected WSL project runtime; routes runtime GitHub issue and work-item actions through the selected WSL project runtime; pins explicit origin preference on runtime open-by-number work item lookups; forwards check-details cancellation without local Git overrides. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. The historical receipt/currentCommand selecting src/main/ssh/ssh-multiplexer-transport-writer.test.ts remains historical and is not a passing result for the reduced current command. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Delete the complete tail-wait memo suite: its sole current authored callback checks supplied fromTail/blocked field values. Retire all three broader memoization/equivalence/eviction assertion aliases; they are not backed by retained source callbacks. No scan-work budget, sustained output, physical terminal or resource measurement is assigned to this removed test. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1674 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Local macOS evidence covers main-process pending-output and projection-admission caps, saturated-tail wait detection, direct-SSH source retention/accounting, always-on V1 negotiation with legacy peer fallback, exact provider-generation pause ownership, renderer exit/data ordering, decoder input bounds, bounded reconnect scheduling, and WSL stdio transport settlement through deterministic unit contracts. A joined main/runtime oracle exercises renderer-sourced headed semantics and headless-model snapshot semantics without launching a live paired server. Separately, a macOS-hosted Docker OpenSSH run exercises only the deployed Linux relay and direct SSH provider with an exact 256 KiB source-credit plateau, concurrent PTY typing, fixed-size filesystem frames, Git churn, and owner-lease reconnect. Neither deterministic topology labels nor Docker SSH constitute live headed desktop, headless orca serve, or physical WSL evidence; local/daemon, prior-version daemons, Windows named pipes/ConPTY, folder workspaces, and mixed-version clients also remain uncovered. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://github.com/stablyai/orca/pull/6836", "https://github.com/stablyai/orca/pull/6858", @@ -8851,7 +8851,7 @@ "https://github.com/stablyai/orca/pull/7054" ], "invariant": "High-volume terminal output must stay bounded across daemon socket writes, SSH relay writer/decoder/source retention/reconnect, main model and projection admission, runtime metadata, detectors, and tail checks, main-to-renderer in-flight bytes, renderer scheduler queues, and hidden-output restore without starving focused input. Every new SSH session offers V1 and every same-build relay supports it; legacy delivery is reachable only when capability negotiation proves the peer cannot use V1. Negotiated SSH spans remain exact and contiguous through activation, exit, cancellation, recovery, desktop projection, and required remote replacement; provisional activation data cannot project, reconnect may release it only into the exact attempt's private recovery quarantine after contract validation, and exit seals that quarantine against later same-token frames until ordered admission or exact cancellation proof. Reconnect checkpoint capture freezes exact provider-generation/PTy admission, cancels queued old work, and waits for the running raw completion before exposing its accepted checkpoint; timeout or failure detaches only that PTy's old model, makes the checkpoint unavailable, and releases the bounded fence without closing the shared provider, while the same failure outside an active exact migration remains generation-fatal and overlapping reconnects preserve any earlier outstanding fence. Pending renderer projection IDs are capped and compacted across split remainders, exit preparation owns its renderer fence through finalization, exit-time cancellation transfers published projections before proof commit, generation close fences late proof while draining exact projection waiters, closed-generation identity compacts without weakening stale rejection, canceled source-delivery retirement retains at most one ordered token per PTY, and pause/resume targets only the exact provider generation. Additional subscribers cannot stall the owner, while the required legacy primary retains backpressure.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Previous oracle (preserved verbatim): Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous oracle (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous oracle (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous oracle (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous oracle (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous oracle (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous oracle (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous oracle (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous oracle (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous oracle (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous oracle (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous oracle (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous oracle (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous oracle (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous oracle (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Previous oracle (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Module layout SSH policy: Retire the multiplexer private-clock saturation registration, the writer saturation transition-count registration and the changed-source dated writer/backpressure receipt. Retained byte delivery, lane order, fairness, settlement and cancellation cases are unvalidated by that historical receipt. No equivalent replacement, new execution, measured performance, live SSH/WSL or cross-platform proof is claimed. The unchanged WSL protocol receipt retains only its original command scope. Historical prior account (preserved verbatim): Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2070 (sources2068/2069): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/hooks-and-hosted-review.spec'). src/main/ssh/ssh-multiplexer-transport-writer.test.ts: reports saturated epochs and their drain boundary exactly once. src/main/runtime/orca-runtime-tests/worktree-removal-archive-hook-gate.spec.ts: removes and records the waiver when the failure is explicitly overridden; removes without an override record when the hook succeeds; removes when the hook is configured but not requested; removes when no archive hook is configured; holds a desktop removal in the same repo until this one is accepted; joins a desktop removal of the same worktree accepted while this one waited. src/main/runtime/orca-runtime-tests/worktree-ps-agent-row-dismissal.spec.ts: drops the row as soon as the user dismisses it, without waiting for the PTY to exit; tells paired clients to republish on the transition and on the dismissal; rejoins a row through its %s handle after pane ownership clears. src/main/runtime/orca-runtime-tests/hooks-and-hosted-review.spec.ts: reports an error when the SSH filesystem provider is unavailable; reports an SSH orca.yaml as an error without reading any filesystem; reports ok for a local repo hook check; reads SSH issue commands as absent and drops writes without touching any filesystem; allows host integration slug helpers for SSH repos through provider-aware GitHub clients; routes runtime GitHub repo identity helpers through the selected WSL project runtime; routes runtime GitHub issue and work-item actions through the selected WSL project runtime; pins explicit origin preference on runtime open-by-number work item lookups; forwards check-details cancellation without local Git overrides. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. The historical receipt/currentCommand selecting src/main/ssh/ssh-multiplexer-transport-writer.test.ts remains historical and is not a passing result for the reduced current command. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Delete the complete tail-wait memo suite: its sole current authored callback checks supplied fromTail/blocked field values. Retire all three broader memoization/equivalence/eviction assertion aliases; they are not backed by retained source callbacks. No scan-work budget, sustained output, physical terminal or resource measurement is assigned to this removed test. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1674 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: The current executable slice injects main-process renderer backlog pressure, then asserts ordinary terminal-wait detection without a joined-tail allocation and with full prompt-family continuity, per-PTY and total pending-output caps, a 1,024-ID projection cap with split-remainder compaction, preserved sequenced-tail metadata, active-pending protection ahead of background trimming, exact provider-generation pause ownership, renderer exit/data ordering, and ACK-gated in-flight bounds. The negotiation seam proves every initial connection and reconnect offers V1, the same-build relay advertises V1 without launch flags, old clients remain token-free, and method-not-found peers fall back without installing source ACK publication. The joined source-intake/multiplex oracle blocks renderer or headless serialization, admits one snapshot-covered span and one trailing span, requires replacement reservation only after the authoritative sequence is known, advances upstream ACK eligibility exactly to the covered boundary after SnapshotEnd, and delivers then ACKs the trailing span through the ordinary live path without cancellation. Source-range ledger and multiplex seams additionally require cumulative byte ACK 40 then 100 for one 100-byte frame to release exact byte credit without early source settlement, admit a contiguous higher-generation recovered token while the prior token remains unsettled, and reject stale stream or source generations without releasing in-flight byte credit. Replacement reservation rolls back every earlier span on a later failure, commit requires the exact frozen transfer states, and an authoritatively reclaimed covered span rejects commit without local trim authority while rollback removes the reservation idempotently. The exit-deadline oracle publishes a source-backed desktop projection, advances a fake clock to cancellation, and requires one projection transfer, proof commit, preparation, and final exit with no provider close, retained obligation, duplicate finalization, or process-lifetime cancellation tombstone; a second controlled proof remains pending across generation close and must never publish final exit, while both paths release the renderer preparation lease exactly once. The model-migration oracle accepts span A, blocks span B's raw emulator completion, begins disconnect migration, and proves no checkpoint or reconnect attach can remain at A; releasing B advances the exact checkpoint to B and projects B once. Its fake-clock arm reaches the 10-second deadline, requires checkpoint-unavailable, one exact model reset, zero retained admission charge and timers, and no effect from late raw settlement. Its two-PTY failure arm rejects one running raw callback under an active exact migration and requires one checkpoint-unavailable result, one model reset, zero provider closes, retained sibling checkpoint and transport liveness, zero charges and timers, and an unaffected other generation; a non-migrating sibling oracle still requires generation close. The intake additionally closes 2,048 sequential provider generations into one exact range, rejects stale events across that range, and preserves an out-of-order live gap until its own close. Direct-SSH contracts additionally prove activation-response settlement before the first source frame, claim-gated projection under same-decoder-turn response/data delivery, exact early-ACK reservation through send settlement or same-token retry, recovery completion visible before lease-held frames transfers those frames only into the private quarantine and admits no output until exact checkpoint-to-recoveryEnd coverage, invalid-checkpoint restore retention through response settlement followed by fresh-token retry and one live source frame, isolated saturated-subscriber eviction with healthy owner liveness, preserved legacy-primary backpressure, one-write ownership across write(false)/drain, control selection ahead of queued ordinary PTY input with FIFO lanes and bounded fairness, liveness rebasing, failed-exit retention through late ACK or exact recovery, response-settlement cancellation authority, token-scoped exit cancellation, exact checkpoint-to-recoveryEnd continuity, stale-owner one-shot retry, no physical PTY teardown on recovery failure, exact provider-generation closure and once-only cleanup when recovery cancellation publication or proof rejects, 16 MiB-plus-1 MiB decoder caps, charged per-PTY/session retention budgets, and eight-wide isolated reattach. The live Electron perf oracle adds hidden-output floods, renderer scheduler queue depth, dropped-output-zero normal scenarios, and active key latency budgets before promotion.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Previous oracle (preserved verbatim): Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous oracle (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous oracle (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous oracle (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous oracle (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous oracle (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous oracle (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous oracle (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous oracle (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous oracle (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous oracle (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous oracle (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous oracle (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous oracle (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous oracle (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Previous oracle (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Module layout SSH policy: Retire the multiplexer private-clock saturation registration, the writer saturation transition-count registration and the changed-source dated writer/backpressure receipt. Retained byte delivery, lane order, fairness, settlement and cancellation cases are unvalidated by that historical receipt. No equivalent replacement, new execution, measured performance, live SSH/WSL or cross-platform proof is claimed. The unchanged WSL protocol receipt retains only its original command scope. Historical prior account (preserved verbatim): Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2070 (sources2068/2069): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/hooks-and-hosted-review.spec'). src/main/ssh/ssh-multiplexer-transport-writer.test.ts: reports saturated epochs and their drain boundary exactly once. src/main/runtime/orca-runtime-tests/worktree-removal-archive-hook-gate.spec.ts: removes and records the waiver when the failure is explicitly overridden; removes without an override record when the hook succeeds; removes when the hook is configured but not requested; removes when no archive hook is configured; holds a desktop removal in the same repo until this one is accepted; joins a desktop removal of the same worktree accepted while this one waited. src/main/runtime/orca-runtime-tests/worktree-ps-agent-row-dismissal.spec.ts: drops the row as soon as the user dismisses it, without waiting for the PTY to exit; tells paired clients to republish on the transition and on the dismissal; rejoins a row through its %s handle after pane ownership clears. src/main/runtime/orca-runtime-tests/hooks-and-hosted-review.spec.ts: reports an error when the SSH filesystem provider is unavailable; reports an SSH orca.yaml as an error without reading any filesystem; reports ok for a local repo hook check; reads SSH issue commands as absent and drops writes without touching any filesystem; allows host integration slug helpers for SSH repos through provider-aware GitHub clients; routes runtime GitHub repo identity helpers through the selected WSL project runtime; routes runtime GitHub issue and work-item actions through the selected WSL project runtime; pins explicit origin preference on runtime open-by-number work item lookups; forwards check-details cancellation without local Git overrides. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. The historical receipt/currentCommand selecting src/main/ssh/ssh-multiplexer-transport-writer.test.ts remains historical and is not a passing result for the reduced current command. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Delete the complete tail-wait memo suite: its sole current authored callback checks supplied fromTail/blocked field values. Retire all three broader memoization/equivalence/eviction assertion aliases; they are not backed by retained source callbacks. No scan-work budget, sustained output, physical terminal or resource measurement is assigned to this removed test. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1674 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: The current executable slice injects main-process renderer backlog pressure, then asserts ordinary terminal-wait detection without a joined-tail allocation and with full prompt-family continuity, per-PTY and total pending-output caps, a 1,024-ID projection cap with split-remainder compaction, preserved sequenced-tail metadata, active-pending protection ahead of background trimming, exact provider-generation pause ownership, renderer exit/data ordering, and ACK-gated in-flight bounds. The negotiation seam proves every initial connection and reconnect offers V1, the same-build relay advertises V1 without launch flags, old clients remain token-free, and method-not-found peers fall back without installing source ACK publication. The joined source-intake/multiplex oracle blocks renderer or headless serialization, admits one snapshot-covered span and one trailing span, requires replacement reservation only after the authoritative sequence is known, advances upstream ACK eligibility exactly to the covered boundary after SnapshotEnd, and delivers then ACKs the trailing span through the ordinary live path without cancellation. Source-range ledger and multiplex seams additionally require cumulative byte ACK 40 then 100 for one 100-byte frame to release exact byte credit without early source settlement, admit a contiguous higher-generation recovered token while the prior token remains unsettled, and reject stale stream or source generations without releasing in-flight byte credit. Replacement reservation rolls back every earlier span on a later failure, commit requires the exact frozen transfer states, and an authoritatively reclaimed covered span rejects commit without local trim authority while rollback removes the reservation idempotently. The exit-deadline oracle publishes a source-backed desktop projection, advances a fake clock to cancellation, and requires one projection transfer, proof commit, preparation, and final exit with no provider close, retained obligation, duplicate finalization, or process-lifetime cancellation tombstone; a second controlled proof remains pending across generation close and must never publish final exit, while both paths release the renderer preparation lease exactly once. The model-migration oracle accepts span A, blocks span B's raw emulator completion, begins disconnect migration, and proves no checkpoint or reconnect attach can remain at A; releasing B advances the exact checkpoint to B and projects B once. Its fake-clock arm reaches the 10-second deadline, requires checkpoint-unavailable, one exact model reset, zero retained admission charge and timers, and no effect from late raw settlement. Its two-PTY failure arm rejects one running raw callback under an active exact migration and requires one checkpoint-unavailable result, one model reset, zero provider closes, retained sibling checkpoint and transport liveness, zero charges and timers, and an unaffected other generation; a non-migrating sibling oracle still requires generation close. The intake additionally closes 2,048 sequential provider generations into one exact range, rejects stale events across that range, and preserves an out-of-order live gap until its own close. Direct-SSH contracts additionally prove activation-response settlement before the first source frame, claim-gated projection under same-decoder-turn response/data delivery, exact early-ACK reservation through send settlement or same-token retry, recovery completion visible before lease-held frames transfers those frames only into the private quarantine and admits no output until exact checkpoint-to-recoveryEnd coverage, invalid-checkpoint restore retention through response settlement followed by fresh-token retry and one live source frame, isolated saturated-subscriber eviction with healthy owner liveness, preserved legacy-primary backpressure, one-write ownership across write(false)/drain, control selection ahead of queued ordinary PTY input with FIFO lanes and bounded fairness, liveness rebasing, failed-exit retention through late ACK or exact recovery, response-settlement cancellation authority, token-scoped exit cancellation, exact checkpoint-to-recoveryEnd continuity, stale-owner one-shot retry, no physical PTY teardown on recovery failure, exact provider-generation closure and once-only cleanup when recovery cancellation publication or proof rejects, 16 MiB-plus-1 MiB decoder caps, charged per-PTY/session retention budgets, and eight-wide isolated reattach. The live Electron perf oracle adds hidden-output floods, renderer scheduler queue depth, dropped-output-zero normal scenarios, and active key latency budgets before promotion.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts", @@ -10291,7 +10291,7 @@ "providers": ["local", "daemon", "ssh", "remote-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": [], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. NEXT136 retains only unchanged execution receipts at their original scopes. Reduced-source selectors and retained static assertions have no new execution or equivalent replacement credit.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. NEXT136 retains only unchanged execution receipts at their original scopes. Reduced-source selectors and retained static assertions have no new execution or equivalent replacement credit.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-3800", "https://linear.app/stably/issue/STA-3904", @@ -10301,7 +10301,7 @@ "https://github.com/stablyai/orca/issues/14347" ], "invariant": "Injected orchestration task prompts for recognized agent CLIs must send the prompt body inside one bracketed-paste frame, sanitize embedded ESC bytes, preserve chunk boundaries without losing the frame, and submit exactly once only after the agent can accept Enter. Local worker-start with supported observation must preserve an unobserved turn as start_unknown without revoking authority, closing questions, or triggering a rescue Enter; a worker report during observation must settle normally. Claude and Codex must emit a post-paste composer marker and then settle, or reach the bounded fallback first; every other agent retains the platform delay.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Retained assertion references and unchanged receipts keep their original scopes; retired callbacks and archived receipts provide no current coverage.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Retained assertion references and unchanged receipts keep their original scopes; retired callbacks and archived receipts provide no current coverage.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/shared/agent-launch/startup/agent-prompt-injection.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/orchestration/runs/tasks-dispatch.test.ts src/main/runtime/orchestration/coordinator/coordinator.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/rpc/methods/orchestration/worker/start/readiness/worker-start-prompt-contract.test.ts --reporter=dot", @@ -10528,7 +10528,7 @@ "providers": ["local", "daemon", "ssh", "wsl", "remote-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": [], - "coverageNotes": "Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. CLI ownership cleanup retires low-value test cases, including explicit-current no-read, legacy success/idempotent acceptance, and caller-pane routing examples. Retained refusal and recovery assertions are listed below; dated receipts keep their original scopes. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. The complete federation-lifecycle-settlement suite is removed, losing its fake remote settlement/protocol/acknowledgment examples. Its exact standalone command, testFiles entry and complete assertionRef are retired and prior fields archived. Existing receipts select unrelated migration and compiled CLI journeys and retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2923 accepts loss of the first/replayed attested legacy worker_done CLI acceptance alarms, including exact send parameters, sender pane proof, lifecycle-wait flag and minted mutation identity. The removed fixture supplied settled runtime verdicts; it did not execute settlement authority, durable replay or outage recovery. Reduced selectors have no new execution, fault-control or equivalent replacement credit. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. NEXT136 retains only unchanged execution receipts at their original scopes. Reduced-source selectors and retained static assertions have no new execution or equivalent replacement credit. CLI command/orchestration owner cleanup intentionally retires three registrations selected by this gate: two mock-supplied lifecycle/Run-home rejection projection examples and the complete manually assembled JSON failure-envelope example. The JSON-envelope suite, its exact command token/testFiles entry and whole assertion reference are removed; the retired Run-home relay assertion is removed from the retained lifecycle suite. These boundary projection alarms are lost without equivalent replacement. Retained fail-closed settlement and mutation-recovery cases keep their own scope. Pure owner moves do not invalidate unrelated dated receipts, which retain their exact original bytes; no fresh reduced-selector execution, native or remote evidence is claimed.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. CLI ownership cleanup retires low-value test cases, including explicit-current no-read, legacy success/idempotent acceptance, and caller-pane routing examples. Retained refusal and recovery assertions are listed below; dated receipts keep their original scopes. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. The complete federation-lifecycle-settlement suite is removed, losing its fake remote settlement/protocol/acknowledgment examples. Its exact standalone command, testFiles entry and complete assertionRef are retired and prior fields archived. Existing receipts select unrelated migration and compiled CLI journeys and retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2923 accepts loss of the first/replayed attested legacy worker_done CLI acceptance alarms, including exact send parameters, sender pane proof, lifecycle-wait flag and minted mutation identity. The removed fixture supplied settled runtime verdicts; it did not execute settlement authority, durable replay or outage recovery. Reduced selectors have no new execution, fault-control or equivalent replacement credit. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. NEXT136 retains only unchanged execution receipts at their original scopes. Reduced-source selectors and retained static assertions have no new execution or equivalent replacement credit. CLI command/orchestration owner cleanup intentionally retires three registrations selected by this gate: two mock-supplied lifecycle/Run-home rejection projection examples and the complete manually assembled JSON failure-envelope example. The JSON-envelope suite, its exact command token/testFiles entry and whole assertion reference are removed; the retired Run-home relay assertion is removed from the retained lifecycle suite. These boundary projection alarms are lost without equivalent replacement. Retained fail-closed settlement and mutation-recovery cases keep their own scope. Pure owner moves do not invalidate unrelated dated receipts, which retain their exact original bytes; no fresh reduced-selector execution, native or remote evidence is claimed.", "motivatingLinks": [ "https://github.com/stablyai/orca/pull/11107#discussion_r3663321387", "https://github.com/stablyai/orca/issues/13858", @@ -10538,7 +10538,7 @@ "https://linear.app/stably/issue/STA-4022" ], "invariant": "Starting a worker in the coordinator's current workspace must materialize one inactive terminal tab before worker-start returns, preserve coordinator focus, and remain exactly once after workspace re-entry. After an app update or restart, an exact live legacy worker must fence automatic provider resume, adopt its original PTY into its original background pane, retain readable output, and clear the resume record without spawning, writing, signalling, interrupting, replacing, or focusing the worker. A current-contract worker whose renderer graph identity is temporarily absent must retain its Dispatch capability and settle exactly once from exact hook-attested handle, pane, and process evidence; otherwise only an exact attested coordinator may take over. A worker_done caller may report success only after the owning runtime returns an explicit lifecycle verdict or authoritative reads prove that the exact Task, Dispatch, and worker report receipt settled the expected outcome. Federated terminal settlement must remain replay-eligible until the worker durably acknowledges it, and identical same-outcome retries must converge idempotently. Independently updated clients and worker servers must preserve the negotiated protocol: current peers use Run-home lifecycle settlement, while protocol v1/v2 peers retain their legacy completion path without receiving newer-only fields. A federated worker may accept only the authority defined by its negotiated protocol. An exact existing target workspace must receive a discoverable tab without stealing coordinator focus; if renderer reveal fails, worker-start must expose that the live worker remains background-only. Run and Dispatch checks must resolve through the caller's stable pane identity when a terminal handle is reminted, while a live handle outranks mismatched pane metadata. A nested worker's creator edge requires the current creator pane, process incarnation, and owning Run generation; reminting and rebinding that pane to another Run must remove the stale edge. Explicit legacy terminal inspection remains handle-scoped, and remote or headless worker presentation remains background-only.", - "oracle": "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. The complete federation-lifecycle-settlement suite is removed, losing its fake remote settlement/protocol/acknowledgment examples. Its exact standalone command, testFiles entry and complete assertionRef are retired and prior fields archived. Existing receipts select unrelated migration and compiled CLI journeys and retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2923 accepts loss of the first/replayed attested legacy worker_done CLI acceptance alarms, including exact send parameters, sender pane proof, lifecycle-wait flag and minted mutation identity. The removed fixture supplied settled runtime verdicts; it did not execute settlement authority, durable replay or outage recovery. Reduced selectors have no new execution, fault-control or equivalent replacement credit. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Retained assertion references and unchanged receipts keep their original scopes; retired callbacks and archived receipts provide no current coverage.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. The complete federation-lifecycle-settlement suite is removed, losing its fake remote settlement/protocol/acknowledgment examples. Its exact standalone command, testFiles entry and complete assertionRef are retired and prior fields archived. Existing receipts select unrelated migration and compiled CLI journeys and retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit2923 accepts loss of the first/replayed attested legacy worker_done CLI acceptance alarms, including exact send parameters, sender pane proof, lifecycle-wait flag and minted mutation identity. The removed fixture supplied settled runtime verdicts; it did not execute settlement authority, durable replay or outage recovery. Reduced selectors have no new execution, fault-control or equivalent replacement credit. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Retained assertion references and unchanged receipts keep their original scopes; retired callbacks and archived receipts provide no current coverage.", "commands": [ "ORCA_BACKGROUND_LAUNCH=1 npx vitest run --config config/vitest.config.ts src/main/runtime/rpc/methods/orchestration/messaging/check-worker-federated-attachment.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/cli/orchestration/orchestration.test.ts src/cli/orchestration/messages/orchestration-check-identity.test.ts src/cli/orchestration/workers/orchestration-worker-cli.test.ts src/main/runtime/rpc/methods/orchestration/worker/start/worker-start-composition.test.ts src/main/runtime/rpc/methods/orchestration/messaging/check.test.ts src/main/runtime/rpc/methods/orchestration/messaging/send.test.ts", @@ -12371,14 +12371,14 @@ "providers": ["local", "remote-runtime", "ssh"], "coveredPlatforms": ["macos", "linux"], "coveredProviders": ["local", "remote-runtime", "ssh"], - "coverageNotes": "Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous account (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous account (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous account (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous account (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous account (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous account (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous account (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous account (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous account (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous account (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous account (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous account (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous account (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous account (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous account (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous account (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Independently remove one authored/finite Playwright closed-gate app-boot inventory: accept loss of real renderer/preload/main refusal composition, saved-host row and Add/Move hiding/Remove visibility alarms. Keep the unchanged scripted-move rendered dialog lifecycle/reload case. It runs no real WSL migration, host connection, file copy or automation restoration. No selector or Playwright participation cleanup is required because the spec remains. Previous account (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1645 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1516 removes package-electron-runtime-contract.test.mjs, a copied release-workflow platform and marker matrix. Its workflow wiring alarm is intentionally retired; no actual packaged watcher delivery was exercised and no equivalent replacement is claimed. Unaffected dated execution receipts retain their original scope; changed optional selectors have no fresh full-gate execution result. This audit removes the paired-web stopped-during-outage and late-ready direct-branch examples. The retained pre-ready cancellation, failed-unwatch retry and replay-continuity cases are not equivalent replacements for those ordering alarms. Current web-runtime-client.test.ts assertions below name only its retained pre-ready callback case; replay cases remain in their separate suite. Historical commands and results remain historical. Deterministic tests cover one shared healthy child per runtime process plus at most four bounded fault-quarantine children, native desktop, paired-runtime, and WSL snapshot processes sharing the same eight-physical-child reservation with typed serialized event-driven capacity recovery, including recursive re-wait when crash recovery reclaims an announced slot, one quarantine attempt per watch lifetime, generation-scoped cancellation, bounded termination with removable deadline waiters, replacement-crawl errors that cannot be blessed by a late readiness ack, pre-ready paired-web cancellation registered before an unbounded capacity wait, already-resolved and late physical-exit retry of a rejected paired-web teardown, physical desktop-install and local/SSH PTY teardown, stale-generation rejection, per-child stat bounds, final-overflow RPC delivery, and renderer eviction before terminal callbacks can retry. Destructive local and SSH removal fences both ID-derived and resolved-cwd terminal roots, including sibling-root/cwd combinations with reverse admission rollback, closes descendant relay watches before parent deletion, enumerates authoritative provider/cwd ownership, falls back to daemon spawn cwd before OSC 7, and keeps daemon/relay immediate shutdown pending until native exit or a bounded fail-closed error; a dead relay PID discovered during attach also settles concurrent shutdown before its stale entry is reaped, while Windows runtime watcher deadlines retain one removable physical-close waiter, treat Node's error-close path as positive physical-exit proof, and clear root ownership on late close. The relay policy test maps a standard repository's base, Git common directory, and worktree roots to one healthy supervisor, then proves a shared-child failure recovers those roots in separate quarantine supervisors with overflow and resumed events. Abortable capacity, quarantine, runtime-root, relay, SSH, and relay pre-install setup waits attach one reaction to each shared promise and explicitly remove 10,000 cancelled caller closures while one anchor remains. Built-entry macOS harnesses kill the desktop/runtime and SSH relay watcher children, then require automatic resubscription and later events while the host process and relay PTY survive. SSH bundles the child boundary beside relay.js, requires both artifacts for install completeness, and preserves registration-owned same-root cancellation. The test-value audit corrects the active renderer watch command/file reference to the existing runtime-file-client-watch.test.ts, whose three cases are retained unchanged. They observe synthetic subscription retry/fanout and unsubscribe/unwatch calls, not actual preload/main bookkeeping or physical watcher-process cleanup. The separate read/preview client suite loses forwarding, supplied-error and negative-only examples, retaining binary detection and Unicode SVG decoding. Historical evidence commands/results are unchanged. Test-value audit1293 removes src/main/providers/local-pty/local-pty-provider-session-inventory.test.ts (2 authored cases). Their isolated assertions no longer run; surviving tests are not claimed as equivalent coverage. Fresh execution against the candidate SHA is required. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1412: src/main/workspaces/removal/watcher-removal-gate.test.ts reduced. Loses exact removal-error compatibility with renderer predicate under shared message constants. Kept public refusal outcomes do not establish that renderer classification. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous account (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous account (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous account (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous account (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous account (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous account (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous account (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous account (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous account (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous account (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous account (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous account (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous account (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous account (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous account (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous account (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Independently remove one authored/finite Playwright closed-gate app-boot inventory: accept loss of real renderer/preload/main refusal composition, saved-host row and Add/Move hiding/Remove visibility alarms. Keep the unchanged scripted-move rendered dialog lifecycle/reload case. It runs no real WSL migration, host connection, file copy or automation restoration. No selector or Playwright participation cleanup is required because the spec remains. Previous account (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1645 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1516 removes package-electron-runtime-contract.test.mjs, a copied release-workflow platform and marker matrix. Its workflow wiring alarm is intentionally retired; no actual packaged watcher delivery was exercised and no equivalent replacement is claimed. Unaffected dated execution receipts retain their original scope; changed optional selectors have no fresh full-gate execution result. This audit removes the paired-web stopped-during-outage and late-ready direct-branch examples. The retained pre-ready cancellation, failed-unwatch retry and replay-continuity cases are not equivalent replacements for those ordering alarms. Current web-runtime-client.test.ts assertions below name only its retained pre-ready callback case; replay cases remain in their separate suite. Historical commands and results remain historical. Deterministic tests cover one shared healthy child per runtime process plus at most four bounded fault-quarantine children, native desktop, paired-runtime, and WSL snapshot processes sharing the same eight-physical-child reservation with typed serialized event-driven capacity recovery, including recursive re-wait when crash recovery reclaims an announced slot, one quarantine attempt per watch lifetime, generation-scoped cancellation, bounded termination with removable deadline waiters, replacement-crawl errors that cannot be blessed by a late readiness ack, pre-ready paired-web cancellation registered before an unbounded capacity wait, already-resolved and late physical-exit retry of a rejected paired-web teardown, physical desktop-install and local/SSH PTY teardown, stale-generation rejection, per-child stat bounds, final-overflow RPC delivery, and renderer eviction before terminal callbacks can retry. Destructive local and SSH removal fences both ID-derived and resolved-cwd terminal roots, including sibling-root/cwd combinations with reverse admission rollback, closes descendant relay watches before parent deletion, enumerates authoritative provider/cwd ownership, falls back to daemon spawn cwd before OSC 7, and keeps daemon/relay immediate shutdown pending until native exit or a bounded fail-closed error; a dead relay PID discovered during attach also settles concurrent shutdown before its stale entry is reaped, while Windows runtime watcher deadlines retain one removable physical-close waiter, treat Node's error-close path as positive physical-exit proof, and clear root ownership on late close. The relay policy test maps a standard repository's base, Git common directory, and worktree roots to one healthy supervisor, then proves a shared-child failure recovers those roots in separate quarantine supervisors with overflow and resumed events. Abortable capacity, quarantine, runtime-root, relay, SSH, and relay pre-install setup waits attach one reaction to each shared promise and explicitly remove 10,000 cancelled caller closures while one anchor remains. Built-entry macOS harnesses kill the desktop/runtime and SSH relay watcher children, then require automatic resubscription and later events while the host process and relay PTY survive. SSH bundles the child boundary beside relay.js, requires both artifacts for install completeness, and preserves registration-owned same-root cancellation. The test-value audit corrects the active renderer watch command/file reference to the existing runtime-file-client-watch.test.ts, whose three cases are retained unchanged. They observe synthetic subscription retry/fanout and unsubscribe/unwatch calls, not actual preload/main bookkeeping or physical watcher-process cleanup. The separate read/preview client suite loses forwarding, supplied-error and negative-only examples, retaining binary detection and Unicode SVG decoding. Historical evidence commands/results are unchanged. Test-value audit1293 removes src/main/providers/local-pty/local-pty-provider-session-inventory.test.ts (2 authored cases). Their isolated assertions no longer run; surviving tests are not claimed as equivalent coverage. Fresh execution against the candidate SHA is required. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1412: src/main/workspaces/removal/watcher-removal-gate.test.ts reduced. Loses exact removal-error compatibility with renderer predicate under shared message constants. Kept public refusal outcomes do not establish that renderer classification. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/5308", "https://github.com/stablyai/orca/issues/7547", "https://github.com/stablyai/orca/issues/8212" ], "invariant": "Local runtime and SSH relay roots share one watcher child per host process while healthy and use at most four quarantine children after shard failure; native desktop, paired-runtime, and direct WSL snapshot children consume the same process-wide eight-child reservation and retry transient exhaustion without permanently disabling a root. Cancellation remains owned by its child generation and settles only after acknowledgement or bounded physical exit; paired-web retains cleanup ownership and can cancel before readiness even while capacity is exhausted, retries a failed unwatch after already-resolved or late physical exit without duplicating stream termination, replacements never overlap a still-retiring generation, and aborted or timed-out callers detach immediately even while queued behind same-root relay teardown. Destructive local and SSH cleanup holds one containment-aware watcher/terminal removal fence through descendant watcher and PTY physical exit plus parent Git/filesystem deletion; daemon and relay kill requests retain ownership until the actual subprocess exit callback or an equivalent positive dead-PID proof and propagate rejected signals or a bounded failure otherwise. A crash-fused shared shard moves affected roots into the bounded quarantine pool, a second fused quarantine ends those watch lifetimes, interrupted setup emits overflow after recovery, metadata work stays bounded, and the SSH relay, PTYs, Git, and other filesystem operations never load the watcher native addon or share its fault boundary.", - "oracle": "Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Previous oracle (preserved verbatim): Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous oracle (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous oracle (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous oracle (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous oracle (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous oracle (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous oracle (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous oracle (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous oracle (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous oracle (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous oracle (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous oracle (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous oracle (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous oracle (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous oracle (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Previous oracle (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Fill the global watcher-child reservation, require native desktop, paired-runtime, and WSL setup to retain typed transient capacity and retry after one serialized release, then cancel pending desktop and paired-client watches and require no retry. Hold one live anchor while aborting 10,000 callers at the capacity, quarantine-assignment, runtime-root, relay-setup, and SSH-registration layers; require exactly one explicit waiter and one base-promise reaction to remain, then zero after settlement. Hold an old relay unsubscribe, queue one same-root setup leader and one anchor, then abort 10,000 joiners; require every cancellation to settle before teardown releases, exactly one anchor waiter, and only one later physical install. Advance the former paired-web callback deadline while capacity remains full, require cancellation ownership to stay live, publish the server token before ready, and require files.unwatch to abort setup before a later slot release can install a native root. Reject the first ready-watch unwatch with a physical-exit promise, resolve that exit later, and require a fresh physical unwatch attempt, registry release, and exactly one stream end. Fill one healthy shared child with multiple roots, map a standard SSH repository's base, Git common directory, and worktree roots to one relay supervisor, then inject IPC disconnect-before-exit, queued and active cancellation, operations during termination, kill-without-exit, repeated unkillable generations, initial and recovery timeouts, and shard failure. During local and SSH deletion, attempt a late terminal spawn whose worktree identity and cwd name different roots, enumerate a non-prefix remote PTY by authoritative worktree ownership, withhold daemon/relay exit callbacks, make attach positively prove a concurrent-shutdown PID dead, and make the provider unavailable; require every acquired admission to roll back on rejection, dead-PID proof to settle physical exit before reaping, timeout to retain ownership, and Git/filesystem deletion never to start. Require bounded watcher process count, physical-exit settlement, healthy-root recovery, stale-generation rejection, overflow plus later events, and fail-closed destructive cleanup. Verify relay build hashes and remote install probes require both relay.js and relay-watcher.js. Launch the built relay with a real PTY and Parcel watch, kill only the watcher child, and require the same relay PID and PTY to answer before a replacement child delivers overflow and a post-crash event.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Previous oracle (preserved verbatim): Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous oracle (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous oracle (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous oracle (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous oracle (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous oracle (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous oracle (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous oracle (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous oracle (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous oracle (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous oracle (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous oracle (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous oracle (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous oracle (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous oracle (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Previous oracle (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Fill the global watcher-child reservation, require native desktop, paired-runtime, and WSL setup to retain typed transient capacity and retry after one serialized release, then cancel pending desktop and paired-client watches and require no retry. Hold one live anchor while aborting 10,000 callers at the capacity, quarantine-assignment, runtime-root, relay-setup, and SSH-registration layers; require exactly one explicit waiter and one base-promise reaction to remain, then zero after settlement. Hold an old relay unsubscribe, queue one same-root setup leader and one anchor, then abort 10,000 joiners; require every cancellation to settle before teardown releases, exactly one anchor waiter, and only one later physical install. Advance the former paired-web callback deadline while capacity remains full, require cancellation ownership to stay live, publish the server token before ready, and require files.unwatch to abort setup before a later slot release can install a native root. Reject the first ready-watch unwatch with a physical-exit promise, resolve that exit later, and require a fresh physical unwatch attempt, registry release, and exactly one stream end. Fill one healthy shared child with multiple roots, map a standard SSH repository's base, Git common directory, and worktree roots to one relay supervisor, then inject IPC disconnect-before-exit, queued and active cancellation, operations during termination, kill-without-exit, repeated unkillable generations, initial and recovery timeouts, and shard failure. During local and SSH deletion, attempt a late terminal spawn whose worktree identity and cwd name different roots, enumerate a non-prefix remote PTY by authoritative worktree ownership, withhold daemon/relay exit callbacks, make attach positively prove a concurrent-shutdown PID dead, and make the provider unavailable; require every acquired admission to roll back on rejection, dead-PID proof to settle physical exit before reaping, timeout to retain ownership, and Git/filesystem deletion never to start. Require bounded watcher process count, physical-exit settlement, healthy-root recovery, stale-generation rejection, overflow plus later events, and fail-closed destructive cleanup. Verify relay build hashes and remote install probes require both relay.js and relay-watcher.js. Launch the built relay with a real PTY and Parcel watch, kill only the watcher child, and require the same relay PID and PTY to answer before a replacement child delivers overflow and a post-crash event.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/shared/promise-settlement-waiters.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 1f0564da74e..99963c77012 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -2956,7 +2956,6 @@ "src/main/runtime/integrations/linear/linear-save-markdown.test.ts": 930, "src/main/runtime/workspaces/creation/managed-worktree-create-registry.test.ts": 4143, "src/main/runtime/workspaces/creation/managed-worktree-create-telemetry.test.ts": 4152, - "src/main/runtime/workspaces/removal/missing-worktree-terminal-reconciliation.test.ts": 432, "src/main/runtime/mobile-cloud-relay/testing/desktop-relay-service-installer.test.ts": 48, "src/main/runtime/mobile-cloud-relay/testing/mobile-relay-e2ee.integration.test.ts": 408, "src/main/runtime/mobile-cloud-relay/authentication/relay-auth-coordinator-readiness.test.ts": 580, @@ -2980,7 +2979,6 @@ "src/main/runtime/push/notifications/mobile-notification-replay.test.ts": 43, "src/main/runtime/rpc/server/pairing/mobile-pairing-userdata-path.test.ts": 171, "src/main/runtime/terminal/layout/mobile-presence-lock.test.ts": 25928, - "src/main/runtime/terminal/lifecycle/mobile-session-dead-pty.test.ts": 4708, "src/main/runtime/session-tabs/headless-editor-tabs.test.ts": 57, "src/main/runtime/session-lifecycle/publication/mobile-session-tabs-agent-status-heartbeat.test.ts": 65, "src/main/runtime/terminal/surface/mobile-session-terminal-persistence-retirement.test.ts": 278, @@ -3271,7 +3269,6 @@ "src/main/runtime/rpc/methods/orchestration/worker/release/worker-auto-release-turns.test.ts": 3578, "src/main/runtime/rpc/methods/orchestration/worker/release/worker-auto-release.test.ts": 4863, "src/main/runtime/rpc/methods/orchestration/worker/follow-up/worker-follow-up.test.ts": 4916, - "src/main/runtime/rpc/methods/orchestration/worker/observation/worker-interactive-wait.test.ts": 5579, "src/main/runtime/rpc/methods/orchestration/worker/fleet/worker-list-pagination.test.ts": 4740, "src/main/runtime/rpc/methods/orchestration/worker/output/worker-output.test.ts": 541, "src/main/runtime/rpc/methods/orchestration/worker/release/worker-release-authority-contracts.test.ts": 3949, diff --git a/src/main/runtime/construction/orca-runtime.test.ts b/src/main/runtime/construction/orca-runtime.test.ts index 7c11b8040cf..c1a696a74c8 100644 --- a/src/main/runtime/construction/orca-runtime.test.ts +++ b/src/main/runtime/construction/orca-runtime.test.ts @@ -43,7 +43,6 @@ await import('../orca-runtime-tests/terminal-creation-and-readiness-part-10.spec await import('../orca-runtime-tests/terminal-creation-and-readiness-part-11.spec') await import('../orca-runtime-tests/conpty-stream-read.spec') await import('../orca-runtime-tests/terminal-creation-and-readiness-part-12.spec') -await import('../orca-runtime-tests/terminal-output-and-worker-recovery.spec') await import('../orca-runtime-tests/terminal-output-and-worker-recovery-part-02.spec') await import('../orca-runtime-tests/terminal-output-and-worker-recovery-part-03.spec') await import('../orca-runtime-tests/terminal-output-and-worker-recovery-part-04.spec') @@ -71,7 +70,6 @@ await import('../orca-runtime-tests/mobile-session-tabs-targeted-agent-resume.sp await import('../orca-runtime-tests/mobile-session-tabs-headless-wake-report.spec') await import('../orca-runtime-tests/exit-retirement-activation.spec') await import('../orca-runtime-tests/mobile-creation-and-orchestration.spec') -await import('../orca-runtime-tests/mobile-creation-and-orchestration-part-02.spec') await import('../orca-runtime-tests/mobile-creation-and-orchestration-part-03.spec') await import('../orca-runtime-tests/mobile-creation-and-orchestration-part-04.spec') await import('../orca-runtime-tests/mobile-summaries.spec') diff --git a/src/main/runtime/orca-runtime-tests/mobile-creation-and-orchestration-part-02.spec.ts b/src/main/runtime/orca-runtime-tests/mobile-creation-and-orchestration-part-02.spec.ts deleted file mode 100644 index 309d6f30c51..00000000000 --- a/src/main/runtime/orca-runtime-tests/mobile-creation-and-orchestration-part-02.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { settledWriteStub } from '../../providers/testing/settled-pty-write-stub' -import { describe, expect, it, vi } from 'vitest' -import './runtime-test-module-registration.spec' -import { OrcaRuntimeService } from '../orca-runtime' - -import { - setInMemoryOrchestrationMessages, - store, - syncSinglePty -} from './orca-runtime-test-fixtures.spec' -import { InMemoryOrchestrationMessages } from '../orchestration/mailbox/terminal-pointer/in-memory-orchestration-messages.spec' -import { MessageWake } from '../orchestration/mailbox/runtime-message-waiters' - -describe('OrcaRuntimeService', () => { - it('repoints worker_done when a stale waiter wakes without consuming it', async () => { - vi.useFakeTimers() - try { - const runtime = new OrcaRuntimeService(store) - const db = new InMemoryOrchestrationMessages() - const write = vi.fn().mockReturnValue(true) - setInMemoryOrchestrationMessages(runtime, db) - runtime.setPtyController({ - write, - writeWithSettlement: settledWriteStub(write), - kill: vi.fn(), - getForegroundProcess: async () => null - }) - syncSinglePty(runtime) - - const [terminal] = (await runtime.listTerminals()).terminals - db.setRun({ - id: 'run_stale_waiter', - coordinator_handle: terminal.handle, - coordinator_pane_key: `${terminal.tabId}:${terminal.leafId}` - }) - runtime.onPtyData('pty-1', '\x1b]0;Codex working\x07', 100) - runtime.onPtyData('pty-1', '\x1b]0;Codex done\x07', 101) - write.mockClear() - - const staleWait = runtime.waitForMessage('run:run_stale_waiter', { - typeFilter: ['worker_done'], - timeoutMs: 60_000 - }) - db.insertMessage({ - from: 'term_final_worker', - to: 'run:run_stale_waiter', - subject: 'final worker settled', - type: 'worker_done' - }) - runtime.notifyMessageArrived('run:run_stale_waiter', 'worker_done') - - await expect(staleWait).resolves.toBeInstanceOf(MessageWake) - expect(write).not.toHaveBeenCalled() - - db.insertMessage({ - from: 'term_other_worker', - to: 'run:run_stale_waiter', - subject: 'newer status', - type: 'status' - }) - runtime.deliverPendingMessagesForHandle('run:run_stale_waiter', new Set(['worker_done'])) - await vi.advanceTimersByTimeAsync(500) - - const pointers = () => - write.mock.calls.filter( - ([, payload]) => typeof payload === 'string' && payload.includes('orchestration check') - ) - expect(pointers()).toHaveLength(1) - expect(pointers()[0]?.[1]).toContain('You have 1 orchestration message') - - await vi.advanceTimersByTimeAsync(1_500) - expect(pointers()).toHaveLength(2) - expect(pointers()[1]?.[1]).toContain('You have 1 orchestration message') - db.close() - } finally { - vi.useRealTimers() - } - }) - - // Mail follows the terminal: a window reload does not hold a live-idle terminal's restored mail. -}) diff --git a/src/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts b/src/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts deleted file mode 100644 index 094d7aa182b..00000000000 --- a/src/main/runtime/orca-runtime-tests/terminal-output-and-worker-recovery.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { settledWriteStub } from '../../providers/testing/settled-pty-write-stub' -import { makeAgentStatusStoreWiring } from '../terminal/identity/agent-status-store-wiring.test-fixture' -import { describe, expect, it, vi } from 'vitest' -import { committingSpawn } from './bind-first-pane.test-fixture' -import './runtime-test-module-registration.spec' -import { OrcaRuntimeService } from '../orca-runtime' -import '../../../shared/agent-status/title/agent-title-status' -import '../orca-runtime-test-mocks/setup.spec' -import { waitForMobileSessionTabsEvents } from './mobile-session-tab-event-wait' -import type { RuntimeMobileSessionTabsResult } from '../../../shared/runtime-session-contracts' -import { HEADLESS_LEAF_ID, TEST_WORKTREE_ID, store } from './orca-runtime-test-fixtures.spec' - -describe('OrcaRuntimeService', () => { - // Hook-only OSC 9999 transitions must republish mobile status without a renderer (#7970). - it('republishes mobile session tabs with hook payloads for title-less OSC 9999 transitions', async () => { - const spawn = vi.fn().mockResolvedValue({ id: 'hook-only-pty' }) - const statusWiring = makeAgentStatusStoreWiring() - const runtime = new OrcaRuntimeService(store, undefined, statusWiring.deps) - const uninstallRepublish = statusWiring.attach(runtime) - runtime.setPtyController({ - spawn: committingSpawn(runtime, spawn), - write: () => true, - writeWithSettlement: settledWriteStub(() => true), - kill: () => true, - getForegroundProcess: async () => null - }) - const events: RuntimeMobileSessionTabsResult[] = [] - const unsubscribe = runtime.onMobileSessionTabsChanged((snapshot) => events.push(snapshot)) - - await runtime.createTerminal(`id:${TEST_WORKTREE_ID}`, { - tabId: 'hook-tab', - leafId: HEADLESS_LEAF_ID - }) - events.length = 0 - - runtime.onPtyData( - 'hook-only-pty', - '\x1b]9999;{"state":"working","prompt":"fix the tests","agentType":"opencode"}\x07', - 100 - ) - - await waitForMobileSessionTabsEvents(events, 1) - expect(events).toHaveLength(1) - expect(events[0]?.tabs[0]).toEqual( - expect.objectContaining({ - type: 'terminal', - agentStatus: expect.objectContaining({ - state: 'working', - prompt: 'fix the tests', - agentType: 'opencode' - }) - }) - ) - - runtime.onPtyData( - 'hook-only-pty', - '\x1b]9999;{"state":"waiting","prompt":"fix the tests","agentType":"opencode"}\x07', - 101 - ) - - await waitForMobileSessionTabsEvents(events, 2) - expect(events).toHaveLength(2) - expect(events[1]?.tabs[0]?.type === 'terminal' && events[1].tabs[0].agentStatus).toEqual( - expect.objectContaining({ state: 'waiting' }) - ) - - unsubscribe() - uninstallRepublish() - }) -}) diff --git a/src/main/runtime/rpc/methods/orchestration/worker/observation/worker-interactive-wait.test.ts b/src/main/runtime/rpc/methods/orchestration/worker/observation/worker-interactive-wait.test.ts deleted file mode 100644 index 19f23d68467..00000000000 --- a/src/main/runtime/rpc/methods/orchestration/worker/observation/worker-interactive-wait.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -// Drive real detector/plumbing through RPC to distinguish workers waiting on human prompts (STA-3714, STA-4513). -import { readFileSync } from 'node:fs' -import { join } from 'node:path' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { OrcaRuntimeService } from '../../../../../orca-runtime' -import { OrchestrationDb } from '../../../../../orchestration/db/orchestration-db' -import '../../../../../orchestration/db/contract-constants' -import '../../../../../orchestration/db/messages/mailbox-routing-page' -import '../../../../../orchestration/db/dispatch-row-writer' -import '../../../../../orchestration/db/attempt-outcome-projection' -import { ORCHESTRATION_METHODS } from '../../orchestration' -import { createRootDispatch } from '../../../../../orchestration/db/root-dispatch-test-fixture' - -vi.mock('electron', () => ({ - BrowserWindow: { fromId: vi.fn(() => null) }, - webContents: { fromId: vi.fn(() => null) }, - ipcMain: { on: vi.fn(), removeListener: vi.fn() }, - app: { getPath: vi.fn(() => '/tmp') } -})) - -const LEAF_ID = '22222222-2222-4222-8222-222222222222' -const TAB_ID = 'tab-worker' -const WORKTREE_ID = 'wt-worker' -const PTY_ID = 'pty-worker' - -// Captured verbatim from cursor-agent 2026.08.11-e8db854 driven through Orca. -function fixture(name: string): string { - return readFileSync(join(__dirname, '../../../../../__fixtures__', `${name}.txt`), 'utf8') -} - -function workerShowMethod() { - const method = ORCHESTRATION_METHODS.find( - (candidate) => candidate.name === 'orchestration.workerShow' - ) - if (!method) { - throw new Error('Missing method orchestration.workerShow') - } - return method -} - -describe('worker-show interactive wait (STA-3714, STA-4513)', () => { - let db: OrchestrationDb | undefined - - afterEach(() => db?.close()) - - async function showWorkerPaneServing(paneOutput: string, opts?: { breakIdentity?: boolean }) { - const runtime = new OrcaRuntimeService(null) - const internals = runtime as unknown as { - resolveTerminalWorkspaceLaunchScope: (selector: string) => Promise - } - vi.spyOn(internals, 'resolveTerminalWorkspaceLaunchScope').mockResolvedValue({ - id: WORKTREE_ID, - path: '/repo/app', - connectionId: null, - repo: null, - folderWorkspace: null - }) - runtime.setPtyController({ - spawn: vi.fn().mockResolvedValue({ id: PTY_ID, incarnationId: 'inc-1' }), - write: () => true, - kill: () => true, - getForegroundProcess: async () => 'cursor-agent' - }) - const terminal = await runtime.createTerminal(`id:${WORKTREE_ID}`, { - tabId: TAB_ID, - leafId: LEAF_ID, - title: 'worker' - }) - runtime.attachWindow(1) - runtime.syncWindowGraph(1, { - tabs: [ - { - tabId: TAB_ID, - worktreeId: WORKTREE_ID, - title: 'worker', - activeLeafId: LEAF_ID, - layout: null - } - ], - leaves: [ - { - tabId: TAB_ID, - worktreeId: WORKTREE_ID, - leafId: LEAF_ID, - paneRuntimeId: 1, - ptyId: PTY_ID, - // cursor-agent's spinner title, identical whether it runs or waits. - paneTitle: '⠇ Cursor Agent' - } - ] - }) - runtime.onPtyData(PTY_ID, paneOutput, Date.now()) - - db = new OrchestrationDb(':memory:') - runtime.setOrchestrationDb(db) - const run = db.createRun({ - objective: 'supervise lanes', - coordinatorHandle: 'term_coord', - coordinatorPaneKey: 'tab_coord:leaf_coord' - }) - const task = db.createTask({ spec: 'run the suite', runId: run.id }) - const paneKey = runtime.getTerminalPaneKey(terminal.handle) - const incarnation = runtime.getTerminalProcessIncarnation(terminal.handle) - if (!paneKey || !incarnation) { - throw new Error('Runtime did not expose the worker pane identity.') - } - const dispatch = createRootDispatch( - db, - task.id, - terminal.handle, - paneKey, - 'launch-hash', // A dispatch recorded against a process that has since been replaced. - opts?.breakIdentity === true ? `${incarnation}:replaced` : incarnation - ) - - const method = workerShowMethod() - return method.handler(method.params?.parse({ dispatch: dispatch.id }), { runtime }) - } - - it('names the pending prompt on the observation a coordinator polls', async () => { - const result = await showWorkerPaneServing(fixture('cursor-agent-approval-prompt')) - - expect(result).toMatchObject({ - observation: { - exactWorker: true, - agentWait: { - source: 'prompt-text', - reason: 'agent-approval-prompt', - since: expect.any(Number) - } - } - }) - }) -}) diff --git a/src/main/runtime/terminal/lifecycle/mobile-session-dead-pty.test.ts b/src/main/runtime/terminal/lifecycle/mobile-session-dead-pty.test.ts deleted file mode 100644 index e7004f46437..00000000000 --- a/src/main/runtime/terminal/lifecycle/mobile-session-dead-pty.test.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { expect, it, vi } from 'vitest' -import { rmSync } from 'node:fs' -import { DaemonPtyAdapter } from '../../../daemon/providers/adapter/daemon-pty-adapter' -import { DaemonPtyRouter } from '../../../daemon/providers/routing/daemon-pty-router' -import { DegradedDaemonPtyProvider } from '../../../daemon/providers/routing/degraded-daemon-pty-provider' -import { LocalPtyProvider } from '../../../providers/local-pty/local-pty-provider' -import { - createMockSubprocess, - startDaemonAdapterHarness, - waitFor -} from '../../../daemon/providers/adapter/testing/daemon-pty-adapter-test-harness' -import type { IPtyProvider } from '../../../providers/pty/provider-contract' -import { PtyWriteUnavailableError } from '../../../providers/pty/write-unavailable-error' - -await import('../../orca-runtime-tests/runtime-test-module-registration.spec') -await import('../../orca-runtime-tests/orca-runtime-test-lifecycle.spec') -await import('../../orca-runtime-tests/runtime-test-module-registration.spec') -const { OrcaRuntimeService } = await import('../../orca-runtime') -const { - HEADLESS_LEAF_ID, - TEST_WORKTREE_ID, - TEST_WORKTREE_PATH, - makeRuntimeStoreWithWorkspaceSession, - makeWorkspaceSessionWithHeadlessTerminal -} = await import('../../orca-runtime-tests/orca-runtime-test-fixtures.spec') - -const providerSession = { key: 'session_id' as const, id: 'codex-conversation' } - -function mobileChat(provider: IPtyProvider) { - const { runtimeStore } = makeRuntimeStoreWithWorkspaceSession( - makeWorkspaceSessionWithHeadlessTerminal() - ) - const runtime = new OrcaRuntimeService(runtimeStore) - const spawn = vi.fn() - const write = vi.fn(() => true) - runtime.setPtyController({ - spawn, - write, - kill: () => true, - getForegroundProcess: async () => null, - hasPty: (id) => provider.hasPty?.(id) ?? null, - getPtyAvailability: (id) => provider.getPtyAvailability?.(id) ?? null, - listProcesses: () => provider.listProcesses() - }) - runtime.attachWindow(1) - let snapshotVersion = 0 - const publish = (ptyId: string) => - runtime.syncWindowGraph(1, { - tabs: [ - { - tabId: 'host-tab', - worktreeId: TEST_WORKTREE_ID, - title: 'Codex', - activeLeafId: HEADLESS_LEAF_ID, - layout: null - } - ], - leaves: [ - { - tabId: 'host-tab', - worktreeId: TEST_WORKTREE_ID, - leafId: HEADLESS_LEAF_ID, - paneRuntimeId: 1, - ptyId, - paneTitle: 'Codex' - } - ], - mobileSessionTabs: [ - { - worktree: TEST_WORKTREE_ID, - publicationEpoch: 'renderer', - snapshotVersion: ++snapshotVersion, - activeGroupId: null, - activeTabId: 'host-tab', - activeTabType: 'terminal', - tabs: [ - { - type: 'terminal', - id: `host-tab::${HEADLESS_LEAF_ID}`, - parentTabId: 'host-tab', - leafId: HEADLESS_LEAF_ID, - ptyId, - title: 'Codex', - isActive: true, - agentStatus: { - agentType: 'codex', - state: 'waiting', - prompt: '', - updatedAt: 1, - stateStartedAt: 1, - stateHistory: [], - paneKey: `host-tab:${HEADLESS_LEAF_ID}`, - providerSession - } - } - ] - } - ] - }) - const read = () => runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`) - const published = () => runtime['getMobileSessionTabsForWorktree'](TEST_WORKTREE_ID) - return { runtime, publish, read, published, spawn, write } -} - -it.each(['direct', 'router', 'degraded'] as const)( - 'keeps an unattached retained conversation ready, then withholds a gone PTY through %s', - async (route) => { - const subprocesses: ReturnType[] = [] - const harness = await startDaemonAdapterHarness(() => { - const subprocess = createMockSubprocess() - subprocesses.push(subprocess) - return subprocess - }) - const owner = new DaemonPtyAdapter(harness) - try { - await harness.adapter.spawn({ - sessionId: 'persisted-pty', - cwd: TEST_WORKTREE_PATH, - cols: 80, - rows: 24 - }) - harness.adapter.dispose() - const provider = - route === 'direct' - ? owner - : route === 'router' - ? new DaemonPtyRouter({ current: owner, legacy: [] }) - : new DegradedDaemonPtyProvider({ - current: owner, - legacy: [], - fallback: new LocalPtyProvider() - }) - const chat = mobileChat(provider) - chat.publish('persisted-pty') - const before = await chat.read() - expect(owner.hasPty('persisted-pty')).toBe(false) - expect(before.tabs[0]).toMatchObject({ status: 'ready', terminal: expect.any(String) }) - const original = before.tabs[0] - if (original?.type !== 'terminal') { - throw new Error('Expected the retained terminal') - } - - await owner.attach('persisted-pty') - expect((await chat.read()).tabs[0]).toHaveProperty('terminal', original.terminal) - expect(subprocesses).toHaveLength(1) - subprocesses[0]?._simulateExit(0) - await waitFor(() => !owner.hasPty('persisted-pty')) - expect((await chat.read()).tabs[0]).toMatchObject({ - status: 'pending-handle', - terminal: null - }) - chat.publish('persisted-pty') - expect(chat.published().tabs[0]).toMatchObject({ status: 'pending-handle', terminal: null }) - - await owner.spawn({ - sessionId: 'replacement-pty', - cwd: TEST_WORKTREE_PATH, - cols: 80, - rows: 24 - }) - chat.publish('replacement-pty') - const restored = (await chat.read()).tabs[0] - expect(restored).toMatchObject({ - id: original.id, - status: 'ready', - terminal: expect.any(String), - agentStatus: expect.objectContaining({ providerSession }) - }) - expect(restored).not.toHaveProperty('terminal', original.terminal) - expect(chat.spawn).not.toHaveBeenCalled() - expect(chat.write).not.toHaveBeenCalled() - expect(subprocesses.every((subprocess) => subprocess.write.mock.calls.length === 0)).toBe( - true - ) - } finally { - owner.dispose() - harness.adapter.dispose() - await harness.server.shutdown() - rmSync(harness.dir, { recursive: true, force: true }) - } - } -) - -it('withholds an attached PTY during endpoint recovery and restores its same conversation without replay', async () => { - const subprocess = createMockSubprocess() - const harness = await startDaemonAdapterHarness(() => subprocess) - const respawn = vi.fn(async () => {}) - const owner = new DaemonPtyAdapter({ ...harness, respawn }) - try { - await owner.spawn({ sessionId: 'persisted-pty', cwd: TEST_WORKTREE_PATH, cols: 80, rows: 24 }) - const chat = mobileChat(owner) - chat.publish('persisted-pty') - const original = (await chat.read()).tabs[0] - if (original?.type !== 'terminal') { - throw new Error('Expected the attached terminal') - } - expect(original.status).toBe('ready') - const unavailable = new Promise((resolve) => owner.onWriteUnavailable(() => resolve())) - owner['client'].disconnect() - expect(() => owner.write('persisted-pty', 'refused')).toThrow(PtyWriteUnavailableError) - await unavailable - expect(owner.hasPty('persisted-pty')).toBe(true) - expect(chat.published().tabs[0]).toMatchObject({ status: 'pending-handle', terminal: null }) - - // Successful inventory reconnects transport but does not clear the remount fence. - expect((await chat.read()).tabs[0]).toMatchObject({ status: 'pending-handle', terminal: null }) - await owner.attach('persisted-pty') - expect((await chat.read()).tabs[0]).toMatchObject({ - id: original.id, - status: 'ready', - terminal: original.terminal, - agentStatus: expect.objectContaining({ providerSession }) - }) - expect(subprocess.write).not.toHaveBeenCalled() - expect(chat.spawn).not.toHaveBeenCalled() - expect(chat.write).not.toHaveBeenCalled() - expect(respawn).not.toHaveBeenCalled() - - respawn.mockRejectedValue(new Error('daemon remains unavailable')) - await harness.server.shutdown() - await waitFor(() => owner.getDaemonIdentity() === null) - expect((await chat.read()).tabs[0]).toMatchObject({ status: 'pending-handle', terminal: null }) - expect(owner.hasPty('persisted-pty')).toBe(true) - expect(subprocess.write).not.toHaveBeenCalled() - expect(chat.spawn).not.toHaveBeenCalled() - } finally { - owner.dispose() - harness.adapter.dispose() - await harness.server.shutdown() - rmSync(harness.dir, { recursive: true, force: true }) - } -}) diff --git a/src/main/runtime/workspaces/removal/missing-worktree-terminal-reconciliation.test.ts b/src/main/runtime/workspaces/removal/missing-worktree-terminal-reconciliation.test.ts deleted file mode 100644 index ccadd4d1cb1..00000000000 --- a/src/main/runtime/workspaces/removal/missing-worktree-terminal-reconciliation.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import type { IPtyProvider } from '../../../providers/pty/provider-contract' -import type { Repo } from '../../../../shared/repo-types' -import type { OrcaRuntimeService } from '../../orca-runtime' -import { stopMissingWorktreeTerminals } from './missing-worktree-terminal-reconciliation' -import { createAdapter } from '../../../daemon/providers/routing/testing/daemon-pty-router-test-fixture' -import { DaemonPtyRouter } from '../../../daemon/providers/routing/daemon-pty-router' - -function createProvider(sessionIds: string[]): IPtyProvider { - return { - listProcesses: vi.fn(async () => - sessionIds.map((id) => ({ id, cwd: '/workspace', title: 'shell' })) - ), - shutdown: vi.fn(async () => {}) - } as unknown as IPtyProvider -} - -function createRuntime(): OrcaRuntimeService { - return { - stopTerminalsForWorktree: vi.fn(async () => ({ stopped: 0 })) - } as unknown as OrcaRuntimeService -} - -const localRepo: Repo = { - id: 'repo-1', - path: '/workspace/repo', - displayName: 'Repo', - badgeColor: '#000', - addedAt: 1 -} - -describe('stopMissingWorktreeTerminals', () => { - it('stops only locally owned worktrees absent from the authoritative scan', async () => { - const deletedId = 'repo-1::/workspace/deleted' - const survivingId = 'repo-1::/workspace/surviving' - const provider = createProvider([ - `${deletedId}@@deleted-session`, - `${survivingId}@@surviving-session` - ]) - const runtime = createRuntime() - - const result = await stopMissingWorktreeTerminals( - localRepo, - [deletedId, survivingId, 'repo-2::/workspace/other'], - [survivingId], - { - runtime, - getLocalProvider: () => provider - } - ) - - expect(result).toEqual({ stoppedWorktreeIds: [deletedId] }) - expect(provider.shutdown).toHaveBeenCalledWith( - `${deletedId}@@deleted-session`, - expect.objectContaining({ immediate: true }) - ) - expect(provider.shutdown).not.toHaveBeenCalledWith( - `${survivingId}@@surviving-session`, - expect.anything() - ) - }) - - it.each([false, true])( - 'stops multiple workspaces through scoped daemon listings (unrelated duplicate: %s)', - async (duplicate) => { - const ids = ['repo-1::/workspace/a', 'repo-1::/workspace/b'] - const targetSessions = ids.map((id) => `${id}@@session`) - const currentSessions = [...targetSessions, ...(duplicate ? ['unrelated'] : [])] - const current = createAdapter('current', currentSessions) - const legacy = createAdapter('legacy', duplicate ? ['unrelated'] : []) - const provider = new DaemonPtyRouter({ current, legacy: [legacy] }) - - await expect( - stopMissingWorktreeTerminals(localRepo, ids, [], { - runtime: createRuntime(), - getLocalProvider: () => provider - }) - ).resolves.toEqual({ stoppedWorktreeIds: ids }) - - expect(currentSessions).toEqual(duplicate ? ['unrelated'] : []) - expect( - vi - .mocked(current.shutdown) - .mock.calls.map(([id]) => id) - .sort() - ).toEqual(targetSessions) - expect(legacy.shutdown).not.toHaveBeenCalled() - if (!duplicate) { - expect(current.listProcesses).toHaveBeenCalledOnce() - } - } - ) -}) From d0738019906480ede8502e0da89943dda75505d8 Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 06:16:22 +0000 Subject: [PATCH 023/178] fix(automations): release a run terminal the user closed on the next tick (#41846) The retention sweep trusted a closed pane's still-resolvable handle, so a closed run terminal kept its ids and blocked the automation-run cleanup reason. runOwnHandle now also requires the PTY's exit verdict, and a slept pane keeps its keys while the layout still binds the run's PTY. --- .../launch/automation-terminal-launch.ts | 16 ++++++---- .../automations/runtime-automation-service.ts | 21 +++++--------- .../hook-review-blocked-run-terminals.ts | 15 ++++------ .../run-terminal-retention.ts | 15 ++++++---- .../run-terminal-workspace-session.ts | 29 +++++++++++++++++++ 5 files changed, 61 insertions(+), 35 deletions(-) create mode 100644 src/main/automations/terminal-retention/run-terminal-workspace-session.ts diff --git a/src/main/automations/launch/automation-terminal-launch.ts b/src/main/automations/launch/automation-terminal-launch.ts index f329ca2015c..6c603021abd 100644 --- a/src/main/automations/launch/automation-terminal-launch.ts +++ b/src/main/automations/launch/automation-terminal-launch.ts @@ -286,17 +286,21 @@ async function resolveLocalExistingWorkspace( return selected } -/** Reject reused pane handles whose PTY no longer belongs to this run. */ -export function runOwnHandle( - runtime: Pick, - run: AutomationRun -): string | null { +export type RunOwnHandleRuntime = Pick< + OrcaRuntimeService, + 'getTerminalHandleForPaneKey' | 'getTerminalPtyIdForHandle' | 'getPtyLivenessVerdict' +> + +/** Reject reused pane handles whose PTY no longer belongs to this run, or has exited. */ +export function runOwnHandle(runtime: RunOwnHandleRuntime, run: AutomationRun): string | null { const handle = run.terminalPaneKey ? runtime.getTerminalHandleForPaneKey(run.terminalPaneKey) : null + // A closed pane's handle still resolves to its PTY for exit waits; only the exit verdict tells. return handle && run.terminalPtyId && - runtime.getTerminalPtyIdForHandle(handle) === run.terminalPtyId + runtime.getTerminalPtyIdForHandle(handle) === run.terminalPtyId && + runtime.getPtyLivenessVerdict(run.terminalPtyId)?.status !== 'exited' ? handle : null } diff --git a/src/main/automations/runtime-automation-service.ts b/src/main/automations/runtime-automation-service.ts index f1d237958ca..eb4896978e0 100644 --- a/src/main/automations/runtime-automation-service.ts +++ b/src/main/automations/runtime-automation-service.ts @@ -5,7 +5,6 @@ import { publishedLaunchTarget } from './launch/automation-agent-run-result' import { launchAutomationChatAgent } from './launch/automation-chat-agent-launch' import { tuiAgentDisplayName } from '../../shared/agent-launch/catalog/tui-agent-display-names' import type { Store } from '../persistence/loading-store/store' -import type { AutomationRun } from '../../shared/automations/history/automation-run' import type { OrcaRuntimeService } from '../runtime/orca-runtime' import { AutomationService } from './automation-service' import { @@ -23,6 +22,10 @@ import { createRuntimeAutomationRunTerminalObserver } from './observation/runtime-terminal-run-observer' import { closeEarlierHookReviewRunTerminals } from './terminal-retention/hook-review-blocked-run-terminals' +import { + runPaneKeepsRunPty, + runTabWasFocused +} from './terminal-retention/run-terminal-workspace-session' import { automationRunSetupSkip } from './launch/automation-run-setup-skip' import { automationSystemSettled } from './dispatch/automation-wake-settle' @@ -212,16 +215,6 @@ export function createRuntimeAutomationService(input: { return service } -/** A desktop visit stamps the shown tab's focus; main-created run tabs start unstamped. */ -function runTabWasFocused(runtime: OrcaRuntimeService, run: AutomationRun): boolean { - const tabs = run.workspaceId - ? runtime.getWorkspaceSessionForWorktree(run.workspaceId)?.unifiedTabs?.[run.workspaceId] - : undefined - return Boolean( - tabs?.some((tab) => tab.id === run.terminalSessionId && tab.lastFocusedAt !== undefined) - ) -} - function createRuntimeRunTerminalRetention( service: AutomationService, store: Store, @@ -236,6 +229,7 @@ function createRuntimeRunTerminalRetention( return run.terminalPtyId ? runtime.readTerminalClientUse(run.terminalPtyId) : 'unknown' }, runTerminalAlive: (run) => runOwnHandle(runtime, run) !== null, + runPaneKept: (run) => runPaneKeepsRunPty(runtime, run), shellAloneAtPrompt: (run) => run.terminalPtyId ? runtime.confirmTerminalShellAlone(run.terminalPtyId) @@ -284,7 +278,7 @@ function createRuntimeRunTerminalRetention( }, inAutomationLane: (automationId, operation) => service.runInAutomationLane(automationId, operation), - forgetRunTerminal: async (run) => { + forgetRunTerminal: async (run, keepPane) => { // An idle shell proves a dispatched run exited without a completion result. const unfinished = run.status === 'dispatched' await service.markDispatchResult({ @@ -293,8 +287,7 @@ function createRuntimeRunTerminalRetention( error: unfinished ? (run.error ?? 'The agent exited without reporting completion.') : run.error, - terminalSessionId: null, - terminalPaneKey: null, + ...(keepPane ? {} : { terminalSessionId: null, terminalPaneKey: null }), terminalPtyId: null }) } diff --git a/src/main/automations/terminal-retention/hook-review-blocked-run-terminals.ts b/src/main/automations/terminal-retention/hook-review-blocked-run-terminals.ts index 9f66aba7d78..ebf7dca7262 100644 --- a/src/main/automations/terminal-retention/hook-review-blocked-run-terminals.ts +++ b/src/main/automations/terminal-retention/hook-review-blocked-run-terminals.ts @@ -4,20 +4,17 @@ import type { } from '../../../shared/automations/history/automation-run' import { automationRunRecency } from '../../../shared/automations/history/automation-session-reuse' import type { Store } from '../../persistence/loading-store/store' -import { runOwnHandle } from '../launch/automation-terminal-launch' +import { runOwnHandle, type RunOwnHandleRuntime } from '../launch/automation-terminal-launch' import { AGENT_HOOKS_REVIEW_BLOCKED_ERROR, type AutomationRunTerminalHost } from '../observation/runtime-terminal-run-observer' -type HookReviewTerminalHost = Pick< - AutomationRunTerminalHost, - 'getTerminalHandleForPaneKey' | 'waitForTerminal' -> & { - getTerminalPtyIdForHandle(handle: string): string | null - readTerminalClientUse(ptyId: string): 'used' | 'unused' | 'unknown' - closeTerminal(handle: string): Promise -} +type HookReviewTerminalHost = Pick & + RunOwnHandleRuntime & { + readTerminalClientUse(ptyId: string): 'used' | 'unused' | 'unknown' + closeTerminal(handle: string): Promise + } async function isStillOnHookReview( runtime: HookReviewTerminalHost, diff --git a/src/main/automations/terminal-retention/run-terminal-retention.ts b/src/main/automations/terminal-retention/run-terminal-retention.ts index 5932f05ccde..85ac519a29b 100644 --- a/src/main/automations/terminal-retention/run-terminal-retention.ts +++ b/src/main/automations/terminal-retention/run-terminal-retention.ts @@ -12,8 +12,10 @@ export type RunTerminalRetentionDeps = { listRuns: () => readonly AutomationRun[] /** Used, viewed, or unknown-use terminals belong to the user and must stay open. */ terminalClientUse: (run: AutomationRun) => 'used' | 'unused' | 'unknown' - /** True only while the pane holds this run's PTY. */ + /** True only while the pane holds this run's PTY and it has not exited. */ runTerminalAlive: (run: AutomationRun) => boolean + /** True while the workspace layout still binds the run's pane to its PTY, as a sleep does to resume. */ + runPaneKept: (run: AutomationRun) => boolean /** Require fresh proof that only an idle shell remains in the PTY. */ shellAloneAtPrompt: (run: AutomationRun) => Promise /** Close only this run's pane and PTY; preserve user-created splits. */ @@ -21,8 +23,8 @@ export type RunTerminalRetentionDeps = { runOwnsWorkspace: (run: AutomationRun) => boolean /** Preserve the resumable tab; return false if another terminal is live or sleep fails. */ sleepRunWorkspace: (run: AutomationRun) => Promise - /** Clear terminal references while preserving status, error and output. */ - forgetRunTerminal: (run: AutomationRun) => Promise + /** Clear terminal references while preserving status, error and output; keepPane clears only the PTY. */ + forgetRunTerminal: (run: AutomationRun, keepPane?: boolean) => Promise /** Serialize retention with dispatch to protect reused panes. */ inAutomationLane: (automationId: string, operation: () => Promise) => Promise now?: () => number @@ -42,8 +44,8 @@ export function createRunTerminalRetention(deps: RunTerminalRetentionDeps): RunT const firstSeenAt = new Map() let sweeping: Promise | null = null - const forget = async (run: AutomationRun): Promise => { - await deps.forgetRunTerminal(run) + const forget = async (run: AutomationRun, keepPane = false): Promise => { + await deps.forgetRunTerminal(run, keepPane) firstSeenAt.delete(run.id) } @@ -82,7 +84,8 @@ export function createRunTerminalRetention(deps: RunTerminalRetentionDeps): RunT try { if (!deps.runTerminalAlive(run)) { if (isFinalAutomationRunStatus(run.status)) { - await forget(run) + // A slept pane stays resumable from its keys; a closed one leaves nothing to open. + await forget(run, deps.runPaneKept(run)) } continue } diff --git a/src/main/automations/terminal-retention/run-terminal-workspace-session.ts b/src/main/automations/terminal-retention/run-terminal-workspace-session.ts new file mode 100644 index 00000000000..1a46cabf2f8 --- /dev/null +++ b/src/main/automations/terminal-retention/run-terminal-workspace-session.ts @@ -0,0 +1,29 @@ +import type { AutomationRun } from '../../../shared/automations/history/automation-run' +import { parsePaneKey } from '../../../shared/workspace-layout/stable-pane-id' +import type { OrcaRuntimeService } from '../../runtime/orca-runtime' + +type WorkspaceSessionReader = Pick + +/** A desktop visit stamps the shown tab's focus; main-created run tabs start unstamped. */ +export function runTabWasFocused(runtime: WorkspaceSessionReader, run: AutomationRun): boolean { + const tabs = run.workspaceId + ? runtime.getWorkspaceSessionForWorktree(run.workspaceId)?.unifiedTabs?.[run.workspaceId] + : undefined + return Boolean( + tabs?.some((tab) => tab.id === run.terminalSessionId && tab.lastFocusedAt !== undefined) + ) +} + +/** A sleep keeps the run's pane bound to its PTY to resume; a close or a respawned pane does not. */ +export function runPaneKeepsRunPty(runtime: WorkspaceSessionReader, run: AutomationRun): boolean { + const pane = parsePaneKey(run.terminalPaneKey ?? '') + const layout = + pane && run.workspaceId + ? runtime.getWorkspaceSessionForWorktree(run.workspaceId)?.terminalLayoutsByTabId?.[ + pane.tabId + ] + : undefined + return Boolean( + pane && run.terminalPtyId && layout?.ptyIdsByLeafId?.[pane.leafId] === run.terminalPtyId + ) +} From c68da3937f45a5bc481b0acf105da1c976560e13 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 12 Nov 2026 22:24:26 -0800 Subject: [PATCH 024/178] fix(ui): 36px rows and 13px text in agent launch menus (#41853) * fix(ui): make agent launch menu rows 36px with 13px text * fix(ui): keep muted empty-state styling inside the launch row --- .../agent/AgentLaunchPickerMenu.tsx | 1 - .../agent/AgentLaunchPickerMenuRow.tsx | 3 +- .../components/tab-bar/QuickLaunchButton.tsx | 29 +++++++++---------- src/renderer/src/components/ui/button.tsx | 2 ++ .../src/components/ui/dropdown-menu.tsx | 4 +-- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/renderer/src/components/agent/AgentLaunchPickerMenu.tsx b/src/renderer/src/components/agent/AgentLaunchPickerMenu.tsx index f1c4748a4ad..ba8280941f2 100644 --- a/src/renderer/src/components/agent/AgentLaunchPickerMenu.tsx +++ b/src/renderer/src/components/agent/AgentLaunchPickerMenu.tsx @@ -204,7 +204,6 @@ export function AgentLaunchPickerMenu({ > diff --git a/src/renderer/src/components/tab-bar/QuickLaunchButton.tsx b/src/renderer/src/components/tab-bar/QuickLaunchButton.tsx index fe31b4ba78b..2e4a5e20d0a 100644 --- a/src/renderer/src/components/tab-bar/QuickLaunchButton.tsx +++ b/src/renderer/src/components/tab-bar/QuickLaunchButton.tsx @@ -240,23 +240,22 @@ export function ProjectedAgentMenuItems({ return ( <> {(detectedIds === null && (detectionFailed || needsServerUpdate)) || rows.length === 0 ? ( - - {needsServerUpdate - ? t( - 'auto.components.tab.bar.QuickLaunchButton.terminalListNeedsServerUpdate', - 'Update Orca on this server to list terminal agents' - ) - : detectionFailed + + + {needsServerUpdate ? t( - 'auto.components.tab.bar.QuickLaunchButton.terminalListFailed', - 'Couldn’t list terminal agents for this workspace' + 'auto.components.tab.bar.QuickLaunchButton.terminalListNeedsServerUpdate', + 'Update Orca on this server to list terminal agents' ) - : detectedIds && detectedIds.length > 0 - ? t('auto.components.tab.bar.QuickLaunchButton.8dea9b5cdf', 'No enabled agents') - : t('auto.components.tab.bar.QuickLaunchButton.e518f544b1', 'No agents detected')} + : detectionFailed + ? t( + 'auto.components.tab.bar.QuickLaunchButton.terminalListFailed', + 'Couldn’t list terminal agents for this workspace' + ) + : detectedIds && detectedIds.length > 0 + ? t('auto.components.tab.bar.QuickLaunchButton.8dea9b5cdf', 'No enabled agents') + : t('auto.components.tab.bar.QuickLaunchButton.e518f544b1', 'No agents detected')} + ) : null} {pickerInventory ? ( diff --git a/src/renderer/src/components/ui/button.tsx b/src/renderer/src/components/ui/button.tsx index ebbcbf937a8..2c901f9c878 100644 --- a/src/renderer/src/components/ui/button.tsx +++ b/src/renderer/src/components/ui/button.tsx @@ -43,6 +43,8 @@ const buttonVariants = cva( inlineChip: 'h-6 gap-1 px-1.5', sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5', menu: 'h-8 gap-2 px-2 py-1.5 text-xs leading-5', + // A launch-menu row's label segment, matching DropdownMenuItem's `launch` rows. + 'menu-launch': 'h-9 gap-2 px-2 text-[13px] leading-4', lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', icon: 'size-9', 'icon-xs': "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3", diff --git a/src/renderer/src/components/ui/dropdown-menu.tsx b/src/renderer/src/components/ui/dropdown-menu.tsx index 751eed8b23f..b3ad3f49c0f 100644 --- a/src/renderer/src/components/ui/dropdown-menu.tsx +++ b/src/renderer/src/components/ui/dropdown-menu.tsx @@ -106,7 +106,7 @@ function DropdownMenuItem({ ...props }: React.ComponentProps & { inset?: boolean - /** `launch`: rows in the shared agent menu. */ + /** `launch`: rows in the shared agent menu; at least 36px tall so every launch menu matches. */ variant?: 'default' | 'destructive' | 'launch' }) { return ( @@ -115,7 +115,7 @@ function DropdownMenuItem({ data-inset={inset} data-variant={variant} className={cn( - "relative flex cursor-default items-center gap-2 rounded-md px-2.5 py-[5px] text-[13px] leading-4 font-normal outline-hidden select-none focus:bg-black/8 dark:focus:bg-white/14 data-[search-default]:bg-black/8 dark:data-[search-default]:bg-white/14 data-[context-menu-open]:bg-black/8 dark:data-[context-menu-open]:bg-white/14 data-[disabled]:pointer-events-none data-[disabled]:opacity-40 aria-disabled:opacity-40 data-[inset]:pl-8 data-[current=true]:bg-accent data-[current=true]:data-[disabled]:opacity-100 data-[variant=destructive]:text-[color-mix(in_srgb,var(--destructive)_78%,black)] dark:data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:stroke-[1.5] data-[variant=destructive]:*:[svg]:text-current! data-[variant=launch]:rounded-[7px] data-[variant=launch]:px-2 data-[variant=launch]:py-1 data-[variant=launch]:text-[12px] data-[variant=launch]:leading-4 data-[variant=launch]:font-medium", + "relative flex cursor-default items-center gap-2 rounded-md px-2.5 py-[5px] text-[13px] leading-4 font-normal outline-hidden select-none focus:bg-black/8 dark:focus:bg-white/14 data-[search-default]:bg-black/8 dark:data-[search-default]:bg-white/14 data-[context-menu-open]:bg-black/8 dark:data-[context-menu-open]:bg-white/14 data-[disabled]:pointer-events-none data-[disabled]:opacity-40 aria-disabled:opacity-40 data-[inset]:pl-8 data-[current=true]:bg-accent data-[current=true]:data-[disabled]:opacity-100 data-[variant=destructive]:text-[color-mix(in_srgb,var(--destructive)_78%,black)] dark:data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:stroke-[1.5] data-[variant=destructive]:*:[svg]:text-current! data-[variant=launch]:min-h-9 data-[variant=launch]:rounded-[7px] data-[variant=launch]:px-2 data-[variant=launch]:py-1.5 data-[variant=launch]:text-[13px] data-[variant=launch]:leading-4 data-[variant=launch]:font-medium", className )} onPointerMove={keepFocusWhileMenuCloses(onPointerMove)} From 9103a362db8e6667a0ce1c7f2b359506521f8431 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 22:26:57 -0800 Subject: [PATCH 025/178] test: remove 13 low-value workspace teardown cases (#41851) * test: prune teardown warning and mock projection cases * test: prune redundant unstopped PTY projections --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/reliability-gates.jsonc | 8 +- ...ructured-session-worktree-teardown.test.ts | 108 +------------- .../worktree-teardown-unstopped-pty.test.ts | 139 ------------------ .../removal/worktree-teardown.test.ts | 35 ----- 4 files changed, 6 insertions(+), 284 deletions(-) diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 51ec636be7a..c9adc5d9fbf 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -6165,7 +6165,7 @@ "providers": ["local", "daemon", "ssh", "runtime"], "coveredPlatforms": ["macos"], "coveredProviders": ["local", "daemon", "ssh", "runtime"], - "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1674 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1630 removes low-value Git, GitHub/GitLab and IPC projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Live macOS Electron tests prove exact local PTY disappearance after parked-tab close and detached-pgid descendant death after agent close. A deterministic final-pane interleaving now proves durable tab retirement commits before externally owned PTY teardown can publish graph loss, and preserves an unrelated live canary through duplicate exit retirement. Deterministic tests also cover final-pane CLI close durability and same-incarnation handle continuity; persisted-empty restart suppression remains historical Electron evidence, alongside replacement and hard graph-loss stale fencing, preallocated-handle supersession, daemon and SSH routing, local/daemon pending-snapshot ownership across natural exit, stale-root descendant-signal suppression, graceful-to-immediate kill upgrades, duplicate-kill completion sharing, locale-stable bounded/fresh/coalesced process-table reads, deadline-safe successor scans, cycle-safe linear descendant traversal, target-only escalation indexing, 32-wide bulk teardown, source-scan timestamp identity, same-second PID ambiguity, ordinary runtime close ownership, unified-only hydration, split ownership, pane detach transfer, restart alias hydration, and late-hook suppression; live Linux, Windows, WSL, SSH, and remote-runtime process evidence remains pending. A paired macOS Electron journey (real attached-window desktop host plus a separate real paired desktop client, isolated profiles) covers the inverse authority direction for host-created terminals: a renderer graph sync must not retire a tab that `orca terminal create` made. The retained paired journey covers the incident shape the real CLI produces - a repo's FIRST host-created terminal, since src/cli/handlers/terminal.ts sends no clientMutationId. The repeated target-after-unrelated-terminal journey was retired; its separate ordering alarm is no longer provided. An earlier reading of this gate called a preceding host-created terminal and a `clientMutationId` protective; main-process instrumentation refuted that. The renderer's stale session write deleted the host tab from persistence in every variant, and only the delete's timing relative to the measured sync differed, so those rows were false negatives of a single-sync oracle. The defect was in persistence: persistPtyBinding did not raise the repo's terminal topology fence for the first host-admitted tab, leaving the renderer's pre-create tab list free to replay over it. Elapsed time and paired-client mirroring never decided anything. A windowless `orca serve` parity arm runs the identical oracle and passes on both the merge-base and the branch, confirming that topology does not exhibit the defect (the gate it turns on is window-conditioned) and that making the host binding unconditional did not regress it. All of this is macOS-local only. The reported incident occurred on a physical Windows host; we did not run against one. The claim that the mechanism is platform-independent rests on the authoritative-window gate and publication-epoch inheritance being OS-agnostic, not on Windows evidence, so no ConPTY, Windows window-lifecycle, update, or sleep behavior is covered here. The synthetic default-tabs unit suite was retired: its explicit-empty/default-tab assertions no longer supply unit evidence. This retirement supplies no new persisted-restart, runtime-routing, cross-platform, or mobile evidence; existing historical runs and counts remain historical. The test-value audit removes the isolated re-retirement-token/epoch, activity-cutoff, idle-reattach and sibling-restore examples. The cited exact retirement and chained-detach cases remain unchanged, alongside closed-tab revival fencing and alias churn bounds. Their retained evidence is not equivalent coverage for every removed permutation; historical execution evidence is unchanged. The test-value audit removed shared-owner snapshot/cold-restore reference examples, a direct absent-tab PTY-index update, caller-supplied host-owned teardown boolean/no-dispatch, and a single local-provider rejection example. It also removed hand-built detach-retention comparator examples for different runs, handles, provider identities, and handle-less rows without independent identity ingress. Their specific snapshot, missing-tab, host-decision, rejection-count, and comparator alarms are lost. Current exact authority/ordering and chained-detach cases remain retained; they do not establish equivalent coverage for the removed examples. Historical commands, counts, results, and red-green narratives remain historical. The test-value audit retires terminals-explicit-empty-hydration.test.ts in full: its direct git/folder empty-tombstone assertions and literal-flag auto-create helper check are lost. The store test never mounted the initial-terminal effect or performed a persisted restart. The audit also removes terminal-tab-retirement.test.ts pure ownership/alias/routing plan inventories and sleeping-record map assertions; its former 100-tab scan-count case was subsequently retired too; no assertion remains in that suite. These retained and historical cases do not establish equivalent tombstone, default-create, provider teardown, cross-platform, or mobile coverage. Historical commands, results, counts, and red-green narratives remain historical. The subsequent test-value audit deletes the remaining terminal-tab-retirement.test.ts suite. Its 100-tab case asserted only returned plan count and one outer scan per ownership store; it never inspected per-tab plans or provider teardown. Those direct cardinality/work-count alarms are intentionally lost. Historical executed commands, results, counts and red-green narratives remain historical; no equivalent replacement or new runtime, platform or mobile evidence is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1392: src/main/agent-hooks/server-pane-authority.test.ts reduced. Retires one duplicate-transfer no-op listener count and latest pty/timestamp callback fixture. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4368 intentionally drops seven mocked teardown cases: duplicate single-chat rollback, forced stuck-close warning/retirement, post-close-throw exit acceptance, mixed live/unconfirmed wording, exact serial-close budget work, missing runtime selector success and runtime-sweep rejection. Four structured-session and three PTY teardown cases remain; no replacement or fresh native/process/remote proof is claimed. Historical assertion anchors and receipts are not refreshed by this source-only reduction. Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1674 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1630 removes low-value Git, GitHub/GitLab and IPC projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Live macOS Electron tests prove exact local PTY disappearance after parked-tab close and detached-pgid descendant death after agent close. A deterministic final-pane interleaving now proves durable tab retirement commits before externally owned PTY teardown can publish graph loss, and preserves an unrelated live canary through duplicate exit retirement. Deterministic tests also cover final-pane CLI close durability and same-incarnation handle continuity; persisted-empty restart suppression remains historical Electron evidence, alongside replacement and hard graph-loss stale fencing, preallocated-handle supersession, daemon and SSH routing, local/daemon pending-snapshot ownership across natural exit, stale-root descendant-signal suppression, graceful-to-immediate kill upgrades, duplicate-kill completion sharing, locale-stable bounded/fresh/coalesced process-table reads, deadline-safe successor scans, cycle-safe linear descendant traversal, target-only escalation indexing, 32-wide bulk teardown, source-scan timestamp identity, same-second PID ambiguity, ordinary runtime close ownership, unified-only hydration, split ownership, pane detach transfer, restart alias hydration, and late-hook suppression; live Linux, Windows, WSL, SSH, and remote-runtime process evidence remains pending. A paired macOS Electron journey (real attached-window desktop host plus a separate real paired desktop client, isolated profiles) covers the inverse authority direction for host-created terminals: a renderer graph sync must not retire a tab that `orca terminal create` made. The retained paired journey covers the incident shape the real CLI produces - a repo's FIRST host-created terminal, since src/cli/handlers/terminal.ts sends no clientMutationId. The repeated target-after-unrelated-terminal journey was retired; its separate ordering alarm is no longer provided. An earlier reading of this gate called a preceding host-created terminal and a `clientMutationId` protective; main-process instrumentation refuted that. The renderer's stale session write deleted the host tab from persistence in every variant, and only the delete's timing relative to the measured sync differed, so those rows were false negatives of a single-sync oracle. The defect was in persistence: persistPtyBinding did not raise the repo's terminal topology fence for the first host-admitted tab, leaving the renderer's pre-create tab list free to replay over it. Elapsed time and paired-client mirroring never decided anything. A windowless `orca serve` parity arm runs the identical oracle and passes on both the merge-base and the branch, confirming that topology does not exhibit the defect (the gate it turns on is window-conditioned) and that making the host binding unconditional did not regress it. All of this is macOS-local only. The reported incident occurred on a physical Windows host; we did not run against one. The claim that the mechanism is platform-independent rests on the authoritative-window gate and publication-epoch inheritance being OS-agnostic, not on Windows evidence, so no ConPTY, Windows window-lifecycle, update, or sleep behavior is covered here. The synthetic default-tabs unit suite was retired: its explicit-empty/default-tab assertions no longer supply unit evidence. This retirement supplies no new persisted-restart, runtime-routing, cross-platform, or mobile evidence; existing historical runs and counts remain historical. The test-value audit removes the isolated re-retirement-token/epoch, activity-cutoff, idle-reattach and sibling-restore examples. The cited exact retirement and chained-detach cases remain unchanged, alongside closed-tab revival fencing and alias churn bounds. Their retained evidence is not equivalent coverage for every removed permutation; historical execution evidence is unchanged. The test-value audit removed shared-owner snapshot/cold-restore reference examples, a direct absent-tab PTY-index update, caller-supplied host-owned teardown boolean/no-dispatch, and a single local-provider rejection example. It also removed hand-built detach-retention comparator examples for different runs, handles, provider identities, and handle-less rows without independent identity ingress. Their specific snapshot, missing-tab, host-decision, rejection-count, and comparator alarms are lost. Current exact authority/ordering and chained-detach cases remain retained; they do not establish equivalent coverage for the removed examples. Historical commands, counts, results, and red-green narratives remain historical. The test-value audit retires terminals-explicit-empty-hydration.test.ts in full: its direct git/folder empty-tombstone assertions and literal-flag auto-create helper check are lost. The store test never mounted the initial-terminal effect or performed a persisted restart. The audit also removes terminal-tab-retirement.test.ts pure ownership/alias/routing plan inventories and sleeping-record map assertions; its former 100-tab scan-count case was subsequently retired too; no assertion remains in that suite. These retained and historical cases do not establish equivalent tombstone, default-create, provider teardown, cross-platform, or mobile coverage. Historical commands, results, counts, and red-green narratives remain historical. The subsequent test-value audit deletes the remaining terminal-tab-retirement.test.ts suite. Its 100-tab case asserted only returned plan count and one outer scan per ownership store; it never inspected per-tab plans or provider teardown. Those direct cardinality/work-count alarms are intentionally lost. Historical executed commands, results, counts and red-green narratives remain historical; no equivalent replacement or new runtime, platform or mobile evidence is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1392: src/main/agent-hooks/server-pane-authority.test.ts reduced. Retires one duplicate-transfer no-op listener count and latest pty/timestamp callback fixture. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-4327", "https://github.com/stablyai/orca/pull/14590", @@ -6174,7 +6174,7 @@ "https://github.com/stablyai/orca/issues/14524" ], "invariant": "Close permanently removes the owned provider session, agent descendants, persisted tab/layout authority, and resume authority even when no TerminalPane is mounted; final-pane CLI close commits durable tab retirement before PTY stop can publish graph loss, does not acknowledge before that retirement, and treats the later exit retirement as idempotent. Workspace-wide CLI close applies that same durable contract to every terminal in exactly one worktree, including pinned and persisted-only surfaces, without touching sibling-worktree PTYs or resume records. A persisted explicit-empty state prevents initial-tab fallback from repopulating the workspace after reload or restart. A handle remains valid while the same provider-attested PTY incarnation survives renderer reload or re-key, and becomes stale without adopting a replacement incarnation. A terminating id remains reserved through natural exit, duplicate callers await the same completion, and immediate teardown upgrades any graceful request without signalling a recycled PID or a descendant tree after root ownership is lost; process-table work is locale-stable, bounded, fresh for each post-start request, same-turn coalesced, and begins within the requesting caller's deadline, including bulk worktree cleanup; detach and park preserve ownership; aliases prevent a detached agent's immutable physical pane key from being retired with its former tab.", - "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1674 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Capture exact tab, pane, PTY, handle, incarnation, and persisted layout identities. Final-pane terminal.close must invoke one durability-acknowledged tab retirement before exact PTY teardown; controlled PTY exit and graph removal afterward must remain idempotent, omit the target from persistence and provider inventory, and leave an unrelated canary handle live. terminal.closeAll must force-retire every pinned and unpinned target surface, clear its persisted agent-resume and incarnation records, await authoritative PTY exit, preserve a sibling worktree's tab, PTY, and resume authority, and return an unverifiable failure instead of claiming exit when the owning host does not confirm a stop. The ordinary path stays pending until the persisted row is removed and never falls back to the fire-and-forget pane event; reload/restart must keep the explicitly emptied worktree at zero terminal tabs and provider sessions. Reloading the renderer with the same tab/pane/PTY/incarnation must keep the handle readable; changing only the incarnation behind the same PTY id, losing the authoritative graph, or superseding the renderer handle with a preallocated handle must reject the old handle with terminal_handle_stale. Parked-close tests prove the exact PTY disappears; descendant/process tests keep natural exits reserved, upgrade teardown safely, bound/coalesce process-table work, and protect recycled identities.", + "oracle": "Audit4368 intentionally drops seven mocked teardown cases: duplicate single-chat rollback, forced stuck-close warning/retirement, post-close-throw exit acceptance, mixed live/unconfirmed wording, exact serial-close budget work, missing runtime selector success and runtime-sweep rejection. Four structured-session and three PTY teardown cases remain; no replacement or fresh native/process/remote proof is claimed. Historical assertion anchors and receipts are not refreshed by this source-only reduction. Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Lose the complete completed-worker-retirement-resume suite: exact terminal-close resume-record revocation, PTY-exit recovery retention and settled-worker release examples. Remove its exact command token or standalone command, testFiles entry and assertionRef, archiving complete prior fields. Independent receipts retain original scopes. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit3323 (reconciled source audit3320) intentionally retires seven low-value renderer authority/retirement examples: retires one pane, clears resume authority, and rejects late status without harming siblings — Late status reviving a retired pane, sibling damage, or resume authority left behind.; can retire live pane authority while retaining its sleeping session — Retiring live authority accidentally dropping a retained sleeping session.; keeps a physical pane routed through chained detaches until its current owner closes — Chained detach alias routing broken after original owner closes, or alias survives final close.; forgets routing aliases for purged tabs — Purged-tab routing aliases lingering.; retires split, relay, deferred, and pending sessions for a parked tab — Split, relay, deferred or pending session omitted from teardown, sleeping-record sibling damaged, parked disposer skipped.; retires parked ownership before teardown and sweeps published state before unified close — Parked cleanup/provider teardown/state removal/sweep/unified-close ordering changes.; starts mixed provider teardown and classifies both rejection kinds after synchronous retirement — Mixed teardown failures blocking retirement, or local/runtime warning counts misclassified.. Remove the exact-pane-retirement and chained-detach assertion references because their source cases are gone. Other retained cases do not replace these alarms. Preserve all dated evidenceRuns and prior history with their original source scope; no fresh execution or historical import-closure equivalence is claimed. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1904 reduces persistence-pty-binding-reconciliation from six to four definitions, intentionally losing legacy remap-load and first-binding projection alarms. Current selector/testFile remains valid, with no selected-source assertionRef or active matching receipt. The six unchanged close/continuity receipts keep their exact original scope; no new broad gate or replacement evidence is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Audit1674 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Capture exact tab, pane, PTY, handle, incarnation, and persisted layout identities. Final-pane terminal.close must invoke one durability-acknowledged tab retirement before exact PTY teardown; controlled PTY exit and graph removal afterward must remain idempotent, omit the target from persistence and provider inventory, and leave an unrelated canary handle live. terminal.closeAll must force-retire every pinned and unpinned target surface, clear its persisted agent-resume and incarnation records, await authoritative PTY exit, preserve a sibling worktree's tab, PTY, and resume authority, and return an unverifiable failure instead of claiming exit when the owning host does not confirm a stop. The ordinary path stays pending until the persisted row is removed and never falls back to the fire-and-forget pane event; reload/restart must keep the explicitly emptied worktree at zero terminal tabs and provider sessions. Reloading the renderer with the same tab/pane/PTY/incarnation must keep the handle readable; changing only the incarnation behind the same PTY id, losing the authoritative graph, or superseding the renderer handle with a preallocated handle must reject the old handle with terminal_handle_stale. Parked-close tests prove the exact PTY disappears; descendant/process tests keep natural exits reserved, upgrade teardown safely, bound/coalesce process-table work, and protect recycled identities.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/terminal/surface/orca-runtime-terminal-close-continuity.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts", @@ -12371,14 +12371,14 @@ "providers": ["local", "remote-runtime", "ssh"], "coveredPlatforms": ["macos", "linux"], "coveredProviders": ["local", "remote-runtime", "ssh"], - "coverageNotes": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous account (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous account (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous account (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous account (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous account (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous account (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous account (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous account (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous account (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous account (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous account (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous account (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous account (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous account (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous account (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous account (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Independently remove one authored/finite Playwright closed-gate app-boot inventory: accept loss of real renderer/preload/main refusal composition, saved-host row and Add/Move hiding/Remove visibility alarms. Keep the unchanged scripted-move rendered dialog lifecycle/reload case. It runs no real WSL migration, host connection, file copy or automation restoration. No selector or Playwright participation cleanup is required because the spec remains. Previous account (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1645 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1516 removes package-electron-runtime-contract.test.mjs, a copied release-workflow platform and marker matrix. Its workflow wiring alarm is intentionally retired; no actual packaged watcher delivery was exercised and no equivalent replacement is claimed. Unaffected dated execution receipts retain their original scope; changed optional selectors have no fresh full-gate execution result. This audit removes the paired-web stopped-during-outage and late-ready direct-branch examples. The retained pre-ready cancellation, failed-unwatch retry and replay-continuity cases are not equivalent replacements for those ordering alarms. Current web-runtime-client.test.ts assertions below name only its retained pre-ready callback case; replay cases remain in their separate suite. Historical commands and results remain historical. Deterministic tests cover one shared healthy child per runtime process plus at most four bounded fault-quarantine children, native desktop, paired-runtime, and WSL snapshot processes sharing the same eight-physical-child reservation with typed serialized event-driven capacity recovery, including recursive re-wait when crash recovery reclaims an announced slot, one quarantine attempt per watch lifetime, generation-scoped cancellation, bounded termination with removable deadline waiters, replacement-crawl errors that cannot be blessed by a late readiness ack, pre-ready paired-web cancellation registered before an unbounded capacity wait, already-resolved and late physical-exit retry of a rejected paired-web teardown, physical desktop-install and local/SSH PTY teardown, stale-generation rejection, per-child stat bounds, final-overflow RPC delivery, and renderer eviction before terminal callbacks can retry. Destructive local and SSH removal fences both ID-derived and resolved-cwd terminal roots, including sibling-root/cwd combinations with reverse admission rollback, closes descendant relay watches before parent deletion, enumerates authoritative provider/cwd ownership, falls back to daemon spawn cwd before OSC 7, and keeps daemon/relay immediate shutdown pending until native exit or a bounded fail-closed error; a dead relay PID discovered during attach also settles concurrent shutdown before its stale entry is reaped, while Windows runtime watcher deadlines retain one removable physical-close waiter, treat Node's error-close path as positive physical-exit proof, and clear root ownership on late close. The relay policy test maps a standard repository's base, Git common directory, and worktree roots to one healthy supervisor, then proves a shared-child failure recovers those roots in separate quarantine supervisors with overflow and resumed events. Abortable capacity, quarantine, runtime-root, relay, SSH, and relay pre-install setup waits attach one reaction to each shared promise and explicitly remove 10,000 cancelled caller closures while one anchor remains. Built-entry macOS harnesses kill the desktop/runtime and SSH relay watcher children, then require automatic resubscription and later events while the host process and relay PTY survive. SSH bundles the child boundary beside relay.js, requires both artifacts for install completeness, and preserves registration-owned same-root cancellation. The test-value audit corrects the active renderer watch command/file reference to the existing runtime-file-client-watch.test.ts, whose three cases are retained unchanged. They observe synthetic subscription retry/fanout and unsubscribe/unwatch calls, not actual preload/main bookkeeping or physical watcher-process cleanup. The separate read/preview client suite loses forwarding, supplied-error and negative-only examples, retaining binary detection and Unicode SVG decoding. Historical evidence commands/results are unchanged. Test-value audit1293 removes src/main/providers/local-pty/local-pty-provider-session-inventory.test.ts (2 authored cases). Their isolated assertions no longer run; surviving tests are not claimed as equivalent coverage. Fresh execution against the candidate SHA is required. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1412: src/main/workspaces/removal/watcher-removal-gate.test.ts reduced. Loses exact removal-error compatibility with renderer predicate under shared message constants. Kept public refusal outcomes do not establish that renderer classification. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit4368 intentionally drops seven mocked teardown cases: duplicate single-chat rollback, forced stuck-close warning/retirement, post-close-throw exit acceptance, mixed live/unconfirmed wording, exact serial-close budget work, missing runtime selector success and runtime-sweep rejection. Four structured-session and three PTY teardown cases remain; no replacement or fresh native/process/remote proof is claimed. Historical assertion anchors and receipts are not refreshed by this source-only reduction. Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3994 retires two authored callbacks imported by the shared construction test driver: split OSC 9999 carry and rendererless status ownership, plus OpenCode permission text taking priority in status and tui-idle refusal. Remaining imports and selectors keep their scope. No equivalent replacement or fresh gate execution is claimed. Audit 3959 retires the whole session.test.ts driver (27 authored / 33 finite cases): startup/echo filtering, readiness input gates, ownership confirmation, detach authority, and termination retry/exit ordering alarms are intentionally lost. No equivalent replacement or fresh runtime validation is claimed. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous account (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous account (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous account (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous account (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous account (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous account (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous account (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous account (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous account (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous account (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous account (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous account (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous account (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous account (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous account (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous account (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous account (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Independently remove one authored/finite Playwright closed-gate app-boot inventory: accept loss of real renderer/preload/main refusal composition, saved-host row and Add/Move hiding/Remove visibility alarms. Keep the unchanged scripted-move rendered dialog lifecycle/reload case. It runs no real WSL migration, host connection, file copy or automation restoration. No selector or Playwright participation cleanup is required because the spec remains. Previous account (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1645 removes low-value test projections, mock wiring, private-state assertions, inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained public behavior and refusal boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1634 removes low-value IPC, filesystem and runtime projections, argument inventories and duplicate examples without equivalent replacement coverage. Per-source reviews record retained cancellation, ownership, authorization, filesystem and SQLite boundaries with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1627 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1516 removes package-electron-runtime-contract.test.mjs, a copied release-workflow platform and marker matrix. Its workflow wiring alarm is intentionally retired; no actual packaged watcher delivery was exercised and no equivalent replacement is claimed. Unaffected dated execution receipts retain their original scope; changed optional selectors have no fresh full-gate execution result. This audit removes the paired-web stopped-during-outage and late-ready direct-branch examples. The retained pre-ready cancellation, failed-unwatch retry and replay-continuity cases are not equivalent replacements for those ordering alarms. Current web-runtime-client.test.ts assertions below name only its retained pre-ready callback case; replay cases remain in their separate suite. Historical commands and results remain historical. Deterministic tests cover one shared healthy child per runtime process plus at most four bounded fault-quarantine children, native desktop, paired-runtime, and WSL snapshot processes sharing the same eight-physical-child reservation with typed serialized event-driven capacity recovery, including recursive re-wait when crash recovery reclaims an announced slot, one quarantine attempt per watch lifetime, generation-scoped cancellation, bounded termination with removable deadline waiters, replacement-crawl errors that cannot be blessed by a late readiness ack, pre-ready paired-web cancellation registered before an unbounded capacity wait, already-resolved and late physical-exit retry of a rejected paired-web teardown, physical desktop-install and local/SSH PTY teardown, stale-generation rejection, per-child stat bounds, final-overflow RPC delivery, and renderer eviction before terminal callbacks can retry. Destructive local and SSH removal fences both ID-derived and resolved-cwd terminal roots, including sibling-root/cwd combinations with reverse admission rollback, closes descendant relay watches before parent deletion, enumerates authoritative provider/cwd ownership, falls back to daemon spawn cwd before OSC 7, and keeps daemon/relay immediate shutdown pending until native exit or a bounded fail-closed error; a dead relay PID discovered during attach also settles concurrent shutdown before its stale entry is reaped, while Windows runtime watcher deadlines retain one removable physical-close waiter, treat Node's error-close path as positive physical-exit proof, and clear root ownership on late close. The relay policy test maps a standard repository's base, Git common directory, and worktree roots to one healthy supervisor, then proves a shared-child failure recovers those roots in separate quarantine supervisors with overflow and resumed events. Abortable capacity, quarantine, runtime-root, relay, SSH, and relay pre-install setup waits attach one reaction to each shared promise and explicitly remove 10,000 cancelled caller closures while one anchor remains. Built-entry macOS harnesses kill the desktop/runtime and SSH relay watcher children, then require automatic resubscription and later events while the host process and relay PTY survive. SSH bundles the child boundary beside relay.js, requires both artifacts for install completeness, and preserves registration-owned same-root cancellation. The test-value audit corrects the active renderer watch command/file reference to the existing runtime-file-client-watch.test.ts, whose three cases are retained unchanged. They observe synthetic subscription retry/fanout and unsubscribe/unwatch calls, not actual preload/main bookkeeping or physical watcher-process cleanup. The separate read/preview client suite loses forwarding, supplied-error and negative-only examples, retaining binary detection and Unicode SVG decoding. Historical evidence commands/results are unchanged. Test-value audit1293 removes src/main/providers/local-pty/local-pty-provider-session-inventory.test.ts (2 authored cases). Their isolated assertions no longer run; surviving tests are not claimed as equivalent coverage. Fresh execution against the candidate SHA is required. Audit1312 removes low-value imported specs from aggregate test drivers; old whole-driver execution receipts are archived. Retained registrations are unchanged. No equivalent replacement coverage is claimed. Audit1383: src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts reduced. Lose command-list/receipt/default/background activation matrices whose provider spawn returns canned IDs. Retained test proves actual tab allocation separation, not terminal startup. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1401: src/main/runtime/orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec.ts removed. Loses listing-on-already-repaired-data alarm; it never established runtime repair invocation. src/main/runtime/orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec.ts removed. Loses actual mobile split sibling shell versus recorded-agent resume command alarms; no equivalent coverage asserted. src/main/runtime/orca-runtime-tests/worktree-native-chat-setup.spec.ts removed. Loses separate setup/startup tab result in that manually assembled path. src/main/runtime/orca-runtime.test.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1412: src/main/workspaces/removal/watcher-removal-gate.test.ts reduced. Loses exact removal-error compatibility with renderer predicate under shared message constants. Kept public refusal outcomes do not establish that renderer classification. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/5308", "https://github.com/stablyai/orca/issues/7547", "https://github.com/stablyai/orca/issues/8212" ], "invariant": "Local runtime and SSH relay roots share one watcher child per host process while healthy and use at most four quarantine children after shard failure; native desktop, paired-runtime, and direct WSL snapshot children consume the same process-wide eight-child reservation and retry transient exhaustion without permanently disabling a root. Cancellation remains owned by its child generation and settles only after acknowledgement or bounded physical exit; paired-web retains cleanup ownership and can cancel before readiness even while capacity is exhausted, retries a failed unwatch after already-resolved or late physical exit without duplicating stream termination, replacements never overlap a still-retiring generation, and aborted or timed-out callers detach immediately even while queued behind same-root relay teardown. Destructive local and SSH cleanup holds one containment-aware watcher/terminal removal fence through descendant watcher and PTY physical exit plus parent Git/filesystem deletion; daemon and relay kill requests retain ownership until the actual subprocess exit callback or an equivalent positive dead-PID proof and propagate rejected signals or a bounded failure otherwise. A crash-fused shared shard moves affected roots into the bounded quarantine pool, a second fused quarantine ends those watch lifetimes, interrupted setup emits overflow after recovery, metadata work stays bounded, and the SSH relay, PTYs, Git, and other filesystem operations never load the watcher native addon or share its fault boundary.", - "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Previous oracle (preserved verbatim): Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous oracle (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous oracle (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous oracle (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous oracle (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous oracle (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous oracle (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous oracle (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous oracle (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous oracle (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous oracle (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous oracle (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous oracle (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous oracle (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous oracle (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Previous oracle (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Fill the global watcher-child reservation, require native desktop, paired-runtime, and WSL setup to retain typed transient capacity and retry after one serialized release, then cancel pending desktop and paired-client watches and require no retry. Hold one live anchor while aborting 10,000 callers at the capacity, quarantine-assignment, runtime-root, relay-setup, and SSH-registration layers; require exactly one explicit waiter and one base-promise reaction to remain, then zero after settlement. Hold an old relay unsubscribe, queue one same-root setup leader and one anchor, then abort 10,000 joiners; require every cancellation to settle before teardown releases, exactly one anchor waiter, and only one later physical install. Advance the former paired-web callback deadline while capacity remains full, require cancellation ownership to stay live, publish the server token before ready, and require files.unwatch to abort setup before a later slot release can install a native root. Reject the first ready-watch unwatch with a physical-exit promise, resolve that exit later, and require a fresh physical unwatch attempt, registry release, and exactly one stream end. Fill one healthy shared child with multiple roots, map a standard SSH repository's base, Git common directory, and worktree roots to one relay supervisor, then inject IPC disconnect-before-exit, queued and active cancellation, operations during termination, kill-without-exit, repeated unkillable generations, initial and recovery timeouts, and shard failure. During local and SSH deletion, attempt a late terminal spawn whose worktree identity and cwd name different roots, enumerate a non-prefix remote PTY by authoritative worktree ownership, withhold daemon/relay exit callbacks, make attach positively prove a concurrent-shutdown PID dead, and make the provider unavailable; require every acquired admission to roll back on rejection, dead-PID proof to settle physical exit before reaping, timeout to retain ownership, and Git/filesystem deletion never to start. Require bounded watcher process count, physical-exit settlement, healthy-root recovery, stale-generation rejection, overflow plus later events, and fail-closed destructive cleanup. Verify relay build hashes and remote install probes require both relay.js and relay-watcher.js. Launch the built relay with a real PTY and Parcel watch, kill only the watcher child, and require the same relay PID and PTY to answer before a replacement child delivers overflow and a post-crash event.", + "oracle": "Audit4368 intentionally drops seven mocked teardown cases: duplicate single-chat rollback, forced stuck-close warning/retirement, post-close-throw exit acceptance, mixed live/unconfirmed wording, exact serial-close budget work, missing runtime selector success and runtime-sweep rejection. Four structured-session and three PTY teardown cases remain; no replacement or fresh native/process/remote proof is claimed. Historical assertion anchors and receipts are not refreshed by this source-only reduction. Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit4358 intentionally removes six whole mocked daemon subprocess suites and two extra pre-listener replay variants (18 authored / 20 finite cases). Lost alarms include cancel-during-cwd-validation, root agent startup refusal, output/exit before ConPTY publication, fresh/delayed/unavailable foreground confirmation, cache/decision separation, detached/no-tty child evidence and synthetic agent authorization. The retained lifecycle case checks data and exit replay after handle creation, not startup-time publication or alternate listener order. The retained native Windows Git Bash workflow and host cold-restore seed check have separate scopes. No equivalent replacement or fresh gate execution is claimed. Earlier notes and dated receipts retain their original scope. Audit4351 removes the physical-exit timeout/retry and kill-failure/retry local-PTY shutdown cases. The retained shutdown case proves pending ownership until the injected node-pty exit callback only. These two narrow error-path alarms are intentionally lost; retained commands still select the reduced suite. No equivalent replacement or fresh runtime execution is claimed. Earlier notes and dated receipts retain their original scopes. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3788 retires10 authored/11 expanded cases across six changed drivers, including the entire screencast pending-frame-retry and usage-source-cache-streaming suites. Deliberate losses are false-refused last-frame retry/once-only delivery, ordinary stable-pane reattachment, cleared-chat Dispatch completion, terminal-declared chat-caller refusal, at-rest worker liveness projection, nested depth/cap policy, small unsigned6/sealed7 plain-format and verified-bit examples, generic malformed-ISIZE footer refusal examples, and synthetic10k/100k chunk-size/work-count budgets in gzip and general cache I/O. These include real state/disk/RPC outcomes consciously retired for marginal value; retained E2EE-backlog, receipt/custody/uncertain-ack, integrity/migration and staging-cleanup cases are not equivalent replacements. The footer test did not prove early rejection or bounded allocation; staging cleanup did not seed unrelated files. No replacement or fresh native/remote/performance execution is claimed. Earlier raw selectors and dated receipts remain historical. Audit3779 retires17 authored/17 expanded registrations across six changed drivers, including the entire shared-Codex-home and prompt-telemetry suites. Accepted losses are shared-home wrapper noninterference/opt-out preservation, mocked metric count/provenance filters, SSH activity labeling/clearing and handover idle timing, VM ownership/capability/status projections and asleep-worktree refusal, a link-only external conversation replay sample, and two narrow input-credit noninterference/expiry examples. Retain the direct exact-tab/single-use input-credit case. These bounded real disk/state/routing alarms are intentionally retired for marginal value without equivalent replacement or fresh execution claims; earlier receipts remain historical. Audit3772 retires18 authored/27 expanded cases, including whole Codex service opt-out, RPC capability-parser, creator-provenance, short-ciphertext and readiness-null suites. Accepted losses include service ledger/symlink/mode cleanup, user TOML substring preservation, old-hash cleanup, description pass-through, malformed-file/pending-hash examples, coordinator write/probe counts, capability array bounds, positive creator projections, early short-payload refusal and readiness helper null shape. Two redundant Codex exit-cause rows are also retired; Pi shares their direct-idle path only in this empty-output immediate-exit fixture, and future agent-config drift coverage is lost. Real bounded filesystem/parser/runtime alarms are deliberately retired for marginal value; no equivalent replacements or fresh execution are claimed. Earlier receipts retain their historical scope. Audits3754/3755 intentionally remove9 authored/9 finite registrations: the entire one-case usage-source-cache-streaming.test.ts driver and8 cases from the surviving computer-action-validation.test.ts driver. This gate retains an unfiltered root Vitest command; UNIT_INCLUDE admits both source paths. Accepted losses are the real encoder output chunk-size/work-count alarm on repetitive10k/100k ownership payloads through a mocked durable sink, plus CLI diagnostic precedence, fractional window-id rejection, action-call-site window-target exclusivity, plain-text click-count recovery copy, incomplete click/drag targeting, mouse/modifier/direction allowlist samples and inline/stdin text exclusivity. These CLI cases exercise real argument/handler validation before mocked runtime calls; they are deliberately retired for marginal value, not declared tautologies. Retained cache I/O and CLI cases are not equivalent replacements for these alarms. Existing historical raw receipts remain unchanged and do not validate the deleted or reduced drivers. No fresh execution claim is made. Audits3758/3763 intentionally remove9 authored/11 finite registrations from6 changed drivers, including the entire pane-title-notification, window-linear-notices and worktree-card-lifecycle-effects suites. This gate retains an unfiltered root Vitest command admitting these paths. Accepted losses are same-account linked-row success-notice fanout, sidebar-switch channel forwarding, Linear-only notification forwarding/non-terminal-consumer selection/closed-window detach, destroyed-window refusal and first-send-failure suspension through the pane-title adapter, the100-card synthetic focus/poll absence sample, stored-source local folder badge refresh, unobserved-row badge refusal, and immediate reposChanged lineage invalidation. These exercise real owners with controlled ports; they are retired for marginal value, without equivalent replacement claims. The no-badge-work fixture does not seed its title-promised collection-only cache. Preserved historical receipts do not validate deleted or reduced drivers; no fresh execution is claimed. Audit3759 retires the entire one-case Tailwind compiler/scanner smoke. Accept loss of the direct CSS production/test/spec source-exclusion and rescan alarm; no replacement or current execution is claimed. Audit3752 independently approves audit3749: retire five authored/five finite watcher-process registrations, retaining18 raw-exact registrations. Accept loss of healthy shared-pool joint recovery, last-pending-failure idle retirement/respawn, late VITEST fallback release, missing-entry fail-closed selection and independent-supervisor fuse locality alarms. These are real bounded host-policy alarms intentionally retired for low marginal value; no equivalent replacement is asserted. Remaining driver selections exercise controlled FakeChild supervisor lifecycle, cancellation, physical-exit fencing, recovery and fuse behavior. This source review supplies no fresh native/process/platform/remote/SSH execution; historical receipts retain original source and selector scopes. Previous oracle (preserved verbatim): Audits3738/3742/3743/3744 intentionally remove15 authored/21 finite registrations from six surviving drivers, with no equivalent replacement or fresh execution claim. This gate retains an unfiltered root Vitest command. Current UNIT_INCLUDE admits the partial push-service, metadata-dialog and two config/scripts browser drivers:10 authored/16 finite removals. Accepted losses: actual retry30s/60s progression and register bucket/refill wiring, explicit seven-day lease arithmetic (its notification lacked seq/epoch and never reached send), equal-name edit latch/save payload, real image/font/meta-refresh/form/popup browser refusal controls, and broad rich-markdown bridge/command/edit-select smoke. Actual production state, callback routing and browser enforcement run in these cases; these alarms are deliberately retired on marginal value. All drivers and selectors survive. Existing unrelated receipts remain historical evidence for their unchanged selectors, not proof of the reduced cases. Audit3732 removes 2 authored/3 finite registrations from two partial suites: nominal custom/Kimi local naming success and deferred context usage row presentation. Real losses: custom/Kimi successful extraction through conversation-name sanitization and agentLabel composition; separate ordered counted/deferred lists and exact percentage/token formatting. No replacement or equivalent coverage claimed. Retain 11 authored/13 finite raw-exact registrations for bounded naming instruction/refusal/cancellation, menu dismissal focus ownership and hover/composer Escape/timer cleanup. Source-only bounded value review, no runtime/CI/native/remote/mobile/folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3733 removes2authored/2finite terminal input checks: fixed custom-cap error/secret fixture and three-emoji cap5refusal whose6UTF16units already trip length fast guard (no UTF8-dependent boundary). Accept exact non-disclosure/oversized-refusal alarm losses; no replacement/equivalence claim. Retain13/13scalar/pending input, OSC state and cron DST/cadence outcomes after bounded material reads. Source-only zero runtime/novelty credit; original historical reasons preserved. Previous oracle (preserved verbatim): Audit3729 removes1authored/3finite by deleting whole CLI runtime pairing advisory-close suite. Accept losing post-real-close injected EIO preservation of native/script/unreadable pairing, env identity/preservation and exact close/release assertions; no actual failed-close leak or replacement coverage claimed. Two retained URL grammar/identity suites11authored16finite raw unchanged. Bounded source/owner/caller value review, zero novelty/runtime credit; other seven3724deferred closures remain. Historical receipts preserve original limits. Previous oracle (preserved verbatim): Audit3724 removes3authored/3finite checks by deleting two whole suites: TUI clear burst-size singleton and terminal-file-URL target two-case wrapper suite. Deliberate losses: derived widest burst<64byte alarm, UNC authority+line-column anchor composition, and malformed-percent null refusal. Real production algorithms execute in those tests; no mocked-output tautology, equivalent replacement, actual Claude/file-navigation delivery or universal safety-irrelevance claim. TUI58byte bound and URI examples have low marginal value as standalone suites. Actual1262/1264/1742KEEP superseded explicitly. Remaining14drivers83authored136finite raw unchanged. Bounded source-only review; ten closures deferred, four bounded keep judgments repeated with zero new semantic-frontier credit. No runtime/CI/native/remote/mobile/folder/global completion claim; historical receipts retain original scope. Previous oracle (preserved verbatim): Audit3723 removes 4 authored/4 finite registrations in two whole suites: keybinding cache freeze/token spellings and host-dependent Orca command name examples. Real losses include returned-object freeze, repeated Mod+P key, Space/PageDown/NumpadSubtract mapping, unknown/empty/prototype-token rejection, and headless Linux versus Windows-server/WSL command choice. No equivalent or replacement coverage claimed. Retain 12 authored/21 finite raw-exact registrations exercising generated scaffold validation, artifact failures, shipped editor/hook/worker/panel tests, actual generated typing compilation, independent CLI root routing and intent-name precedence. Source-only bounded audit; no runtime, CI, native, remote, mobile, folder workspace or global completion credit. Previous oracle (preserved verbatim): Audit3720 removes17authored/23finite checks: whole main catalog-date adapter2 and shared date-helper7, seven renderer nominal ranking/cap/no-date/composition cases, and one browser registration expanding to seven forced-container/DOM-child boundary cases. Deliberate real losses: community oldest-date UTC forwarding, official/undated omission, date sorting/window/invalid/future/skew/clamp and default/override cap; nominal ranking/cap and combined Featured/Newly-added outputs; real CSS459/460,719/720,1055/1056breakpoint, five-card partial-row and720px two-card first-row alarms. Browser cases really measure rendered geometry but force width/remove DOM children; no fake-pixel or tautology rationale. No replacement/equivalent coverage claimed. Retain14authored/19finite raw-exact registrations across4drivers: adversarial featured category/order/tie and installability, date order/window selection, actual PluginManager failure/escaping/duplicate-card suppression, real browser viewport/odd-count geometry/phone catalog visibility, syntax ingress/depth/include/printability and68dlong-bracket auto-close fill-in. No tests/browser/native/UI/provider/remote/mobile/folder/CI/global execution or completion credit. Previous oracle (preserved verbatim): Audit3717 removes6 authored/6finite checks in two whole thin-adapter/presentation suites. Deliberate real losses: ordinary recipe repository-URL withholding and provisioned-root credential scrubbing/forwarding; repository label lockstep suffix expansion, host-scoped map identity/collision grouping, Windows crosshost paths and WSL distro suffixes. Actual provision options, label/sort consumers and host/scrubber delegates inspected; no replacement/equivalence claim. Retain40 authored/84finite registrations raw exact in14 drivers with bounded owner/context review. Prior KEEP superseded for removed suites; no runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3714 removes8 authored/39finite checks: whole CLI selector-recovery, task display-text and ANSI grammar suites plus two name joiner/blank tables. Deliberate real losses: selector flag/value/identity and mutation recovery merge/serialization; exact task suffix, transcript fallback, input immutability, cache reuse and edited blocks; independent failed-escape/resumption and OSC/DCS/BEL/ST/C1/nested/unfinished stripping outputs; meaningful-language joiner preservation and invisible/blank-name rejection. These are real bounded alarms, removed as narrow supplied-error examples, helper/token inventories and repeated internal-work fixtures. Prior2936,1195/1196,1214/1215 and1082 KEEP superseded; no replacement or equivalent coverage claimed. Keep9authored/9finite raw-exact registrations in5surviving drivers: screenshot export/failure filesystem behavior, CLI local tree query, event-local reply provenance, vault role-sensitive content and four hostile/UTF16/dangling/tag-name regressions. No native/UI/provider/remote/mobile/folder/runtime/CI/global completion credit. Previous oracle (preserved verbatim): Audit3711 retires2authored/4finite cases by deleting whole clipboard-image-temp-file signature-to-name inventory (HEIC/TIFF/unrecognised bytes) and workspace-last-active switch-on supplied visit/reason singleton, plus its now-orphan test fixture. This intentionally loses real generated-filename suffix/extensionless alarms and real newer-visit archived7day versus inactive30day filtering/older-visit identity alarms. Filesystem writing and production reason pruning really execute; no equivalent replacement or universal absence of safety relevance is claimed. The clipboard test does not inspect written bytes, native delivery or decode; the visit test supplies candidate/timestamps and does not exercise persisted visit routing or actual removal.20authored/20finite cases remain unchanged across4bounded Git/sidecar/removal drivers. No new runner/test/module evaluation or runtime result; historical notes and receipts remain historical. Previous oracle (preserved verbatim): Audit3707 removes10 authored/11finite checks: three whole renderer adapter/assembled-stage suites and two native projection cases. Deliberate losses: plugin URL/ref/default-version/local-path and unsafe credential refusal forwarding; real Task hook/store A→B→A late-save/list/count fences, owner disappearance, legacy-host convergence, query/history/eviction and same-ID host history; native child append ordered-ID/replacement and compound fallback/order/optimistic-admission/child-turn/duplicate/shrink/revision alarms. These are real bounded alarms; removal follows thin adapter, manually assembled Task pipeline and repetitive fixture value judgment, with no replacement or equivalent coverage claim. Prior1179/1182,1220/1221 and1885 KEEP superseded as applicable. Keep36 authored/38finite registrations raw exact across13 surviving drivers. No runtime/CI/provider/browser/native/mobile/remote/folder/global completion credit. Previous oracle (preserved verbatim): Audit3706 removes7authored/7potential cases from3renderer sources: whole usage-label3case and130000-distinct-day usage-overview singleton suites, plus3SSH grouping/ordinary-range examples. Retain11authored/11potential cases across6drivers in this bounded selection; no equivalent replacement or fresh runtime result. Existing bare root-unit commands admit this selection statically; prior command/receipt strings remain historical. Audit3706 intentionally retires first/second-order usage-group label collision alarms; equal50%-weekly bucket versus derived-weekly selection identity and Gemini wk label alarm;130000distinct-day aggregation no-spread-crash/max/intensity alarm; legacy absent-source SSH grouping and ordinary reverse-select/selected-target deselect range alarms. Actual label ambiguity and pathological-input crash are accepted losses. Distinct-day fixture spans~356years; no hard producer/date bound is claimed. Retained filtered-anchor/grouped-order guards do not replace removed deselection/reverse-range/legacy-group coverage. No native SSH, mounted renderer, live provider, remote/folder/mobile end-to-end or current runtime pass was obtained. Previous oracle (preserved verbatim): Audit3699 removes4 authored/4finite checks, including one whole xterm copy-policy suite and one root/group CLI help case. Deliberate losses: direct copy-chord admission for platform, selection, Kitty mode, defaultPrevented, logical/physical key and key phase; command-group disclosure and group help routing including claude-teams early return without RuntimeClient construction. These are real bounded policy/wiring alarms; no replacement or equivalent coverage claimed. Retain14 authored/19finite raw-exact registrations across6 drivers: unsafe CLI refusal, Claude model event carry and disk reconstruction/OSC-first case, attachment ownership/upload lifetime and mixed BMP file-reference/PNG-chip delivery, actual SQLite authority/admission/mutation, and Electron geometry/hit-testing. No runtime, CI, native-key delivery, external provider, mobile, remote, folder or global completion credit. Previous oracle (preserved verbatim): Audit3692 removes5 authored/5finite marginal Codex checks: unrelated login URL without notice; ordinary per-account launch resource/config materialization; no-retained-pane skip; preservation of a previously owed full scan through actual preparation and pending-marker update (no actual history scan/settlement); ordinary later-config refresh. Accept these exact branch/wiring alarms as deliberate losses without replacement. Retain32 authored/34finite raw callbacks/registrations/table bytes exact across14suites, including credential/admission refusal, grace/reset readiness, filesystem ownership, commit-dependent queue state, streaming/host trust, path canonicalization and private-state preservation. All14whole suites challenged without keeper quota; no whole deletion warranted on current concrete signals. No runtime/CI/native WSL/remote/mobile/folder/global completion credit Previous oracle (preserved verbatim): Audit3691 removes two complete runtime-home routing singleton suites (2 authored/2 finite): catalog wrapper mirror login-copy and passive transcript-lookup filesystem noncreation. Exact accepted losses are recorded in knownGaps; no replacement or equivalent coverage claimed. Audit3691: no dedicated alarm remains in the deleted sources for post-launch system login import through prepareHostCodexHomeForReadOnlyAppServer, or for repeated missing-transcript lookups leaving removed Codex account/runtime directories absent and userData entries unchanged. Ownership-marker read avoidance was never independently measured. No real catalog probe, provider, native Windows or crash was validated by those fixtures. Previous oracle (preserved verbatim): Audit3686 current Claude profile/account review removes5 authored/5 finite cases: whole structured-auth diagnostic-label suite3 and Windows-only command suite1 whose advertised cache alarm is ineffective because cache lifetime is per invocation; remove duplicate setup namespace-link refusal1 at wrapper layer. All5 exact historical KEEP are consciously superseded. Retain49 authored/50 finite unchanged, including filesystem privacy/recovery, route identity and concurrency, built worker/helper, legacy WSL alias compatibility and sign-in handoff. These are deliberate coverage losses without replacement. No runtime/CI/native WSL/macOS Keychain, remote/mobile/folder or global completion credit. Explicit accepted real losses: unknown-account host fallback and mixed-version/WSL-only structured auth diagnostic labels; Windows-only sentinel ENOENT-to-friendly-error translation plus fresh-call successful dispatch smoke; setup catch-to-refused wrapper and no-write outcome for a namespace symlink. The command case does not discriminate removal of its per-invocation noncache guard; it still supplies the error-translation alarm, which is intentionally retired. No replacement/equivalence promise. Previous oracle (preserved verbatim): Audit3680 removes eleven authored/eleven finite unit cases across four files, including entire launch setup row projection, structured legacy-model metadata, and legacy classifier suites. Accept loss of exact row partition/start selection, Codex supersession/default/unknown and Claude alias/suffix folding examples, other-agent exemption, getter/cache read budgets, same-array per-agent cache isolation, replaced-catalog metadata, and tracked-only model exclusion from structured and PTY legacy inventories. These are real narrow policy/metadata alarms intentionally retired without equivalent replacements. Retain real menu collapse/reopen in dropdown/select, AI picker cancel/accept and stale-effort filing, IME carry/gesture/frame transitions, and typed-flip versus held-dispatch fencing. No production or shared fixtures change. No dead timing entry exists for the three deleted drivers; retained timing keys stay unchanged. Static source-value review only; no project import, test runner, native/app execution or runtime validation established. Earlier receipts retain historical scope. Previous oracle (preserved verbatim): Audit3678 current-source reassessment removes20 authored/24 finite root-unit cases across7 retained test sources; all10 selected current drivers80 authored/93 finite were read, leaving60 authored/69 finite unchanged. Removed alarms are the in-memory WAL PRAGMA constant, ordinary latest-dispatch/gate-creation inventories, self-reported-failure and explicit-terminal retry policy rows, SSH/unsupported-provider output fallback labels, legacy typing no-op/retain-before-report/manual scheduling rows, exited output status and copied resume-session metadata, incomplete inventory deferral/reset cascade inventory, and nominal release/active-state/corrupt external history/stopped-abandoned outcome/generic close-exception rows. These are intentional coverage losses, not replaced alarms. Retained source includes actual SQLite rollback/custody, mutable transcript/deletion and saved-copy compatibility, cursor mutation, clear lineage, auto-release timing/restart/admission and concurrent recovery seams. Historical KEEP decisions are consciously superseded where stated in decisions.json. Source-only evidence; no runtime, CI, native, provider, remote, mobile/folder or global completion credit. Previous oracle (preserved verbatim): Audit3672 renewed by3676 removes four authored/four finite cases from federation-output.test.ts: nominal show/read routing and terminal-fallback projection; initial archive-table SELECT failure returning retained/no-close; simulated old-peer read/fleet/refusal and method inventory; and absent archive-release capability refusing release. These actual compatibility/guard alarms are intentionally lost without equivalent replacement; the dropped-table case never reaches capture or commit. Current selected frontier9 becomes5: retain actual temporary transcript parsing/path omission, current no-transcript release/unavailable-output/restart/replay regression, and three home deadline/stop nonmutation cases. Prior held-capture case was already removed upstream and receives no new removal credit; prior redacted snapshot archive behavior was rewritten upstream and is not claimed retained here. The surviving driver timing entry stays. Static review/private composition only; no actual peer/network/native/platform or fresh runtime validation. Previous oracle (preserved verbatim): Audit3671 removes14 authored/32 finite root-unit cases, including the whole WSL move-handler refusal suite, WSL preload object-presence suite and internal gate spelling truth table. Also retire the weak withheld-command non-array shape assertion, static serve-only Add/capability/mock-remove example, grammar scope equality and basic configuration/payload schema roundtrips. Genuine wrong-window/malformed-request/gate-before-work, exact gate spellings/platform/argv precedence, static serve-only routing, declaration mismatch and empty grammar/pair admission alarms are consciously lost without replacement; prior KEEP decisions are reconsidered. Seven unchanged authored/finite root-unit cases retain real image/provider/line budgets, live policy transition/capability broadcast, grammar resource/reference traversal and strict-versus-tolerant literal salvage. Source-value evidence only; no runner/compiler/build/app/native/provider/remote/mobile/folder validation or historical fault claim. Previous oracle (preserved verbatim): Audit3663 renews approved3644/3645/3648/amended3651 at privatec1ef: remove10authored/26finite across7changedtestpaths,4whole drivers; retain23authored/24finite including actualcmdkStrictMode defaultcursor race. Retire version1.5.0/1.5.1→shrinksImages capability threshold and launch-to-prompt wiring alarm. Surviving prompt tests explicitly supply capability and do not replace wiring coverage. Retire resolved-before-personOnly precedence; hook-gone/live-dialog guidance; stale-incarnation versus live-looking dialog classification; delayed/clipped warning extraction and wait-for-paint wiring; 3row setting/personOnly approve/deny/disclaimer suggestion composition;13row setting/reason/person/coordinator admission, close/mail attribution, terminal delivery/refusal/preanswered/personOnly/dropped-allow handler propagation alarms. Real state/access/SQLite effects occur; these genuine losses are accepted, not mocked-away. Retire full command extraction from serialized JSON in worker receipt and nontruncation alarm; no rendering, actual worker approval or native shell proof. Retire2row extra-source first/last order and controlled default cursor projection, plus generic open-palette livefolder add/remove render alarm. Explicitly supersede rawexact297KEEP refresh. Keep actualcmdk/StrictMode default cursor race driver; no default-suite loss claim. No equivalent replacement asserted. Retained queue/commit/retryownership/filebudget, terminalfreshness/provenance/competing-answer, RPC DB-effect, qualifiedfolder/runtimehandoff and lingering-reopen cases have distinct scope. Audit3675 preserves the upstream revised unreported-approval close regression, its escalation helper and adapted survivors. Source-only static renewal; no runner/compiler/app/native/remote/CI claim. Upstream41390host-owned approvalwatch/report lifetime reviewed separately; no new lifetime test removed. Preserve all earlier metadata/evidence. Audit3634: Remove five authored/five finite cases, including the complete structured chat terminal-refusal, terminal-plus-agent worker-start validation, and canonical Git common-directory parser suites. Also remove the gh free-text -R argument projection case. Consciously supersede prior KEEP decisions and accept chat-versus-stale redirect classification, real RPC refusal-before-SQLite-Dispatch, old-Git echoed-option relative-path parsing, WSL execution-space resolution, and free-text preservation/bare-repo qualification alarms. These are genuine narrow alarms, intentionally retired without equivalent replacements. Retain real Git stdin/EOF delivery, bound-account host admission, naming environment/account precedence, upload iterator cancellation and remote path confinement. Shared fixtures and production code remain. Remove only the dead canonical-repo-key timing entry. Static source-value evidence only; all prior evidence retains historical scope. Audit3636: Remove the entire maintenance opportunity internal-count driver (one authored/two finite cases), the mocked remote worker start-card argument case (one/one), and the follow-up heading mapping table (one/two). Root-unit discovery loses three authored/five finite cases. Independently remove four Python native screenshot methods/five statically enumerated subTest scenarios: grim argv/stub payload, failure-code mapping, X11 mocked capture payload, and capability flags. The Python lane is verify:computer-native and is not root Vitest. Accept loss of exact native command/projection and exception/capability alarms, ready-only card publication arguments/agent forwarding, and serialized follow-up heading mapping; no equivalent replacement is claimed. Retain actual ownership/mail/queue/reopen transitions, remote lost-response fencing and first-result settlement, filesystem/maintenance admission and successor progress, wrong-region screenshot refusal and Sway geometry. The whole-deleted new driver has no timing key; this component requires no timing cleanup. No production changes. Static source-value review only; no project import, runner, app, native desktop or runtime validation established. All prior evidence retains historical scope. Audit3629 removes two singleton parser suites: WSL Claude plugin metadata out-of-range index rejection and Git checkout unlink-warning path extraction. Their real range-refusal and quoted-path extraction alarms are intentionally lost without replacement; no WSL execution, checkout recovery or equivalent remaining coverage is claimed. Audit3630 removes the sole orphan automation availability test (1 authored/1 finite). Its real policy kind, orphan precondition, delete/toggle enablement and run/edit refusal alarms are intentionally lost. The test did not dispatch an action, enforce a server generation fence or observe a remote mutation. No equivalent replacement, actual renderer/transport execution or new platform/provider evidence is claimed. Previous account (preserved verbatim): Audit3628: remove the entire one-authored/one-finite RepositoryRemoveProjectSection happy-dom suite. Accept losing the local zero-to-one-terminal stale-count revalidation alarm: first confirmation defers the removal callback and second confirmation invokes it after the count snapshot updates. The test never asserts updated warning text/count, actual terminal creation or removal/PTY teardown, host/scope routing, cancellation or folder/mobile behavior. No equivalent replacement or end-to-end warning-before-teardown proof asserted. Static evidence only; historical receipts retain original scope. Audit3624: Remove one authored/five finite positive Gemini identity table from the Claude/Gemini title collision driver. The real normalization, legacy label and explicit title evidence owners execute, so ordinary Gemini marker/name/wrapper recognition and normalized-label composition alarms are consciously retired without equivalent replacement; these five supplied title-to-literal identity examples have low marginal value. Preserve the six-row Claude task-text collision oracle, whose unrelated CLI mentions must not rebrand or destructively normalize titles. The other audited drivers preserve real permission-mode session transitions, prompt secret/fence/bounds defenses, JSON-to-Ask option/preview preservation, and optional GitLab failure summary/trace composition. Root-unit discovery loses one authored/five finite cases; no whole driver or timing key is removed. No actual terminal stream, UI routing, application/native or runtime execution established; all prior evidence retains historical scope. Audit3618: Remove three authored/three finite browser reload cases, including the complete renderer reload-actions suite. Agent bridge fake did-fail-load plus permanently supplied manager error projects description/code into a rejection. Renderer seeded empty metadata projects a saved URL into plain webview src; its earlier not-ready case projects a live-id/throwing fake reload into absence of a loading patch. Real queue/wait/error/cache/classifier/hook code executes, but these narrow supplied-state and exception-to-callback wirings have low marginal value; no rerender, lifecycle, actual cache generation/retirement or guest commit is asserted. Explicitly supersede audit1822 not-ready KEEP and preliminary3617 cross-module retain opinion, accepting offline-reload failure reporting, empty guest to saved URL routing, native-reload bypass and premature-loading/stranded-state alarms without replacement. Retain distinct main cancellation queue progression and absent queued side-effect dispatch. Root-unit discovery loses three/three. No Electron guest, network failure, restored navigation/commit, app/native focus or runtime execution established; earlier evidence retains its historical scope. Audit3614: Remove thirty-one authored/thirty-nine finite potential cases across CLI format and argument drivers, including three whole suites: search-result/status sanitation sites, Linear issue/list/warning sanitation sites, and quote-stripped JSON diagnostic secret non-echo. Those real formatter/guard transformations execute on supplied strings; intentionally retire their field-specific CSI/OSC/C1/newline/bell and diagnostic secrecy alarms as low-value caller/output inventories, without replacement. Also retire twenty-one search registrations/twenty-four finite cases covering minimal positional request/default omission, exhaustive all-flags request projection, nominal named query/index routes, repeated syntax/agent examples, enum/agent/path-count/timestamp/numeric/cursor admission examples, unknown/browser flag examples and postcommand last-wins projections; retire one parser sentinel-only case. Keep actual ambiguity, conflicting index request, precommand/crosscommand scoping, variadic reference parsing and missing-repeatable-value controls, without claiming equivalent coverage. Root-unit discovery loses all twenty-nine/thirty-seven. No CLI/RPC/terminal/PowerShell/host/schema/runtime execution is established by this source-only audit; earlier evidence retains its historical scope. Audit3613: remove six authored/six finite callbacks, including whole reviewer-display oversize-input and explorer-root record-filter suites, plus plugin family-emoji/invisible-only-name and pool legacy-name/urgent-countdown samples. Accept losing oversized-input empty-result/no-split, malformed-record/prototype-sensitive-key/nonstring filtering, exact ZWJ family preservation, invisible-only name refusal, no-group old-host full-name preservation, and80/79/null-reset countdown composition alarms. No equivalent replacement asserted. Retained strict/runtime sanitization, SVG parser, mobile focus recovery and collision/tie cases have different scopes. Static evidence only; historical receipts retain original scope. Audit3609: Remove the complete pnpm CLI invocation literal-selection suite (eleven authored/nineteen finite potential cases), with no replacement claimed. Root-unit discovery loses JavaScript-versus-native command selection, npm/npx executable rejection, Windows cmd/bat casing and shell selection, non-Windows cmd behavior, native PNPM_HOME flat/bin preference, explicit-path priority, relative-home rejection, missing-native fallback and non-Windows home-probe refusal alarms. The suite called the real resolver with independently expected objects, but exercised no pnpm/node/cmd process, installed native executable or real filesystem probe. Earlier KEEP decisions are consciously superseded by accepting these narrow branch and probe-inventory alarms as low value. Real symlink entry, Git signing isolation, Bun filesystem/Node child normalization and compiler scheduling/continuation tests remain distinct tests, not equivalent coverage. No runner, compiler, app, platform or remote execution was performed in this source-only audit. Earlier evidence retains its historical scope. Audit3608: remove five authored/five finite callbacks, including whole owner-slug and remote favicon URL suites and two repository-label projections. Lose five owner acceptance samples, eleven negative owner samples plus nonstring refusal, six host-local icon refusal samples, legacy-SSH same-path label-map cardinality, and explicit runtime ownership over stale legacy metadata at label level. These are accepted narrow grammar/presentation alarms with no equivalent replacement claimed. Retained binary parser, React recovery and label grouping/platform cases do not replace these losses. Static evidence only; historical receipts retain their original scope. Audit3606 adopts the enhanced3604/3605 icon cuts: nine authored/nine finite cases removed, including the whole tray-attention-icon suite. Retires the no-encoder oversized-upload null sample, missing-ID/request-order sample, encoder-null PNG-prefix fallback, SVG/404 rejection samples, transparent amber-dot placement and half-alpha white-tint sample, exact small fetched-byte passthrough, host-qualified image-key inventory and constant-encoder large-upload projection. These specific admission, formatting and bitmap alarms are intentionally lost without equivalent replacements. Retained queue/cache, bounded fetch and Dock lifecycle checks do not establish actual raster decoding, native image conversion or rendered appearance. Historical receipts below are preserved evidence for earlier test sets, not fresh validation of this reduced selection. Audit3601 scope: remove the whole custom-sound happy-path IPC suite and one custom-label/multiline notification formatting example (2 authored/2 finite). Intentionally lose configured .ogg selection, exact byte/MIME success wiring, successful dispatch for the formatting fixture, custom-label whitespace/title normalization and assistant-body whitespace/180-character-cap alarms. Retain prompt-exclusion, persisted qualified dismissal/restart, theme ownership/restore and Ghostty precedence/collision cases; none replaces the retired sound/formatting alarms. No actual playback, native delivery, phone/network, cross-platform or new runtime evidence is claimed; historical execution keeps its original scope. Audit3598: Remove seven authored/nine finite literal-table potential test registrations across workflow routing, Claude cold-start and real-zsh preexec drivers. Root-unit discovery loses six authored/six finite: three test-local workflow parser fixtures, the five-positive/three-negative explicit command-paint route inventory, a boot-banner-before-composer sample and an already-painted bare-title positive sample. The separately selected shell_contracts live-shell driver loses one authored/three finite raw-capture paint-only cases across three hook configurations; its real-wrapper launch-order, four rechunkings (twelve finite) and runtime-wait replay remain, without claiming equivalent replacement. Intentionally lose those parser, exact routing-list, banner, post-paint subscription and native-read-boundary paint alarms. Retained workflow/UNIT_EXCLUDE/classifier cross-source routing checks and readiness-race-to-writer timing are distinct bounded alarms. No runner, real shell, PTY, app, native focus, remote delivery, cross-platform execution or full-feature correctness is established by this source-only audit. Earlier evidence retains its historical scope. Audit3597 scope: remove two renderer-hang-capture examples (2 authored/2 finite): an invalid repeated-token oversized reducer fixture and a shorter unanswered-request fixture. Intentionally lose the direct truncated flag/sanitized frame fixture and request-pending status/zero-sample alarms. Retain window-death/stale-result exclusion, generation reconciliation, and baseline-publication cancellation lifecycles; they are not equivalent bound, redaction or pending-status replacements. Historical execution retains its original source scope; no new runtime evidence is claimed. Audit3591: Retire the whole one-case background journal-producer attribution suite and the fabricated malformed-details-block case (two authored/two finite cases). Intentionally lose the fixed own-task versus output/foreign-task attribution examples across flat/message row shapes, missing-root classification, and direct unbalanced/detailsish fabricated-block rejection alarms. Retained parser sibling/depth and editor navigation tests do not replace these alarms. No equivalent replacement or fresh runtime, transport, persistence, native focus or rendered UI validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3592: Retire the whole three-case shared agent smart-key resolver suite, explicitly superseding its Audit3238/3237 keep decisions. Intentionally lose literal permission-versus-question classification, longest-question digit selection, Codex async-question opener/digit/Enter/skip mapping, and unsupported-agent/state/prompt refusal examples. These were real parser/selection and literal-byte alarms; they did not exercise terminal delivery or timing. No equivalent replacement or fresh runtime, native TUI, mobile, remote or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3587/3588: Retire six authored/fourteen finite cases, including the whole WSL stopped-notice and connecting-status selector suites. Intentionally lose direct WSL retry connection field/target/ready-epoch/null-guard examples, connecting/reconnecting distro and absent-state selector samples, and stopped/connecting notice text/button/data-attribute examples. Retained host-ready retry and content-loader/reload lifecycles do not replace these alarms. No equivalent replacement or fresh runtime, native WSL, remote, rendered UI or performance validation is claimed. Earlier dated text and evidence retain their historical scopes. Audit3580: Retire the whole one-case runtime host environment prototype/subclass suite. Intentionally lose the exact configured lower-case Windows Claude home to account-identity normalization and host-account override precedence alarm previously retained by Audit3129. No equivalent replacement or fresh runtime validation is claimed. Earlier dated keep statements below describe their historical source scope; the current suite is removed. Audit3563 scope: retire the whole Kiro OSC/hook identity suite and the OpenCode2 identity-only case; reduce Codex cached startup child-retirement and missing-session-ID tables to one case each (2 authored/4 finite removals). Intentionally lose Kiro alias/session continuity, OpenCode2 completed OSC identity continuity, cached replacement without relay-generation restart, and missing-previous-ID context-preservation alarms. Remaining provider, remote compatibility, persistence and cross-session pollution cases are not equivalent replacements. Also remove the private Codex roster-size assertion while retaining public child-absence checks. Historical evidence retains its original source scope; no new execution or transferred pass validates these reductions. Audit3553/3559 current scope: delete worktrees-delete-pty-teardown.test.ts and worktrees-issue-command-overrides.test.ts entirely and remove three cases from worktrees-orphan-directory-cleanup.test.ts (6 authored/6 finite removals). Intentionally retire typed unserved-SSH create/delete admission, exact-name collision/error-token, proven-orphan no-force confirmation, standalone .git-directory refusal, and SSH-owned local-path preservation alarms. Retain only actual authorized orphan deletion and no-marker notes-file preservation; they are not equivalent replacements. The no-marker fixture resolves the default getRepos owner, so it does not prove temporary owner-repo binding. Git/Store/PTY/notifications are mocked; no physical process stop, durable metadata, real SSH delivery or native UI proof is claimed. Unchanged historical oracle, coverage, evidenceRuns and escaped receipts retain their original scopes only; no fresh execution or transferred pass validates the reduced sources. Audit3492 composes ROOT-approved75(AUTHOR3475/PEER3476 and AUTHOR3485/PEER3481, including five-field clarification and zero-credit selector3483) plus123(AUTHOR3486/PEER3487 reconciled FINAL3491, including peer case10:3 and9:0 wording clarifications). It intentionally removes198 authored registrations/232 finite literal-table potential registrations across70 changed JS/TS test sources, including45 whole suites;87 authored/90 finite potential registrations remain across95 reviewed JS/TS bodies. One mobile native-suspension manual recipe and the now-unreferenced GitLab mock fixture helper are separately removed with zero registration credit. Static literal table potential is not execution. No equivalent replacement, fresh runtime result or merge credit is claimed. Earlier135 complete dated receipts, all raw gaps and full prior98/161/history notes retain their exact scopes and bytes. The additional123 retirement accepts loss of mocked GitLab request/error/identity/classification inventories; basic Linear migration and metadata discovery; simulated OpenCode key failed-save retention/retry/log redaction; analytics identity-flush smoke that did not prove ordering; AppImage static entry guards and empty-staging cleanup; GPU/process/minidump breadcrumb/annotation shapes and weak redaction oracles; namespace/status/foreign-launcher ownership guards and persistence roundtrips; profile transfer field projections; worker hook/screen/incarnation/result admission/classification guards; and source-coupled CLI transaction fixtures. Privileged publication failure injection was a false-positive: its mkdir regex missed -m 700 and outer rejection accepted an inner assertion before any shell ran. The identity-flush case awaited identity after flush and could pass despite early flush. Retained actual negative-token cache recovery, SQL filter-before-LIMIT open approval, unreadable-root FTS preservation/degradation, latest-connect-wins persistence, worker stop-debt settlement, unmodified same-inode foreign-content transaction, explicit incomplete-pagination error, FTS own-message snippet association, filter-before-route result and bounded-search truncation honesty are distinct bounded outcomes, not replacements for retired alarms. Case9:0 checks both roots before chmod but only mounted-session/degraded-root after chmod. Entire extraction-root suite is removed; no retained extraction cleanup is claimed. Removing the manual iOS foreground-stall shell script loses its unique real native-suspension recipe; timer tests do not replace it. First-match PIDs, indefinite waiting, broad success matching and absent trap cleanup made that diagnostic brittle and able to leave a desktop stopped. The current Windows operational driver remains byte-exact, preserving already-merged removal of orcad-convert and orcad-cli-convert selectors and the retained cmd/powershell/managed CLI lanes. The package skill-sharing release selector loses only the exact already-retired skills-suite token; no extra test credit. Included approved75 retirement account follows (historical proposal labels identify provenance; final198 counts above control): Audit3488 composes ROOT-approved source49(AUTHOR3485/PEER3481, including its immutable five-field clarification) and source26(AUTHOR3475/PEER3476): intentionally removes75 authored registrations/87 finite potential registrations across35 changed sources, including25 whole suites;35 authored/36 finite potential registrations remain in the combined reviewed scopes. Literal table potential counts are not executions. This source-only reduction has no equivalent replacement, fresh runtime result or merge credit. Root unit discovery deliberately loses entire message-row filtering/chunk reconstruction, discussion-pagination rejection passthrough, Linear bearer/stack redaction, new-profile telemetry/default/existing-JSON no-op, WSL user-trigger routing, terminal-delivery refusal, native-chat worker-approval UI, followup-paste routing, live-surface tombstone refusal, activity-bar hydration, fabricated approval-caller policy, child-PATH transformation and branch-review URL/identity suites. It also loses cache roundtrip/mode/fingerprint/fallback/no-op samples; GPU field-shape privacy examples; older auth-probe failure retention; orphan SQLite-sidecar/future-schema admission; usage tie/duplicate/composite-key examples; allocation-name sanitizer/unknown-prefix examples; real worker-and-backup preflight wrapper smoke; and JSONL descriptor-loop truncation. The discussion page contained id-only discussions without notes, so its rejection did not prove a nonempty notes-prefix oracle. The preflight did launch actual worker/backup entrypoints with production internal self-validation; removing its revision/version wrapper deliberately retires that real-entry smoke alarm. The skipped-prefix JSONL keeper rewrites/grows after collecting the suffix and before final validation, not concurrently during parsing. Retained queue rejection/recovery and held/drain mechanisms, recorder nonsettling-provider deadline, corrupt authoritative-DB refusal, legacy cache split migration, merge-added-row indexing, external-frame redaction, and pinned JSONL read/verification/mutation checks are bounded concrete oracles, not equivalent replacements for retired alarms. The full cloud relay cohort/ceiling/control suite is separately removed from the relay package runner, outside root unit discovery; no cloud runtime result is claimed. The Python computer-use benchmark remains unchanged tooling with zero authored/removal credit; no native benchmark trial ran. The package skill-sharing release selector drops only the exact already-deleted skills-suite token, with zero additional removal credit. Prior dated receipts, raw gaps, and complete earlier161/98/history notes remain exactly at their original scopes; no changed-source historical receipt becomes fresh reduced-suite evidence. Included approved26 alarm account: Broad root-unit discovery loses entire ref-label string, hostile/malformed CSV-link rendering, Codex filename-discovery, mocked paged-preview postscan mutation, usage path-containment, real symlink-ancestor, Codex skip RPC, local/remote inference routing, width-preference, and synthetic workspace-document input suites. It also loses the hand-assembled sorted CSV edit example, connect-time JPEG seed and invalid AVCC guard samples, and eight-record/64KiB detector boundary cases. The symlink suite exercised actual kernel traversal; its lost ancestor alarm could omit supplemental local editor watches and leave content stale, not bypass authorization. Retained linked stream cancellation, preview-only save fence, failed-flush dirty-draft preservation, clipboard byte/edit preservation, fragmented AVCC parsing and independent quoted-CSV interpretation checks do not replace these deliberately retired alarms. The stream fixture uses an in-memory source and does not establish actual fd cleanup; editor save endpoints are synthetic, browser events are synthetic, and no native focus, actual remote delivery, real disk write, cross-platform execution or interoperability is claimed. Earlier complete dated receipts, raw known gaps and previous notes are historical evidence at their original source/selector scope, not fresh evidence for this reduced suite. Audit3438 composes ROOT-adopted3434/independent3436: intentionally removes60 authored/62 potential expanded cases across16 sources, deleting9 whole suites. Eight authored/eight potential cases remain; delete the102line unused ACP fixture and57line unused teardown helper with zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses ACP model-catalog fixtures, resume-record projections, dashboard mock payload/state cases, mobile folder/launch/session examples and dictionary pipeline rows. Selected E2E removals also retire auto-conversion and golden-core journeys and one Windows CLI conversion registration. Retain ACP reply text across a resumed subagent, both VAD checks, focused tab send ownership, async focus ownership in happy-dom, the question-skip journey, the original managed Windows CLI lifecycle journey and dictionary substitution. These checks do not replace the removed alarms. Audit3470 composes ROOT-approved AUTHOR3466/PEER3467 source proposal: intentionally removes38 authored registrations/59 finite potential expansions, including8 whole registering suites, retaining13 authored registrations/18 finite potential expansions. The sole dead shell-recipe caller cleanup has zero additional test credit. No equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses process-import boundary, Korean/Japanese locale policy, worker-reset, mobile agent-history/queued-message and median-summary suites, plus packaged mobile-manifest, worker-pool fixture, HTML preview, mobile shell and terminal samples. The environment-provider contract and its sole shell-recipe caller are deleted. Separate cross-version-wire discovery loses the large byte-budget fresh-snapshot/replay sample while retaining independent live same-revision note publication; the retained terminal stale-refresh and dirty HTML draft persistence checks do not replace removed alarms. The source4 unused start/stop fixture hooks are removed; its retained nested failed-import test remains byte-exact with its own lifecycle. Unchanged SVG preview and Git-resolution registrations stay intact. Root balanced CI excludes the cross-version directory, which has a separate job; unbalanced root discovery can include its parent. No native app/device, actual remote, released-pair interoperability or cross-platform execution result is claimed. Audit3468 accepted source proposal AUTHOR3462/PEER3463, composed privately by audit3469: removes62 authored registrations/95 finite potential expansions, including5 whole suites, retaining25 authored registrations/29 finite potential expansions. ROOT approved source value; no equivalent replacement, fresh runtime or merge credit is claimed. Broad root-unit discovery loses the profile-state worker smoke, worker-agent views and three search discovery suites, plus samples from node-server qualification, skill-bundle generation, CLI hooks/profile/recovery/offline admission, permission-hook holds and search indexing. The separate Cloud relay runner loses one relay blackbox sample; unchanged mobile native-chat and dependent-transcript drivers remain. Retained source callback/table/ancestor/fixture bytes bind to the sealed candidate. No physical-device, mixed-version, remote, cross-platform or app execution result is claimed. Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. Broad root-unit discovery loses whole cross-client workspace-sync, terminal-preview, picker-skill-hint and close-routing suites plus reduced worker-stop, remote-owned-record, scanner, mobile-bundle and daemon-health samples. Separate E2E discovery also loses the paired mobile/desktop mirror journey and two unit drivers. Retained callback/table/outer-loop/guard bytes are exact; retained checks do not replace the removed alarms. No app, native device, mixed-version, remote or cross-platform execution result is claimed. Audit3456 composes ROOT-adopted3449/independent3450: intentionally removes62 authored/82 potential expanded cases from16 selected drivers, including7 whole suites, while13 authored/13 potential cases remain. Delete the unused48line legacy workspace-session JSON fixture with zero test credit. These are source-only reductions without equivalent replacement or fresh runtime claims. Broad unit discovery intentionally loses whole trust-dialog observer, background-turn notifications, redirected session refusal, typed-agent identity, chat-tab lifetime-cost, model-manager and mobile diff-review controller suites, plus individual persistence, synthetic composition, pairing, manifest and subprocess samples. Retain deferred snapshot dirty-domain persistence, two direct editor ownership cases, stream counter-domain distinction, two persisted pairing identity cases, parser entrypoint asymmetry, dictation retry and process-group timeout; paired restart and packaged old-binary/corrupt-profile journeys remain in their E2E driver scope. Retained checks do not replace removed alarms. No fresh execution, native IME/microphone, real authentication, mixed-version/platform or performance result is claimed. Audit3451 composes ROOT-adopted3444/independent3445: intentionally removes101 authored registrations across16 reviewed sources, deleting7 whole suites and reducing8; one reviewed source is unchanged. Fresh main has123 authored registrations before and22 after because an upstream direct-child shell regression remains verbatim with zero removal credit. Registered removals are100+7*S and explicit-active removals99+7*S, where S is the unprobed count0..3 of admitted bash/zsh/fish shells. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses extra stream lifecycle/forwarding examples, unreadable-store/cache projections, orchestration presentation/benchmark cases, remote-owned routing matrices, mobile legacy navigation and model-catalog listing/cache examples. Whole suites retire renderer client-event projections, manually assembled structured-session reader ownership, mobile prompt presentation, renderer AI-vault resume commands, mobile Jira URL lookup, composer retention and mobile folder source-repository queries. Retained checks preserve original stream/store/orchestration/routing/navigation/catalog cases and shell behavior, including the upstream direct-child case. No real provider, native desktop or mobile acceptance is established by this reduction. Audit3446 composes ROOT-adopted3441/independent3442: intentionally removes100 authored registrations/119 potential expanded cases (118 conditional-active before platform gates) across16 sources, retaining11 authored/13 expanded cases and deleting6 whole suites. Two parent registrar import/call cleanups and the unused68line CLI recording fixture carry zero test credit. No equivalent replacement or fresh execution is claimed. Broad unit and mobile discovery loses interrupt/wake registrars, task storage/session-close/account snapshot hooks, permission tables, copy/preference/model-discovery/composer examples, launch placement examples, VM projections and extra mobile-history scenarios. Retained checks cover abandoned versus dispatched fork uncertainty; /fast dispatch supersession; discovery lock release after exit; stale-send draft preservation; OSC status lifetime; future-version file preservation; launch cancellation before/during dispatch on both layout owners; mobile resume addressability before hooks; and recipe snapshot cleanup after plugin removal. The paired browser-link Electron source is unchanged. Terminal launch seams, mobile roles-only history and injected provider results do not establish native terminal destruction, transcript text fidelity or real remote provider cleanup. Audit3437 composes ROOT-adopted3431/independent3432: intentionally removes127 authored/174 potential expanded cases across12 sources, deleting7 whole suites and one267line unused helper without test credit. Retain8 authored/13 potential cases. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses reduced or deleted relay, coordinator mail, recovery watchdog, automation observer, launch picker, folder PTY, profile restart, web migration, retirement, pointer delivery, agent-row and run-bound mailbox examples. Retained checks cover relay hello/payload and SIGTERM drain, coordinator pending-check and clear-to-Run continuity, profile persistence, the full six-row web merge table and real run-bound delivery waiter. These do not replace removed alarms. Audit3429 composes root-adopted3426/independent3427: intentionally removes168 authored/205 potential expanded cases across16 semantic sources, deleting11 whole suites and one304line unused assertion helper. Five authored/five potential cases remain; the duplicate500k graph row is dropped while its50k row and callback remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted migration/recovery/federation/rich-draft/paste/automation/mobile/catalog examples. Keep server-edit preservation during migration, native supervised cancellation holding the agent home until physical stop, pagehide lost-text preservation, graph old-result WRAPPER WeakRef collection with50k rows, and mobile React render isolation. The WeakRef does not prove all commit objects are collectible or a memory budget. Audit3425 composes root-adopted3420/independent3421: intentionally removes83 authored/112 potential expanded registrations across13 semantic sources, deleting7 whole conventional suites and1 registering helper;7 authored/10 potential expanded cases remain. No equivalent replacement or fresh execution is claimed. Broad unit discovery loses the adopted mock orchestration, refusal, restart and storage examples. Retain unchanged two-phase real-WSL deployment/survival, the reduced idle-release holder proof without restart/first-output measurement, real worker stop/release cleanup barrier rows, physical Git branch-preservation rows, and SSH disconnected-close proof. Other SSH and CLI variants and WSL telemetry/warmup churn are intentionally removed. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. Broad root-unit discovery loses naming/title/queued-host fixtures, main-window focus-shortcut and attach-services mock orchestration, Git Bash console-capacity fake process inventories, most real-Git preparation/locking variants and Claude/Codex catch-up examples. Keep lifetime rejected-writer followed by late termination proof and autonomous real-checkout cleanup; two-row real-Git rollback after a real move preserving a replacement/admin marker or revoked permission; a live translator write during a held transcript read fencing stale Claude history append; and Codex expected-item revision rejection before a historical writer shim. Codex driver registers retained bounds exactly once with original inputs; bounds does not prove modern writer persistence. Accepted changed-lock preflight sentinel and omitted-parent routing losses are not replaced. Audit3413 composes root-adopted3405/independent3406 and3409/independent3410: intentionally removes337 authored registrations/363potential expanded cases from13sources, retaining31authored/32potential cases and deleting one whole clipboard suite, without equivalent replacement or fresh execution. Broad root-unit discovery loses35WSL examples,22STT examples,22shared-server-banner examples,18clipboard-handler examples (whole suite deleted),14VM lifecycle examples and16commit-message cancellation examples. Retain4WSL and7STT mocked connector/worker cases,1rendered stale-banner refusal,2VM guard/failed-compensation cases and1late-stream ownership cancellation case; real WSL/microphone/remote-provider proof is not established. Separately discovered e2e-unit drivers lose13cross-version-wire local cases and15mobile active-publication cases, keeping same-process disk-reopen/new-host cursor content/epoch/sequence outcomes and a host/renderer-snapshot selection and inventory revision race respectively. Imported released stop-note scenarios remain unchanged and receive no duplicate credit. Cloud suites are outside root UNIT_INCLUDE. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. The broad root-unit command loses the UNIT_INCLUDE src/ subset of retired examples; mobile is outside UNIT_INCLUDE. Other lost alarms are recorded by the adopted per-case source reviews. Audit3390 composes root-adopted3382/independent3383 and3386/independent3387 and3388/independent3389: intentionally retires216 authored registrations/230 expanded cases in42 whole authored suites plus5 zero-case registration drivers, without equivalent replacement. Lost local alarms include fake PTY identity/routing/startup/replay/mode/title/mouse/IME/scroll matrices, broad startup identity and file-open wiring, mock mailbox title recovery, mobile row parity and whole-v-selective save parity. Retained24 registrations/26 expanded cases in22 suites preserve actual xterm Windows buffer/wrap/scroll, typed TOML scalar, real Promise cancellation/settlement, HTTP Codex scanner/projector semantics, durable recovery admission/newer-schema bytes, federation completion/ACK/SQLite atomicity and failed stale-run-write versus imported durable history. Additional retained clock-domain scheduling, durable freeze/ACK/checkpoint ordering, ABA dirty intent, SQL atomicity, folder-domain retier/last-note deletion, scrollback durable no-sync staging, PanelBridge subscriber lifetime and reserved-send publication preserve narrower concrete behaviors. Current finite owner drift assessed in bounded defining bodies; retained callback bytes unchanged. Historical notes and complete dated receipts keep their original exact scopes. No new execution, equivalent replacement, native focus/device/pixel, live remote or global mixed-version guarantee is claimed. Audit3381 composes root-adopted3376/independent3377 and3379/independent3380: intentionally retires436 authored registrations/670 expanded cases, including79 whole suites and12877 test lines, without equivalent replacement. Lost alarms include scheduling, account/host/status/filter/format inventories, microphone/image cleanup, title/bell/capability lifecycle routing, access/impersonation/outcome matrices, paste/input resend/ledger/retention, stale completions/reload containment, trust-read failure and popup OAuth/security/geometry vectors. Retained30 registrations/34expanded cases in22 suites preserve narrower SQLite TEMP/TZ/WAL, HTTP transcript root resume, mobile original-index Choose/Submit, child environment/Git lock expiry, stale ACK/PTY lifetime/peer fairness/durable replay/output ordering, actual DOM paste/editor focus, DEC2026 conservation, filesystem rollback, owned buffer lifetime, actual OSC parser/tracker handback, POSIX Python UTF8/fallback, durable trust concurrency/version, OpenSSH verdicts and hidden Chromium POST/route lifecycle. Mocked boundaries limit claims. Current bounded defining-owner reads preserve the retained oracles; captured identities are not full transitive semantic review. Both component blind pins authenticated before exchange; final reconciliation and explicit postpin3380reason correction supplement control, without retroactive blind claims. Root unit command excludes mobile and separately admitted Electron probes; use existing respective runners. No fresh execution, native focus/device/pixel UI, live SSH/remote, equivalent replacement or broad mixed-version/cross-platform validation is claimed. Prior dated receipts retain exact original scopes. Audit3375 composes root-adopted3370/independent3371 and3373/independent3374: intentionally retires79 authored registrations/84 expanded cases, including39 whole suites, without equivalent replacement. Lost alarms include federation field/parser inventories; Claude rejected-key precedence, credential clone redaction, managed-versus-user hook preservation and CLI defaults; setting/status/activity projections, mocked SSH/notification/reveal/Linear refusal, fake-clock reconnect and noise; image-fold artifact visibility, paste eligibility, setup trust, scripted ACP error-to-journal publication and ligature cache tuple aliasing; plus supplied folder-runner uniqueness, mobile host-overlay/credential/update/preference races, status/markup/title/keyed-row projections, subscriber cleanup, cancellation/close/refusal/link-issue and mirror suppression alarms. Retained3373 merger contiguity, dual-clock overdue-resume exactly-once, rejected-paste IPC sink and annotation schema roundtrip remain bounded. Retained actual HTTP/OSC status composition, destructive sibling-filesystem isolation, external contrast invariant and fixture CSS pixel intervention remain narrower. Original reviewed87authored/92expanded become9/9; current OpenCode suite adds two preserved upstream cases and the synthetic-title suite gained one Cursor policy mock-call case, independently reviewed for removal in root3378; current90/95 become11/11. That Cursor suppression/ready-frame regression alarm is intentionally lost; it does not observe native title ownership or tab flicker. For3370 only the peer was independently blind, with author exposure explicit;3373/3374 both blind pins were verified. This broad root unit command selects the reduced root unit suite; UNIT_INCLUDE excludes mobile tests, which require the existing mobile runner; retained Playwright component-pixel spec uses its separate existing headless fixture runner and is not validated by the broad unit command. No fresh execution, actual agent/shell/provider, OS material/focus, historical import equivalence, mobile, mixed-version or cross-platform evidence is claimed. All dated receipts retain their original scope. Audit3368 composes root-adopted3363/3364 and3367/3369: intentionally retires181 authored registrations/196 expanded cases, including28 whole suites, without equivalent replacement. Lost alarms include hibernation attention/liveness/backoff/sequential teardown and inventory, host launch compatibility/legacy/Kimi/resource cleanup, terminal recovery parser/pause/quarantine/remount, window-visibility omission-fence lifetime, launch-option rendered Start, sleeping-resume saved recipe/WSL quoting/cancellation, plus removed project/window/input variants. Additional tiny-suite losses include sentence/label/field/default inventories, credential omission, Windows argument/environment mapping, URI decoding, diagnostics, provider classification and fake-clock callback/parser examples. Retained ten registrations/twelve expanded cases include two literal Unicode Mn/Cf zero-width range guards in the actual character-range algorithm and protect narrower delayed editor bytes/destructive ownership, mounted hidden collision repair, ten transport epochs, duplicate-command refusal, caller timeout typing, executor unknown-opener refusal and failed-chunk user bytes. This broad unit command selects the reduced suite; no fresh runtime, real provider/network/PTY, filesystem restart, native UI, historical import equivalence, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3362 composes root-adopted3358/independent3360: intentionally retires129 authored registrations/140 expanded cases, including four whole suites, without equivalent replacement. Lost alarms include supplied orphan-adoption retry/malformed inventory/cancellation; fake-terminal replay parser/wedge/disposal callbacks; all-host catalog metadata and Windows preferences; host keepalive/protocol/stale/settings failures; and removed exact-stop/suppression, source save lifetime, diff-note queue, rename/editor close, input flood/chunk refusal/limits, purge and creation examples. Retained18 registrations/21 expanded cases protect narrower ownership, generation, dirty-state, queue convergence, encoding and attachment-lease outcomes. No replacement or keeper quota claim. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY/xterm parser, disk restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3359 composes root-adopted source3355/independent3356: intentionally retires130 authored registrations/139 expanded cases, including eight whole suites, without equivalent replacement. Lost alarms include strict create naming/capability/legacy payloads; supplied transport host replacement, deadline reattach, create-handoff and split activation; agent-session fork/clipboard/platform flags; shutdown cleared-scrollback/unmounted bindings; simulator host-collision/selection/shutdown; and removed editor, catalog, mutation, launch and status examples. Retained12 adverse-ordering/state-consequence cases are narrower and do not replace all removed alarms. Two queue absences receive zero removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH/PTY, filesystem restart, native UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3349 composes adopted batches3343,3345 and refreshed3354: intentionally retires234 authored registrations/294 expanded cases, including13 whole suites, without equivalent replacement. Lost alarms include supplied terminal reconnect/replacement/typeahead/mirror/result and snapshot examples; synthetic render-desync persistence/recovery; worktree listing/owner/catalog/lineage/removal and activation/creation examples; host-scoped PR and task-record projections; editor diff/status/comment and UI send-target examples; automation observation/wake, supplied sleep/deadline clock and native-chat completion/notification/relaunch samples; held-tab layout and cleanup/progress guard examples. Retained adverse-ordering and wire-boundary cases are narrower and do not replace all removed alarms. The already-upstream visibility-resume deletion receives zero new removal credit. This broad unit command selects the reduced suite; no fresh execution, historical import equivalence, real provider/network/SSH, native process/power/GPU, rendered UI, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain their original scope. Audit3342 composes adopted source batches3333/3334,3336/3337 and3340/3341: intentionally retires180 authored registrations/263 expanded cases, including seven whole suites, without equivalent replacement. Lost alarms include corrupt editor hydration; mobile editor/diff publication and privacy projections; stale-resubscribe routing; model-picker projection/refetch/retry/fallback; editor check-details and frame property inventories; latched-pane cleanup/quiescence; client create replay identity/options/compatibility; and removed owner, async routing, terminal and layout examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts keep their original scope. Audit3335 composes adopted source unions3327/3328 and3330/3332: intentionally retires100 authored registrations/123 expanded cases, including ten whole suites, without equivalent replacement. Lost alarms include seeded host persistence/contention and stale-host cleanup; browser snapshot assembly; mocked wake/DPR callbacks and frame budgets; preflight projections; palette authority/cache suppression, URL intent and saved-title cross-repo collisions; metadata coverage/set patch and dropdown assembly; and removed terminal, worktree, review-refresh and owner-routing examples. Retained definitions do not replace these alarms. This broad root unit command selects the reduced suite; no fresh execution, historical source/import equivalence, real provider/network/SSH, rendered UI/GPU/focus, durability restart, mobile, mixed-version or cross-platform validation is claimed. Unrelated dated receipts retain only their original scope. Audit 3318 composes adopted sources 3310, 3313 and 3317: intentionally retires 71 authored declarations and 102 expanded cases, including whole terminal-detach agent-retention, seeded batch/single open-files equivalence, shallow context-menu shortcut-badge, mocked active-workspace delete-dispatch, workspace session hydration, SSH target cleanup, editor session hydration and worktree terminal purge suites. Lost alarms include chained transfer alias and immediate close/exit retention; batch/single divergence, survivor/selection/map preservation and input nonmutation across 400 imperative seeds (one registered case); disconnected pending-handle retry, authoritative absences/close-record shortcut/older-host absence and published-presence/unpublished-frame examples; published empty host-graph omission to pane exit; legacy search ranking/exclusion fallback; provider request upgrade/dedupe/finalizer ownership; asynchronous PR suppression/primary-change and same-number different-repository URL rejection. Deletion routing loses host/instance qualification, paired and legacy mapping, folder ownership, active-versus-hover/menu-row selection, ambiguity, modal/editable/creation/view/main/deleting/missing guards and shortcut advertisement. Unchanged real keyboard/hover/confirmation/catalog E2E remains narrower, not an equivalent replacement. Additional losses include scripted old-host disk/UTF-8/noop/baseline and landed/unlanded lost-reply/replacement-retry outcomes; ordinary/repeated healthy-link reattach; cross-owner terminal/chat binding; oversized replay permanent-unavailable classification; floating reveal keyboard ownership/close/disable/listener disposal; naming/retained-turn/Sleep/error/close/reject status tables; SSH membership prune/new-target-only reads and environment/local bucket isolation; same-frame receipt capacity and positive bare/headless publisher readmission. These actual regression alarms are deliberately lost; no fresh gate, provider, rendered, mobile, mixed-version, cross-platform, soak or measured-budget validation is claimed. Audit 3312 composes adopted sources 3302 and 3308: deliberately retires 33 authored declarations and 36 expanded cases, including the whole backend startup-focus, active-browser IPC-close-routing, remote-browser-creation and agent-terminal-presence suites. Intentionally lost alarms cover distinct six-pane reconnect handles; SSH host label/Connect callback, unhydrated visit cleanup, repo cache aliases and hidden worktree/tab/PTY cleanup; adopted-agent startup focus and viewer navigation/reuse; staged, pageless, source, stale and orphan browser-close routing; web local-materialization refusal, desktop incapable-host fallback, failed/thrown remote creation and paired-web sign-in refusal; BOM/NUL and remote/local custom-editor byte-read examples; immediate folder activation and unread clearing; and batching idle/coalescing, accumulated byte ceiling, leading-edge-disabled, standalone synchronous oversize rejection, validation yield, deferred pending-tail order and both viewport-clear examples. The newly reintroduced agent-terminal-presence suite also loses actual terminal-status membership positive and nonterminal-status callback suppression alarms, including the recent 40916 guard regression; the older different 2133 presence-suite history remains historical and receives zero duplicate removal credit. These real regression alarms are intentionally lost. The unchanged survivors provide narrower coverage, with no equivalent replacement or fresh gate, provider, rendered, remote/mobile, mixed-version, cross-platform, soak or measured-budget validation claimed. Audit 3301 combines adopted source batches 3296 and 3299: retire 27 authored declarations and 36 expanded scenarios, including six whole suites. Intentionally lost alarms cover failed-verification/recovery/restart counters and mocked hosting/toast; early overlapping pane-read scheduling/count; unreadable removal listing draft guard; bulk-purge remote-qualified and raw-local guard maps; initial-snapshot blank/mode/oversize/printable/fresh-spawn callback and replay examples; fallback mobile file close and ordinary MRU; default queue overload/drain; seeded browser-page reuse and supplied missing-page URL recreation; ambiguous sleep/hibernation owner refusal and untouched bindings/origins; ordinary selection/activity/fallback priority; skip-remote success and retained hints; supplied structured-chat unsent retention and scoped removal draft eviction; caller-chat focus precedence; open/draft PR cache eviction and merged-head fallback result inventories. Retained adverse-ordering and ownership cases are narrower and do not establish equivalent replacements. No fresh runtime, native, physical provider, cross-platform, mobile or mixed-version evidence is claimed. Audit 3292 combines adopted 3285/3287/3289/3293 removals: 40 authored declarations and 47 expanded scenarios, including whole frame-editor-view, ambiguous remote-delete-refusal, unverifiable-host subscription and editor-close cleanup suites. The previously reviewed dock-badge suite was already removed upstream in PR 40895 and receives zero new removal credit. Intentionally retired alarms include background browser selection and last-tab landing; main startup fallback, SSH project cleanup/survivor routing, exact-PR contradiction/saved-push positives, replay/sidecar rollback and wrapped remote error normalization; records/snapshot missing-diff hydration, ambiguous folder-owner delete refusal, focus no-op notifications, nested editor context-only cache check, pending broadcast-first status and ordinary four-confirmation aggregation; preview-source frontmatter pruning, approved local/runtime agent dispatch, non-GitHub legacy PR cache invalidation, stale merged-head transient-error fallback, empty-queue immediate query reply and unverified-to-concrete preflight risk. Additional intentionally retired alarms are injected host subscription/unsubscribe counts, synthetic editor reveal/draft/fallback/scoped close, ambiguous unread refusals and hidden mark/read roundtrip, immediate connect backoff/noError, positive mirrored evidence-clock renewal, supplied VM failure/success projections and git-local/folder-SSH wake permutations. Remaining temporal/ownership cases are narrower; no equivalent replacement or fresh execution is claimed. Audit 3283 removes 20 authored declarations and 31 expanded scenarios, including two whole suites. Retired alarms cover PR refresh-state pruning, shared runtime subscription cleanup and store isolation, language content-change routing and older completion ownership, ordinary panel retry-timer cleanup and pending loading, four hosted-review lifetime permutations, mocked terminal creation error/order wiring, five sleep-inventory variants with pending-frame-only assertions, palette fallback/composite highlights, Windows/UNC encoded-editor input rows, mixed-repository partial failure counts, title setters and generated-title bypass/replacement, five window-close coordinator delegation/guard/finally branches, and ambiguous-old-record frame reveal. These regression checks are intentionally lost under the test-value audit; retained cases are not equivalent replacements. Prior coverage notes and receipts remain historical. Audit 3273 removes 14 authored declarations and 19 expanded scenarios across four whole suites and eight partial suites. Retired alarms cover saved-local cancellation/layout ownership, ambiguous-owner warning reset, folder attachment routing, VM false-capability bypass, abandoned reveal warmup cleanup and viewport claim deduplication/rearming, quiet unrelated remote-removal refusal, startup-command one-shot consumption, repository/floating recovery-ledger rows, fake-React pinned-scroll intent, split-ratio state identity/subscriber suppression and fractional change, cross-host project-group join refusal, three PR request-lifetime permutations, upstream-status preservation on error, and unresolved Git-owner refusal. These regression checks are intentionally lost under the test-value audit; retained scenarios are not equivalent replacements. Historical coverage receipts remain historical, including gate35 subscriber claims. Audit 3256 removes these specific alarms from the broad root unit commands: skill dialog active-share selection, close and pending-share handoff; simple keyed-record deletion; marker live-region insertion, simple landing and nine baseline policy examples; General search/control navigation inventory; orphan-count cache bootstrap; seven Codex code classifications and HTTP 429 warning mapping; two skill unread-root verdict examples; peer-mail payload and conversation-ID rejection; and the GitHub organization TABLE text-field optimistic rollback/failed-envelope example. These include real behaviors; retained tests do not provide equivalent coverage for every removed scenario. Mobile losses are recorded separately and are not coverage of these root commands. Audit3246 retires NewRowsMarker reveal/no-repeat and pane-scoped beforeinput checks; skill-owner option availability/count/dedup/order; six styled Input/Textarea IME forwarding rows; the static Codex/OpenCode smart-key inventory; tab-map AST ratchet diagnostic fixtures; child-roster work-budget and retained-aggregate alarms; malformed persisted floating-cwd fallback/no-crash/empty exact-root checks; the unanswered-host empty-list discriminator; the direct invalidated-cache commit-false assertion; one redundant digit-keyup IME row; cached non-supported Claude title-generator rows; and the duplicate late naming-result singleton. These root broad commands lose those specific alarms without equivalent replacement. Distinct retained owner/state checks have their own bounded claims; the real scheduler still rejects stale response data, while the direct false-return alarm is retired. Audit3231 retires supported-host restore-pane key mapping, closed/open creation-sidebar toggle visibility, unrelated scope-error/null classification and quick-submit reveal/no-create checks. These broad commands lose those real narrow alarms; no replacement coverage is claimed. Audit3219 removes the synthetic Cmd+J extra-source suite; this broad command no longer checks its match/tie placement and selection, announced count, empty-query cap, or workspace hiding with retained recent tabs. No replacement coverage is claimed. Audit3209 composes approved sources3202/3206: deliberately retires seven authored/expanded cases in four whole suites without equivalent replacement. Work-item queries lose real GitHub/GitLab oversized ASCII secret-prefix empty-query and whitespace-before-trim refusal alarms; inputs share the imported size bound and exercise only its fast length branch, with no multibyte or actual provider/exposure guarantee. Skill sharing loses complete/partial managed-bundle and destination-ambiguity association checks plus three negative origin/path/scheme admission examples; these do not establish uploaded contents, arbitrary-URL fetching, authorization or execution. Retain exact originless-project direct-link fallback, cloud-delete confirmation, bundled-target repair/owner isolation and explicit Windows-host preference over stale WSL suites. Rejected3201 runtime-preference removal is excluded. No real cloud/network/host/disk, mobile or mixed-version execution validation is claimed. Audit3200 composes approved source3194: deliberately retires thirteen authored registrations/twenty-seven expanded cases in six whole automation host/list policy suites without equivalent replacement. Real RPC category/retryability and wrapped-timeout recognition, unscoped-failure/scoped-success health evidence precedence, exact-ID create selectors and per-run workspace omission, paired-web local/SSH/absent host catalog routing, incompatible/removed/unhydrated/offline execution health, runtime-before-nested-SSH recovery dispatch, and no-owned-focus/successor/foreign-list focus policy alarms are consciously lost. Retain exact20line orphan allow/refuse/precondition and exact90line cross-host same-ID search suites. No retry-timer/fence lifecycle, actual reconnect/network/write delivery, native focus, mobile/mixed-version runtime or disk persistence coverage is claimed. Audit3191 combines approved source3182/3187/3188: deliberately retires thirteen authored Vitest registrations and fifteen expanded cases, including five whole suites, without equivalent replacement. Attachment intake loses real portal/native-capture terminal interception and unowned-container refusal, immediate child-only committed availability/claim-ref negotiation and delivery, and direct-target over stable focused/background composer routing through disable/re-enable/remount and independent question focus. Exact destination/cache/draft and final-stat owner/workspace/File-release suites remain; they do not replace Surface recipient routing. Automation authoring loses destination runtime catalog membership, local/direct-SSH exclusion of paired-runtime repos, unknown-runtime empty catalog, logical project/host/setup/repo/path context payload, and missing/nonready setup refusal; these were genuine direct helper alarms, while exact schedule transformation, legacy schedule and setup-decision suites remain. Agent picker loses setup-chat/terminal/missing-installer/unsupported-or-unknown-host row projection samples and the ordinary unsearched menu variant; the entire original26-line resume recipe suite remains, including reported bypass versus default/absent permission modes, as does the exact search-result menu callback with its fixtures, model/effort commits and staged Arguments reopen outcome. Rejected predecessor3180 full recipe deletion is excluded. No actual native drag/cursor/focus/IME, upload bytes or disk/restart persistence, full automation save/scheduler/runtime transport, real resume-process flags/environment delivery, or native menu/rendered layout validation is claimed. Audit3178 combines source3169/3170/3175: deliberately retires eight authored units (six Vitest cases and two imperative custom relay scenarios), including two whole suites, without equivalent replacement. Voice Section loses per-model pending-delete isolation across interleaved A/B completion and the two-success refresh-call alarm; exact VoicePane delayed key-clear/newer enabled-toggle merge remains and does not replace row deletion coverage. Speech loses dedicated rating-scale/inclusive-ceiling, unsupported-versus-unmeasured, explicit/system-language precedence/locale alias, accuracy versus fast named/default/best-measured selection, and unmeasured/no-match examples; exact offered-user-pick versus retired-selection case and its fixture remain. Relay loses legacy ping-delivery count/liveness sampled before the silence deadline and grouped malformed Bearer/host-ID 401/429 routing checks; twelve real Worker/DurableObject/shipped-client scenarios remain, not equivalent replacement for those narrow alarms. Six prior speech cases are not all deleted: approved successor3175 keeps the original user-choice case. No actual credential/file persistence, native/GPU/engine/download delivery, production assignment policy or deployed-runtime validation is claimed by this reduction. Audit3165 combines source3160/3162: deliberately retires six authored/expanded cases across three whole suites without equivalent replacement. Launch/status losses: observed React-render pending/resuming coordination across settings and deferred offer read; stale-offer reread suppressing dialog reopen; held-local-chat recovery visibility with new-chat preferences off; disabled-local admission avoiding offer RPC despite an injected but unconsumed paired-tab fixture; and run-only lost-reply uncertainty reachability until direct viewed-run retirement. Launch probe does not prove arbitrary synchronous-store atomicity, the paired fixture is not remote runtime enrollment, and the status case directly marks viewed state rather than rendering the summary. Crew-hook losses: old-host method_not_found cache persists across party changes but is released by independently supplied connection generation/verified pairing status or pairing revision, with actual frame publication and teardown. These are real narrow coordination, admission, uncertainty and mixed-version cache alarms consciously lost. Exact web verified-runtime writer handoff guard remains; it does not replace those alarms or prove live host/native/mobile delivery. Audit3156 combines3147/3150/3152/3153/3154: deliberately retires39authored/40expanded native-chat recovery cases, eight whole suites and two orphan test-support files without equivalent replacement. Lost alarms: lost-reply unconfirmed reconciliation, skipped rows/counts and missed-frame repair, unknown/pending outcome and omitted-failure-list compatibility, uncertain history after dismissal, retry/refusal/dismiss lifecycle, hidden/consumed picker ownership, result-tree keyboard focus/disclosure and all-skipped retirement; unconfirmed-over-conflict guidance and failed-chat changes despite older host timestamps; nearest-listed ancestor/cyclic forest shape, descendant membership, host ordering/missing-host local fallback; skipped/readiness/admission counts, provisional skip/progress monotonicity, early per-chat verdicts with batch pending protection, older-host waiting, start-slot/delivery distinction, retry-over-failure and unknown-after-withdrawal attention; remote snooze/reconnect/reoffer, unseen mixed summary through open/close, watched completion retirement, new success beside old failure, failed-reread mixed-result preservation, filter/collapse completion resets, retry summary replacement and current-offer/history selection; cross-host workspace identity, rendered cyclic reachability, nested tri-state/toggle propagation, project eligibility isolation and collapse preserving selection. Exact fallback-list completion barrier, batched verdict/cleanup retention, stale-read versus dismissal publication race, and requested-but-never-rendered finished-run retirement remain; they do not replace retired alarms. Audit3146 combines source reviews3132/3133/3135/3138/3143: intentionally retire20authored/31expanded cases without equivalent replacement. Provider-session losses: structured-native terminal-resume exclusion, metadata-less completed identity, new-turn completion clearing, Pi provider-only/periodic/manual promotion, changed-transcript launch-config isolation, Pi/omp/prime-agent completed-session remote/manual-shutdown promotion, and quit/done precedence over heartbeat. Side-thread/question losses: finished-pane acquisition, hidden title/recap states, local/relay/relay-done conversation takeover with prompt/session/child resets, plain-message abandonment, compaction/session replacement and completion resets. Codex startup losses: incoming-owner cached child replacement, old-relay foreign live-session correction, completed foreign-root turn-fact permutations. Transcript losses: real temporary-JSONL partial sibling/final-child completion, unlabelled child attribution, compact cached replay preservation and100/1000completed-child roster retention/release. Compiler-ratchet losses: valid-line uncompiled-list parser grammar examples and injected-existence stale-path filtering; these did not exercise actual filesystem canonicalization or build exclusion. These are real lifecycle, ownership, replay, liveness and retention alarms, including recent#40246 regressions, deliberately lost. Retained conditional record replacement, clockless root/SSH owner isolation, persisted main-thread fork rejection, partial/final question-answer lifetime, cached-roster/missing-ID/live HTTP/durable startup boundaries and restarted-relay refusal/acceptance do not replace those losses. No native/SSH/WSL execution or equivalent coverage is claimed. Audit3128 scoped coverage reduction: Audit3121 removes VoiceDictationLanguageSetting.test.tsx (Spanish-to-es callback and unsupported-model Korean option presence) and voice-speech-model-rows.test.ts (provider/language-order/retired-on-disk list policy); these real narrow alarms are deliberately retired without replacement. VoiceSpeechModelSection per-model concurrent deletion remains unchanged. Audit3122 removes the direct absolute/parent-segment sparse-directory exception-type sample and the five-row IPC sparse-preset error-code/non-save table (two authored/six expanded cases). Those real typed-validation and IPC refusal/error-code alarms are deliberately retired. Keep both real symlink/junction floating-directory trust cases and the minimal RPC structured validation-reason serialization case. The RPC case does not replace IPC save/refusal behavior, typed normalization for both path inputs, actual Git sparse checkout or rendered localization. No equivalent replacement or fresh test/runtime/platform/remote/performance validation is claimed. Audit3124 removes ExperimentalPane.test.tsx (ordinary Pet switch callback), WorkspaceDirectorySetting.test.tsx (padded absolute saved-path normalization/no-write blur), and repository-search.perf.test.ts (navigation/selected-pane builder-call policy, fixture cardinality/nonempty entries and renamed project keyword inclusion). These real narrow alarms are retired without replacement; RepositoryPane.search.test.tsx search-to-sparse-editor request remains unchanged. Audit3129: resolver Windows case-insensitive and POSIX case-sensitive configured-env composition plus mocked-base launch-preflight Windows composition are intentionally no longer checked by the two deleted suites. No equivalent coverage claimed. Keep runtime-structured-agent-host-env.test.ts unchanged: configured Windows Claude-home account identity and host-account precedence remain checked. Audit3114 incorporates source3101: retire three structured Claude child-environment wrapper cases covering inherited stamp isolation/default and explicit entrypoint, Windows SDK-key canonicalization/default/duplicate-key order, and configured lookup/final environment/config-dir conjunction. Keep direct auth/session sanitation and Windows-versus-Linux overlay checks, actual executable lookup from supplied inherited PATH, native Windows child environment delivery, and stale option-mutation fencing. These are not equivalent replacements for the retired wrapper alarms. No fresh test/runtime, real login-shell probe, Claude SDK startup, remote/mixed-version, UI or performance result is claimed. Audit3114 incorporates source3105: retire 17 authored/20 expanded daemon launch-note cases, including the entire two-case SessionLaunchNote suite. Deliberately lose split-marker carry across same-token re-arm, stale-token retirement refusal, foreground tri-state and serial settlement, omitted-note/no-incarnation mixed-version policy, sticky retirement, first-incarnation/replacement launch preservation and held-launch precedence. Keep only the unchanged unresolved foreground-read race against a later finished attach. Supplied notes/foreground and runtime calls do not establish actual daemon/main restart, SSH, native Windows observation, mobile UI, equivalent replacement or fresh runtime/performance validation. Audit3114 incorporates source3109: retire six authored/35 expanded pricing cases: 21 accepted model-name examples, 10 unsupported/future-model fail-closed examples, malformed TTL clamp, unknown-model estimate refusal, flat-model long-context disregard and oversized long-context clamp. Keep mixed five-minute/one-hour write allocation and short/all-long/mixed long-context allocation with original fixed oracles. Real parser/refusal/bounds alarms are knowingly lost without equivalent replacement; retained tests do not validate provider rates, actual billing or request threshold classification. Preserve the archived usage-scan-append source-freeze receipt without transferring historical execution/performance credit. No fresh runtime result is claimed. Audit3108 source-only reduction (3094/3095/3100): retire 27 authored/30 expanded cases, including complete markdown view-state, file-preview link-open and CLI agent-server-route suites, without replacement. Deliberate losses include preview visibility/reference lifetime and same-path owners; SSH stat, directory/missing-target, explicit-null owner, nonmarkdown line/column, local URI routing, remote-to-client guard and same-file reuse; document-preview source-owner/folder/reuse routing, stable grant location and outside-workspace POSIX/Windows request projection; and verified remote CLI send/check refusal, suggested --on argv/copy, shell no-probe bypass, unverified-launch admission and older-peer refusal. Retain two markdown source-runtime/qualified-reveal alarms, one stale grant-rejection fence and two remote read/mutation evidence policies, with original callbacks. Final last-wins reveal did not prove transient cancellation; CLI prerequisite did not prove no real send, and envelope tests do not execute server refusal. No fresh runtime, rendered UI, actual file/SSH/paired transport, authorization, mixed-version or performance result is claimed. Audit3096 source-only reduction (3087/3088): retire 69 authored/76 expanded cases, including complete recovery-portals and reconnect-banner suites. Intentionally lose dead/default TUI replay, generated alternate-frame style composition, reset/mode/dimension and legacy missing-mode-boundary alarms, UI notice/action/reveal policies, and isolated recovery lifecycle variants, without equivalent replacement. Retained registrations and callbacks are unchanged; headless cells are not pixels, and no fresh runtime, host, SSH, WSL, rendered-app or performance result is claimed. Audit3086 source-only reduction (3079/3080/3081): intentionally retire 93 authored/100 expanded mock-boundary cases, including the complete spawn-errors and SSH reconnect overlay suites, without replacement. Retired alarms include spawn classification/ownership, reply ceilings and renderer-write failure recovery, disconnect/detach fencing branches, authority facts, and overlay retry/visibility wiring. Retained callbacks are unchanged; no fresh execution, real PTY/SSH, rendered UI, or performance result is claimed. Audit2432: The broad unit command loses the isolated BranchPicker open-change notification sample. Its unique true/false callback alarm is intentionally retired; reopening/status-refresh behavior was not exercised. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2425: The broad unit command loses the workspace-copy-status supplied readError forwarding sample. It did not exercise the branch-switch blocking promised by its title. This alarm is intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2417: The broad unit command loses two ordinary clean Git cherry-pick/revert success samples and two terminal startup foreground-read work-budget samples. These unique alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2412: The broad unit command loses the whole six-case fake-React-hook scroll-anchor suite, two generic draft-retention samples, a simple new-agent order sample, a real history-publication batching-count sample, an unmounted handoff abort/timer sample and three basic WSL preview/error samples. These alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2404: The broad unit command loses the standalone share-name lookup 503 propagation suite, initial deletion placement-kind sample, direct changed-mtime stale sample, first-pass rollback existence example and abandoned-preparation directory sample. These exact alarms are intentionally retired without equivalent replacement or fresh validation of the reduced selector. Audit2397 review amendment: The broad unit command loses three standalone suites, two partial-case definitions and two URL table rows across package/download/placement tests. Keep initial wrong-port authorization, modified independent-copy digest refusal and abort-after-begin cancellation dispatch. The remaining alarms are deliberately retired; no equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2393 current-parent refresh2401: Remove the basic skill-install service alias sample and original public managed-remove receiptless refusal case. Preserve the upstream request ownership suite and transaction interruption coverage. No equivalent complete replacement or fresh validation of the reduced selector is claimed. Audit2389: The broad unit command loses the missing-preview suite, plain-global destination sample and prepublished-receipt recovery example. No equivalent replacement or fresh validation of the reduced selector is claimed. Audit2385: The broad unit command loses four authored walker/staging/protocol examples (six expanded cases); no equivalent replacement or fresh validation is claimed for the reduced suite. Audit2383: The unscoped Vitest command no longer includes the removed Antigravity catalogue suite; its exact provider-root/owner example has no equivalent replacement or fresh validation claim. Audit2377/2381: The unscoped Vitest command includes the reduced bundled-skill and native-discovery suites; the eight removed backup/legacy/catalogue/symlink/SSH-builder examples have no replacement-equivalence or fresh validation claim here. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2056 (sources2054/2055): Intentionally retire these low-value alarms without replacement: src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts: delivers remote file changes to listeners for the same worktree. src/main/runtime/orca-runtime.test.ts: ordinary registration cleanup removes only await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec'). src/main/runtime/orca-runtime-tests/terminal-side-effect-facts-part-03.spec.ts: arms the stale-title timer for a seeded working title; arms the Command Code scrape from the noted spawn command; prefers the tracked title over a stale renderer lastTitle in the hydration seed; writes recent output into the current runtime output map; uses a later title handler replacement with the runtime receiver; calls the current injected listener with the runtime receiver; keeps optional reconciliation lazy after its callback is removed. src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-14.spec.ts: lists them, so a paired client can mirror a migrated editor; closes one by retiring it from the host session; keeps an unsaved draft unless the close is forced; shares aggregate work but leaves a replacement pending promise alone; lets a targeted refresh finish while an aggregate refresh waits; refuses a close when the store lacks %s before the guard; keeps a later missing %s on the original caught failure path. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Exact retired assertionRefs, preserved as historical objects only: {\n \"file\": \"src/renderer/src/runtime/files/watching/runtime-file-client-watch.test.ts\",\n \"assertions\": [\n \"terminal watcher error and setup failure evict the shared entry before retrying callbacks run\",\n \"an old stream end cannot evict the replacement stream, which still delivers to its listener\",\n \"two same-worktree listeners share a synthetic subscription until the final stop unsubscribes and sends unwatch\"\n ]\n } Historical prior account (preserved verbatim): Audit1973: Delete the standalone mocked watcher cleanup suite and its entire sole-file prospective command. Retire the removed file-command transport-abort alias; preserve the independent cancellation suite and final-overflow/error/end cleanup callback, and13 unrelated receipt objects. Whole original changed dated receipts are retired, never narrowed or transferred. This metadata review claims no fresh execution, equivalent replacement, provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result. Historical prior account (preserved verbatim): Audit1931 (sources1928/1929): Partial runtime files-watch suite retains controlled Windows close-event waiting, shared-client survival, late physical-exit ownership/deadline cleanup and shutdown joining. Its combined Windows close/deadline/late-exit alias remains backed by unchanged callbacks. Retire native-error-close, pre-publication setup ownership and failed-delete rearm aliases; associated delegation, failure forwarding, removal routing and recovery examples are lost. These tests inject watch/subscription seams and do not observe an actual native watcher or physical process. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1925 (sources1922/1923): Partial runtime watcher-host suite loses mocked option/event/overflow/resubscription forwarding, supplied teardown-error propagation, aborted-signal/no-call and private ten-thousand-waiter-count inventories. Retire the 10,000-caller bound and failed-recovery-subscriber-ending assertion aliases. Keep same-root owner cancellation, pre-ready replacement, held root-close join, stale-generation recovery delivery and initial failed-root ownership mappings backed by retained real-host callbacks with injected subscription/error/physical-exit promises. No actual native watcher, process termination or resource bound was observed. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Audit1683 retires changed-source assertion and execution scope without equivalent replacement coverage. Existing unchanged-selector receipts remain scoped to their original sources. Historical prior-source oracle: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Fill the global watcher-child reservation, require native desktop, paired-runtime, and WSL setup to retain typed transient capacity and retry after one serialized release, then cancel pending desktop and paired-client watches and require no retry. Hold one live anchor while aborting 10,000 callers at the capacity, quarantine-assignment, runtime-root, relay-setup, and SSH-registration layers; require exactly one explicit waiter and one base-promise reaction to remain, then zero after settlement. Hold an old relay unsubscribe, queue one same-root setup leader and one anchor, then abort 10,000 joiners; require every cancellation to settle before teardown releases, exactly one anchor waiter, and only one later physical install. Advance the former paired-web callback deadline while capacity remains full, require cancellation ownership to stay live, publish the server token before ready, and require files.unwatch to abort setup before a later slot release can install a native root. Reject the first ready-watch unwatch with a physical-exit promise, resolve that exit later, and require a fresh physical unwatch attempt, registry release, and exactly one stream end. Fill one healthy shared child with multiple roots, map a standard SSH repository's base, Git common directory, and worktree roots to one relay supervisor, then inject IPC disconnect-before-exit, queued and active cancellation, operations during termination, kill-without-exit, repeated unkillable generations, initial and recovery timeouts, and shard failure. During local and SSH deletion, attempt a late terminal spawn whose worktree identity and cwd name different roots, enumerate a non-prefix remote PTY by authoritative worktree ownership, withhold daemon/relay exit callbacks, make attach positively prove a concurrent-shutdown PID dead, and make the provider unavailable; require every acquired admission to roll back on rejection, dead-PID proof to settle physical exit before reaping, timeout to retain ownership, and Git/filesystem deletion never to start. Require bounded watcher process count, physical-exit settlement, healthy-root recovery, stale-generation rejection, overflow plus later events, and fail-closed destructive cleanup. Verify relay build hashes and remote install probes require both relay.js and relay-watcher.js. Launch the built relay with a real PTY and Parcel watch, kill only the watcher child, and require the same relay PID and PTY to answer before a replacement child delivers overflow and a post-crash event.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/shared/promise-settlement-waiters.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/watchers/filesystem-watcher.test.ts src/main/ipc/filesystem/watcher-process/child-lifecycle/parcel-watcher-child-registry.test.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-process.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-supervisor-capacity.test.ts src/main/ipc/filesystem/watcher-process/supervision/parcel-watcher-disconnect-termination.test.ts src/main/ipc/filesystem/watcher-process/event-delivery/parcel-watcher-event-delivery.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-local-unsubscribe.test.ts src/main/ipc/filesystem/watchers/filesystem-watcher-wsl.test.ts src/main/ipc/worktrees-orphan-directory-cleanup.test.ts src/main/ipc/worktrees-remove-preflight.test.ts src/main/workspaces/removal/watcher-removal-gate.test.ts src/main/runtime/files/watch/orca-runtime-files-watch.test.ts src/main/runtime/workspaces/removal/worktree-teardown.test.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/methods/files/files.test.ts src/main/runtime/rpc/methods/files/files-watch-cancellation.test.ts src/renderer/src/web/web-runtime-client.test.ts src/shared/cross-platform-path.test.ts src/shared/physical-exit-tracker.test.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts src/main/providers/local-pty/local-pty-provider-shutdown.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts src/main/daemon/terminal-host/testing/terminal-host.test.ts src/main/daemon/terminal-host/testing/terminal-host-session-reaping-leak.test.ts --reporter=dot", diff --git a/src/main/runtime/workspaces/removal/structured-session-worktree-teardown.test.ts b/src/main/runtime/workspaces/removal/structured-session-worktree-teardown.test.ts index 51fc3bece97..108314d6295 100644 --- a/src/main/runtime/workspaces/removal/structured-session-worktree-teardown.test.ts +++ b/src/main/runtime/workspaces/removal/structured-session-worktree-teardown.test.ts @@ -12,11 +12,8 @@ vi.mock('../../../native-chat/agent-session-wire/host/structured-agent-session-r const { killAllProcessesForWorktree } = await import('./worktree-teardown') type TeardownRuntime = NonNullable[1]['runtime']> -const { - classifyWorktreeForceDeleteReason, - isProvenLiveStructuredSessionRemovalError, - isUnstoppedPtyRemovalError -} = await import('../../../../shared/worktree/removal') +const { classifyWorktreeForceDeleteReason, isUnstoppedPtyRemovalError } = + await import('../../../../shared/worktree/removal') await import('./structured-session-worktree-teardown') const WORKTREE = 'repo_1::/tmp/wt-a' @@ -187,81 +184,6 @@ describe('worktree teardown and structured agent sessions', () => { hostRef.current = null }) - it('puts the chat tab back when the removal refuses over the session', async () => { - // A refused removal preserves the workspace and must restore its chat tabs. - const host = installHost({ - records: [record('s1', WORKTREE)], - stuck: new Set(['s1']), - visible: ['s1'] - }) - await expect(killAllProcessesForWorktree(WORKTREE, destructiveDeps())).rejects.toThrow( - /still live: 1 agent session \(claude\)/ - ) - expect([...host.visible]).toEqual(['s1']) - }) - - it('still removes under force when a close does not settle, and says so', async () => { - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - const retired: string[] = [] - installHost({ records: [record('s1', WORKTREE)], stuck: new Set(['s1']), visible: ['s1'] }) - const result = await killAllProcessesForWorktree(WORKTREE, { - ...destructiveDeps({ allowUnverifiedStop: true }), - runtime: runtimeDouble({ - retireStructuredAgentSessionTabFromSnapshot: (sessionId: string) => { - retired.push(sessionId) - return true - } - }) - }) - expect(result.structuredStopped).toBeUndefined() - // The live arm of that record, carrying the verdict the refusal would have shown. - expect(structuredSessionWarning(warn)).toContain('still live: 1 agent session (claude)') - expect(retired).toEqual(['s1']) - warn.mockRestore() - }) - - it('takes the proof when a failed close is re-observed as exited', async () => { - // Later exit evidence can prove a child gone after close threw or restored its tab. - const retired: string[] = [] - const runtime: TeardownRuntime = { - ...presentationRuntime(), - getPtyLivenessVerdict: () => null, - stopTerminalsForWorktree: async () => ({ stopped: 0 }), - retireStructuredAgentSessionTabFromSnapshot: (sessionId: string) => { - if (!retired.includes(sessionId)) { - retired.push(sessionId) - } - return true - } - } - installHost({ records: [record('s1', WORKTREE)], settledThenThrows: new Set(['s1']) }) - await expect( - killAllProcessesForWorktree(WORKTREE, { ...destructiveDeps(), runtime }) - ).resolves.toMatchObject({ structuredStopped: 1 }) - // Retired here because the close gave up before its own retirement step, and a chat tab left - // behind re-attaches a released session pointing at a workspace that is about to be deleted. - expect(retired).toEqual(['s1']) - }) - - it('names the unconfirmed sessions too, instead of counting only the live ones', async () => { - // The PTY sibling may drop everything outside its live list because a fresh inventory PROVED - // those exited. Nothing proves that here: an `unverifiable` session is unclosed as well, so - // naming only the live subset told the user "1 agent session" while two were about to go. - installHost({ - records: [record('s1', WORKTREE), record('s2', WORKTREE, { provider: 'codex' })], - stuck: new Set(['s1']), - unverifiable: new Set(['s2']) - }) - const error = await killAllProcessesForWorktree(WORKTREE, destructiveDeps()).catch( - (thrown: Error) => thrown.message - ) - expect(error).toContain( - 'still live: 1 agent session (claude); could not confirm these closed: 1 agent session (codex)' - ) - // The marker still leads, so the toast keeps showing the stronger of the two warnings. - expect(isProvenLiveStructuredSessionRemovalError(error as string)).toBe(true) - }) - it('refuses in agent-session wording when the close outlives the sweep budget', async () => { vi.useFakeTimers({ toFake: ['Date', 'setTimeout', 'clearTimeout'] }) try { @@ -303,32 +225,6 @@ describe('worktree teardown and structured agent sessions', () => { } }) - it('stops issuing new closes once the budget is spent', async () => { - // Stop issuing closes at deadline; the uncancellable in-flight close and untouched tail both remain unverified. - let releaseFirstClose: () => void = () => {} - const firstClose = new Promise((resolve) => { - releaseFirstClose = resolve - }) - const host = installHost({ - records: [record('s1', WORKTREE), record('s2', WORKTREE)], - closeGates: { s1: firstClose } - }) - vi.useFakeTimers() - try { - const outcome = killAllProcessesForWorktree( - WORKTREE, - destructiveDeps({ timeoutMs: 5 }) - ).catch((thrown: Error) => thrown.message) - await vi.advanceTimersByTimeAsync(5) - expect(await outcome).toContain('could not confirm these closed: 2 agent sessions (claude)') - releaseFirstClose() - await vi.advanceTimersByTimeAsync(0) - expect(host.closed).toEqual(['s1']) - } finally { - vi.useRealTimers() - } - }) - it('keeps every chat tab when the removal refuses over a session it could not close', async () => { // Refusal must preserve both the stuck chat and its detached workspace sibling. const host = installHost({ diff --git a/src/main/runtime/workspaces/removal/worktree-teardown-unstopped-pty.test.ts b/src/main/runtime/workspaces/removal/worktree-teardown-unstopped-pty.test.ts index dd09ed2d1d6..4db53a2aaee 100644 --- a/src/main/runtime/workspaces/removal/worktree-teardown-unstopped-pty.test.ts +++ b/src/main/runtime/workspaces/removal/worktree-teardown-unstopped-pty.test.ts @@ -10,10 +10,6 @@ vi.mock('../../../memory/pty-registry', () => ({ import { killAllProcessesForWorktree } from './worktree-teardown' import { ABANDONED_SWEEP_GRACE_MS } from './forced-sweep-settlement' -import { - classifyWorktreeForceDeleteReason, - isProvenLivePtyRemovalError -} from '../../../../shared/worktree/removal' import type { IPtyProvider } from '../../../providers/pty/provider-contract' import type { PtyProcessInfo } from '../../../providers/pty/process-info' @@ -72,57 +68,7 @@ describe('destructive teardown when a PTY stop cannot be proven', () => { } }) - it('names the blocking PTYs and the escape hatch when one is still live', async () => { - const localProvider = createProviderStub(async () => [ - { id: 'w1@@live-1', cwd: '/tmp/w1', title: 'shell' } - ]) - ;(localProvider.shutdown as unknown as ReturnType).mockRejectedValue( - new Error('kill failed') - ) - listRegisteredPtysMock.mockReturnValue([]) - - await expect( - killAllProcessesForWorktree('w1', { localProvider, requirePhysicalStop: true }) - ).rejects.toThrow(/still live: w1@@live-1[\s\S]*--force/) - }) - // A different provider's empty list cannot prove an unreachable SSH host's PTY exited. - it("blocks removal when the stop lost contact with the PTY's own host", async () => { - // Retain the owning-host verdict; local absence cannot prove remote exit. - const localProvider = createProviderStub(async () => []) - listRegisteredPtysMock.mockReturnValue([]) - const runtime = { - stopTerminalsForWorktree: async ( - _worktreeId: string, - opts: { - stopPty?: ( - ptyId: string, - stop: () => boolean | Promise - ) => Promise<{ stopped: boolean; owner: boolean }> - } - ) => { - await opts.stopPty?.('ssh:conn-1@@relay-9', () => false) - return { stopped: 0 } - }, - getPtyLivenessVerdict: (ptyId: string) => - ptyId === 'ssh:conn-1@@relay-9' - ? { status: 'unverifiable', reason: 'its SSH provider is no longer registered' } - : null - } - - await expect( - killAllProcessesForWorktree('w1', { - runtime: runtime as never, - localProvider, - resolvedConnectionId: 'conn-1', - includeProviderInventory: false, - includeLocalRegistry: false, - requirePhysicalStop: true - }) - ).rejects.toThrow( - /could not verify[\s\S]*ssh:conn-1@@relay-9[\s\S]*SSH provider is no longer registered[\s\S]*--force/ - ) - }) it('does not infer a remote exit from fallback local inventory without a cached verdict', async () => { const localProvider = createProviderStub(async () => []) @@ -156,47 +102,7 @@ describe('destructive teardown when a PTY stop cannot be proven', () => { expect(localProvider.listProcesses).not.toHaveBeenCalled() }) - it('reports unverifiable separately from live when the process list fails', async () => { - const localProvider = createProviderStub(async () => { - throw new Error('daemon socket closed') - }) - listRegisteredPtysMock.mockReturnValue([ - { ptyId: 'stale-1', worktreeId: 'w1', sessionId: null, paneKey: null, pid: 100 } - ]) - ;(localProvider.shutdown as unknown as ReturnType).mockRejectedValue( - new Error('Session not found: stale-1') - ) - - await expect( - killAllProcessesForWorktree('w1', { - localProvider, - includeProviderInventory: false, - requirePhysicalStop: true - }) - ).rejects.toThrow(/could not verify[\s\S]*stale-1[\s\S]*daemon socket closed/) - }) - // Force must also cross sweep failures before the unproven-stop gate. - it('lets force through a provider whose inventory rejects outright', async () => { - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - try { - listRegisteredPtysMock.mockReturnValue([]) - const localProvider = createProviderStub(async () => { - throw new Error('ssh channel closed') - }) - - await expect( - killAllProcessesForWorktree('w1', { - localProvider, - requirePhysicalStop: true, - allowUnverifiedStop: true - }) - ).resolves.toEqual({ runtimeStopped: 0, providerStopped: 0, registryStopped: 0 }) - expect(warn).toHaveBeenCalledWith(expect.stringContaining('ssh channel closed')) - } finally { - warn.mockRestore() - } - }) it('lets force through a sweep that never settles before the deadline', async () => { const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) @@ -242,51 +148,6 @@ describe('destructive teardown when a PTY stop cannot be proven', () => { // Force must join active shutdowns before filesystem deletion or Windows can fail with EBUSY. // Wrap raw provider failures in recognizable force-removal wording without losing their cause (#11960). - it('offers force delete for a sweep-level failure without losing the provider wording', async () => { - listRegisteredPtysMock.mockReturnValue([]) - const localProvider = createProviderStub(async () => { - throw new Error('SSH channel closed while listing processes') - }) - - const error = await killAllProcessesForWorktree('repo-1::/w', { - localProvider, - requirePhysicalStop: true - }).then( - () => new Error('expected a rejection'), - (rejection: Error) => rejection - ) - expect(error.message).toContain('SSH channel closed while listing processes') - expect(classifyWorktreeForceDeleteReason(error.message)).toBe('unstopped-pty') - expect(isProvenLivePtyRemovalError(error.message)).toBe(false) - }) // Timeout does not cancel shutdown; force must await it before deletion to avoid partial Windows removal. - - it('lets an explicit force removal proceed past PTYs it could not stop', async () => { - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - try { - const localProvider = createProviderStub(async () => [ - { id: 'w1@@live-1', cwd: '/tmp/w1', title: 'shell' } - ]) - ;(localProvider.shutdown as unknown as ReturnType).mockRejectedValue( - new Error('kill failed') - ) - listRegisteredPtysMock.mockReturnValue([]) - const onPtyStopped = vi.fn() - - await expect( - killAllProcessesForWorktree('w1', { - localProvider, - onPtyStopped, - requirePhysicalStop: true, - allowUnverifiedStop: true - }) - ).resolves.toMatchObject({ providerStopped: 0 }) - expect(warn).toHaveBeenCalledWith(expect.stringContaining('w1@@live-1')) - // Keep proven-live PTYs discoverable for users and later sweeps. - expect(onPtyStopped).not.toHaveBeenCalled() - } finally { - warn.mockRestore() - } - }) }) diff --git a/src/main/runtime/workspaces/removal/worktree-teardown.test.ts b/src/main/runtime/workspaces/removal/worktree-teardown.test.ts index fd4e9aed298..40e85c75ee4 100644 --- a/src/main/runtime/workspaces/removal/worktree-teardown.test.ts +++ b/src/main/runtime/workspaces/removal/worktree-teardown.test.ts @@ -180,39 +180,4 @@ describe('killAllProcessesForWorktree', () => { }) // Bound Windows daemon kill RPCs below the sweep deadline so failures report the stop cause (#9500). - - it('tolerates an unresolved runtime selector during destructive removal', async () => { - // An absent newly created/removed graph entry means no runtime-owned PTYs, not failed teardown. - const stopTerminalsForWorktree = vi.fn().mockRejectedValue(new Error('selector_not_found')) - const runtime = { - stopTerminalsForWorktree - } as unknown as Parameters[1]['runtime'] - const localProvider = createProviderStub(async () => []) - listRegisteredPtysMock.mockReturnValue([]) - - const result = await killAllProcessesForWorktree('w1', { - runtime, - localProvider, - requirePhysicalStop: true - }) - - expect(result.runtimeStopped).toBe(0) - }) - - it('fails destructive teardown closed when the runtime sweep rejects', async () => { - const stopTerminalsForWorktree = vi.fn().mockRejectedValue(new Error('runtime sweep failed')) - const runtime = { - stopTerminalsForWorktree - } as unknown as Parameters[1]['runtime'] - const localProvider = createProviderStub(async () => []) - listRegisteredPtysMock.mockReturnValue([]) - - await expect( - killAllProcessesForWorktree('w1', { - runtime, - localProvider, - requirePhysicalStop: true - }) - ).rejects.toThrow('runtime sweep failed') - }) }) From 1f4ab5b6316919ae0095345e9b48b472bbd54d62 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 22:46:06 -0800 Subject: [PATCH 026/178] test: remove synthetic mailbox and admission pipeline cases (#41863) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- ...creation-and-orchestration-part-03.spec.ts | 116 +----------------- ...session-tabs-targeted-agent-resume.spec.ts | 72 +---------- 2 files changed, 2 insertions(+), 186 deletions(-) diff --git a/src/main/runtime/orca-runtime-tests/mobile-creation-and-orchestration-part-03.spec.ts b/src/main/runtime/orca-runtime-tests/mobile-creation-and-orchestration-part-03.spec.ts index 7456f981a34..de8ebeff271 100644 --- a/src/main/runtime/orca-runtime-tests/mobile-creation-and-orchestration-part-03.spec.ts +++ b/src/main/runtime/orca-runtime-tests/mobile-creation-and-orchestration-part-03.spec.ts @@ -1,124 +1,10 @@ -import { settledWriteStub } from '../../providers/testing/settled-pty-write-stub' import { describe, expect, it, vi } from 'vitest' import './runtime-test-module-registration.spec' import { OrcaRuntimeService } from '../orca-runtime' -import { - bindSinglePtyRun, - setInMemoryOrchestrationMessages, - store, - syncSinglePty -} from './orca-runtime-test-fixtures.spec' -import { InMemoryOrchestrationMessages } from '../orchestration/mailbox/terminal-pointer/in-memory-orchestration-messages.spec' -import { MessageWake } from '../orchestration/mailbox/runtime-message-waiters' +import { store } from './orca-runtime-test-fixtures.spec' describe('OrcaRuntimeService', () => { - it('leaves rows a live filtered waiter reserved out of the pushed batch', async () => { - vi.useFakeTimers() - try { - const runtime = new OrcaRuntimeService(store) - const db = new InMemoryOrchestrationMessages() - const write = vi.fn().mockReturnValue(true) - setInMemoryOrchestrationMessages(runtime, db) - runtime.setPtyController({ - write, - writeWithSettlement: settledWriteStub(write), - kill: vi.fn(), - getForegroundProcess: async () => null - }) - syncSinglePty(runtime) - - const [terminal] = (await runtime.listTerminals()).terminals - const mailbox = bindSinglePtyRun(db, terminal.handle) - runtime.onPtyData('pty-1', '\x1b]0;Codex working\x07', 100) - runtime.onPtyData('pty-1', '\x1b]0;Codex done\x07', 101) - await runtime.waitForTerminal(terminal.handle, { condition: 'tui-idle' }) - write.mockClear() - - // Push scans all pending rows; exclude worker_done owned by the blocked check or it delivers twice. - const waitPromise = runtime.waitForMessage(mailbox, { - typeFilter: ['worker_done'], - timeoutMs: 5_000 - }) - const status = db.insertMessage({ - from: 'sender', - to: terminal.handle, - subject: 'unclaimed status', - type: 'status' - }) - runtime.notifyMessageArrived(terminal.handle, 'status') - const done = db.insertMessage({ - from: 'sender', - to: terminal.handle, - subject: 'reserved completion', - type: 'worker_done' - }) - runtime.notifyMessageArrived(terminal.handle, 'worker_done') - - await expect(waitPromise).resolves.toBeInstanceOf(MessageWake) - await vi.advanceTimersByTimeAsync(600) - const payloads = write.mock.calls - .map(([, data]) => data) - .filter((data): data is string => typeof data === 'string') - expect(payloads).toContain( - '\nYou have 1 orchestration message. Run `orca-dev orchestration check --run run_test`.\n' - ) - expect(payloads.some((data) => data.includes('reserved completion'))).toBe(false) - expect(status.delivered_at).toEqual(expect.any(String)) - expect(done.delivered_at).toBeNull() - db.close() - } finally { - vi.useRealTimers() - } - }) - - it('skips rows claimed by a waiter that registered after the notify', async () => { - vi.useFakeTimers() - try { - const runtime = new OrcaRuntimeService(store) - const db = new InMemoryOrchestrationMessages() - const write = vi.fn().mockReturnValue(true) - setInMemoryOrchestrationMessages(runtime, db) - runtime.setPtyController({ - write, - writeWithSettlement: settledWriteStub(write), - kill: vi.fn(), - getForegroundProcess: async () => null - }) - syncSinglePty(runtime) - - const [terminal] = (await runtime.listTerminals()).terminals - const mailbox = bindSinglePtyRun(db, terminal.handle) - runtime.onPtyData('pty-1', '\x1b]0;Codex working\x07', 100) - runtime.onPtyData('pty-1', '\x1b]0;Codex done\x07', 101) - await runtime.waitForTerminal(terminal.handle, { condition: 'tui-idle' }) - write.mockClear() - - const message = db.insertMessage({ - from: 'sender', - to: terminal.handle, - subject: 'claimed late', - type: 'status' - }) - // A waiter arriving after notify still owns rows before deferred push; consult live waiters at push time. - runtime.notifyMessageArrived(terminal.handle, 'status') - const waitPromise = runtime.waitForMessage(mailbox, { - typeFilter: ['status'], - timeoutMs: 5_000 - }) - await Promise.resolve() - - expect(write).not.toHaveBeenCalled() - expect(message.delivered_at).toBeNull() - - await vi.advanceTimersByTimeAsync(5_000) - await expect(waitPromise).resolves.toBe('timed_out') - db.close() - } finally { - vi.useRealTimers() - } - }) - it('rejects leaf PTY waits when the request signal aborts', async () => { vi.useFakeTimers() try { diff --git a/src/main/runtime/orca-runtime-tests/mobile-session-tabs-targeted-agent-resume.spec.ts b/src/main/runtime/orca-runtime-tests/mobile-session-tabs-targeted-agent-resume.spec.ts index 03572fefd51..3f8ef42d770 100644 --- a/src/main/runtime/orca-runtime-tests/mobile-session-tabs-targeted-agent-resume.spec.ts +++ b/src/main/runtime/orca-runtime-tests/mobile-session-tabs-targeted-agent-resume.spec.ts @@ -19,16 +19,7 @@ import type { PtyProcessInfo } from '../../providers/pty/process-info' import { deterministicAgentSessionUuid } from '../terminal/launch/runtime-agent-launch-resolution' import { deriveRemoteRuntimeTerminalCreateHandle } from '../terminal/identity/remote-runtime-terminal-create-identity' import type { RuntimePtyController } from '../terminal/lifecycle/runtime-pty-controller-contract' -import { agentSessionConversationView } from '../../../shared/agent-session/lifecycle/agent-session-conversation-view' -import { AgentSessionRecordStore } from '../agent-session/records/agent-session-record-store' -import { - agentSessionLeaseFixture, - agentSessionRecordFixture -} from '../../../shared/agent-session/lifecycle/agent-session-record.test-fixture' -import { - chooseSavedResumeFreshLaunch, - savedResumeResult -} from '../../ipc/pty/host-env/saved-resume-admission' +import { savedResumeResult } from '../../ipc/pty/host-env/saved-resume-admission' import { spawnedCommands, resumeSpawns } from './agent-resume-spawn-commands.test-fixture' const PANE_KEY = `host-tab:${HEADLESS_LEAF_ID}` @@ -131,67 +122,6 @@ function storedRecord(fixture: Fixture) { } describe('resumeAgentSessionInWorkspace', () => { - it.each(['live', 'worktree-sleep'] as const)( - 'releases a %s saved resume claim when actual admission creates a plain shell', - async (origin) => { - const fixture = makeSleptWorkspace() - const saved = storedRecord(fixture) - if (!saved) { - throw new Error('missing saved conversation') - } - saved.origin = origin - const chat = agentSessionRecordFixture( - agentSessionLeaseFixture({ - claimStatus: 'released', - ownerProcess: null, - reservedSpawnToken: null, - deathEvidence: { kind: 'exit-observed', detail: 'closed', observedAt: 1 } - }) - ) - chat.providerHandleChain[0]!.handle.nativeId = SESSION.id - let nativeOpen = true - const store: AgentSessionRecordStore = Object.create(AgentSessionRecordStore.prototype) - store.listConversations = () => [agentSessionConversationView(chat)] - store.getVisibleSessionTabIndex = () => ({ - present: true, - sessionIds: nativeOpen ? [chat.sessionId] : [] - }) - const effectiveCommands: (string | undefined)[] = [] - const { runtime, spawn, openOldPane, resume } = makeRuntime(fixture, { - spawn: async (args) => { - const admission = await chooseSavedResumeFreshLaunch( - { openAgentSessionRecordStore: async () => store }, - args, - null, - args - ) - effectiveCommands.push(args.command) - return { - id: effectiveCommands.length === 1 ? 'refused-shell' : 'resumed-C', - launchAgent: args.launchAgent, - launchConfig: args.launchConfig, - ...savedResumeResult(admission) - } - } - }) - await openOldPane() - expect(runtime.getTerminalLaunchResult('refused-shell')).toMatchObject({ - agentResumeRefused: 'chat-home', - savedResumeDecision: { agent: 'claude', providerSession: SESSION, verdict: 'chat-home' } - }) - expect(runtime.getTerminalLaunchResult('refused-shell')?.launchAgent).toBeUndefined() - expect(effectiveCommands).toEqual([undefined]) - nativeOpen = false - await expect(resume({ mutationId: 'after-native-close' })).resolves.toMatchObject({ - ok: true, - ptyId: 'resumed-C' - }) - expect(effectiveCommands[1]).toContain(SESSION.id) - expect(spawn).toHaveBeenCalledTimes(2) - expect(runtime.getTerminalLaunchResult('refused-shell')).toBeDefined() - } - ) - it('settles only fresh matching saved results and binds unproven or observed results until exit', async () => { for (const kind of [ 'matching', From d2e2c788a5463b53f165d006762b6ff188d48204 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 22:49:12 -0800 Subject: [PATCH 027/178] refactor: organize WSL guest modules by concrete ownership (#41852) * refactor: organize WSL guest modules by concrete ownership * chore: link historical relay evidence to the moved command * chore: prune retired guest test timing debt --- config/flaky-test-pattern-baseline.txt | 11 +- .../process-host-direct-import-baseline.txt | 2 +- config/reliability-gates.jsonc | 23 +-- .../scripts/ci/sharding/ci-shard-timings.json | 89 +++++----- .../relay-json-payload-benchmark.ts | 5 +- .../installation/opencode-plugin-settings.ts | 2 +- .../http/server-startup-lifecycle.test.ts | 2 +- .../server-hermes-resume-identity.test.ts | 2 +- .../server-hook-session-binding.test.ts | 4 +- ...server-opencode-writer-observation.test.ts | 2 +- .../server-claude-helper-kill.test.ts | 2 +- .../server-claude-helper-recovery.test.ts | 2 +- .../server-relayed-claude-cancel.test.ts | 2 +- .../server-hook-root-provenance.test.ts | 4 +- .../codex/server-codex-session-start.test.ts | 4 +- ...ver-ingest-remote-codex-transcript.test.ts | 2 +- ...r-relayed-claude-permission-replay.test.ts | 2 +- ...erver-claude-task-wakeup-lifecycle.test.ts | 2 +- .../wsl-relay/wsl-guest-plugin-install.ts | 2 +- .../wsl-relay/wsl-hook-relay-deps.ts | 2 +- .../wsl-relay/wsl-hook-relay-guest-install.ts | 2 +- .../wsl-relay/wsl-hook-relay-manager.test.ts | 4 +- .../wsl-opencode-settings-lifetime.test.ts | 6 +- ...k-result-retry-scheduler-muse-poll.test.ts | 98 ----------- .../presence}/relay-agent-presence.test.ts | 14 +- .../presence}/relay-agent-presence.ts | 8 +- .../relay-hook-root-acquisition.test.ts | 14 +- .../relay-http-owner-probe-lifecycle.test.ts | 10 +- .../relay-idle-process-owner.test.ts | 10 +- .../relay-nested-same-provider-owner.test.ts | 10 +- .../presence}/relay-pane-root.ts | 6 +- .../presence}/relay-tmux-hook-owner.ts | 15 +- .../publication}/agent-hook-envelope-build.ts | 8 +- .../agent-hook-envelope-publication.test.ts | 9 +- .../agent-hook-envelope-publication.ts | 6 +- .../agent-hook-integration.test.ts | 12 +- .../agent-hook-result-retry-scheduler.ts | 22 +-- ...er-claude-owed-notification-expiry.test.ts | 8 +- .../agent-hook-endpoint-coordinates.ts | 2 +- .../agent-hook-opencode-tui-identity.test.ts | 66 +------- .../server}/agent-hook-request.ts | 34 ++-- .../server}/agent-hook-server-bind.ts | 2 +- .../server}/agent-hook-server-contract.ts | 6 +- .../server}/agent-hook-server.test.ts | 8 +- .../server}/agent-hook-server.ts | 33 ++-- .../server}/agent-hook-spool-ingest.ts | 22 +-- .../server}/wsl-agent-hook-relay.test.ts | 4 +- .../status}/agent-hook-cached-pane-status.ts | 8 +- .../agent-hook-canonical-status.test.ts | 6 +- .../status}/agent-hook-canonical-status.ts | 16 +- .../status}/agent-hook-event-admission.ts | 30 ++-- ...gent-hook-interrupt-reconciliation.test.ts | 12 +- .../agent-hook-interrupt-reconciliation.ts | 20 +-- .../agent-hook-provider-normalization.test.ts | 6 +- .../agent-hook-provider-normalization.ts | 18 +- ...server-claude-session-continuation.test.ts | 10 +- ...gent-hook-server-codex-side-thread.test.ts | 6 +- ...k-server-codex-subagent-transcript.test.ts | 6 +- ...ook-server-codex-turn-interruption.test.ts | 6 +- .../claude-terminal-interrupt-status.ts | 8 +- .../{ => commands}/preflight-handler.test.ts | 12 +- .../{ => commands}/preflight-handler.ts | 12 +- .../{ => commands}/relay-command-env.test.ts | 0 .../{ => commands}/relay-command-env.ts | 4 +- .../{ => commands}/wsl-hook-fs-bridge.test.ts | 5 +- .../{ => commands}/wsl-hook-fs-bridge.ts | 4 +- .../dispatcher-bulk-failed-admission.test.ts | 56 ------ ...dispatcher-frame-guard-regressions.test.ts | 86 ---------- ...cher-per-connection-state-baseline.test.ts | 102 ----------- src/wsl-guest/dispatcher.ts | 17 -- .../opencode-startup-prompt-install.test.ts | 2 +- .../installation}/plugin-source-limit.ts | 0 .../installation}/plugin-sources.ts | 0 .../wsl-install-plugins-handler.test.ts | 2 +- .../wsl-install-plugins-handler.ts | 8 +- .../opencode}/opencode-canonical-config.ts | 8 +- .../opencode}/opencode-overlay-mirror.ts | 2 +- .../overlays}/claude-agent-view-plugin.ts | 4 +- .../overlays}/omp-config-root.test.ts | 4 +- .../overlays}/omp-status-extension.ts | 0 .../overlays}/plugin-overlay-env.test.ts | 2 +- .../overlays}/plugin-overlay-env.ts | 6 +- .../overlays}/plugin-overlay.test.ts | 0 .../{ => plugins/overlays}/plugin-overlay.ts | 21 ++- src/wsl-guest/relay-frame-decoder.ts | 31 ---- .../relay-hot-path-operation-counts.test.ts | 159 ------------------ .../connection}/client-request-aborts.ts | 0 .../dispatcher-capacity-signals.ts | 7 +- .../dispatcher-client-close-cause.test.ts | 5 +- .../dispatcher-client-lifecycle.ts | 12 +- .../connection}/dispatcher-client-state.ts | 11 +- .../connection}/dispatcher-contract.ts | 7 +- .../connection}/dispatcher-frame-codec.ts | 9 +- ...dispatcher-frame-guard-regressions.test.ts | 28 +++ .../dispatcher-rpc-cancel-id-coercion.test.ts | 4 +- .../connection}/dispatcher-rpc-routing.ts | 16 +- .../dispatcher-silent-client-reaper.test.ts | 9 +- .../dispatcher-structured-error.test.ts | 8 +- .../connection}/dispatcher-timeout.test.ts | 4 +- .../dispatcher-bulk-admission.test.ts | 0 ...spatcher-bulk-retirement-admission.test.ts | 0 .../dispatcher-bulk-write-test-harness.ts | 8 +- .../dispatcher-capacity-degradation.test.ts | 33 +--- .../dispatcher-json-payload.test.ts | 13 +- .../dispatcher-notification-ownership.test.ts | 0 .../dispatcher-producer-capacity.ts | 0 .../dispatcher-producer-transport.ts | 14 +- .../dispatcher-pty-publication.ts | 4 +- .../publication}/dispatcher.test.ts | 102 +---------- .../publication/dispatcher.ts} | 8 +- .../wire}/protocol-backpressure.test.ts | 13 +- .../wire}/protocol-handshake.test.ts | 17 +- .../wire}/protocol-json-payload.test.ts | 32 +--- .../{ => transport/wire}/protocol.ts | 23 +-- .../transport/wire/relay-frame-decoder.ts | 20 +++ .../writer}/dispatcher-client-writer.test.ts | 8 +- .../writer}/dispatcher-client-writer.ts | 12 -- .../dispatcher-writer-admission.test.ts | 0 .../writer}/dispatcher-writer-admission.ts | 0 .../writer}/dispatcher-writer-drain-arm.ts | 0 .../dispatcher-writer-lane-scheduler.ts | 0 .../writer}/dispatcher-writer-sink.ts | 0 .../legacy-relay-publication-ledger.test.ts | 0 .../legacy-relay-publication-ledger.ts | 0 src/wsl-guest/wsl-agent-hook-relay.ts | 18 +- .../omp-config-root-relay-shell.test.mjs | 4 +- 126 files changed, 502 insertions(+), 1251 deletions(-) delete mode 100644 src/wsl-guest/agent-hook-result-retry-scheduler-muse-poll.test.ts rename src/wsl-guest/{ => agent-hooks/presence}/relay-agent-presence.test.ts (86%) rename src/wsl-guest/{ => agent-hooks/presence}/relay-agent-presence.ts (87%) rename src/wsl-guest/{ => agent-hooks/presence}/relay-hook-root-acquisition.test.ts (88%) rename src/wsl-guest/{ => agent-hooks/presence}/relay-http-owner-probe-lifecycle.test.ts (86%) rename src/wsl-guest/{ => agent-hooks/presence}/relay-idle-process-owner.test.ts (95%) rename src/wsl-guest/{ => agent-hooks/presence}/relay-nested-same-provider-owner.test.ts (84%) rename src/wsl-guest/{ => agent-hooks/presence}/relay-pane-root.ts (85%) rename src/wsl-guest/{ => agent-hooks/presence}/relay-tmux-hook-owner.ts (69%) rename src/wsl-guest/{ => agent-hooks/publication}/agent-hook-envelope-build.ts (88%) rename src/wsl-guest/{ => agent-hooks/publication}/agent-hook-envelope-publication.test.ts (97%) rename src/wsl-guest/{ => agent-hooks/publication}/agent-hook-envelope-publication.ts (97%) rename src/wsl-guest/{ => agent-hooks/publication}/agent-hook-integration.test.ts (96%) rename src/wsl-guest/{ => agent-hooks/results}/agent-hook-result-retry-scheduler.ts (89%) rename src/wsl-guest/{ => agent-hooks/results}/agent-hook-server-claude-owed-notification-expiry.test.ts (89%) rename src/wsl-guest/{ => agent-hooks/server}/agent-hook-endpoint-coordinates.ts (96%) rename src/wsl-guest/{ => agent-hooks/server}/agent-hook-opencode-tui-identity.test.ts (71%) rename src/wsl-guest/{ => agent-hooks/server}/agent-hook-request.ts (77%) rename src/wsl-guest/{ => agent-hooks/server}/agent-hook-server-bind.ts (93%) rename src/wsl-guest/{ => agent-hooks/server}/agent-hook-server-contract.ts (84%) rename src/wsl-guest/{ => agent-hooks/server}/agent-hook-server.test.ts (97%) rename src/wsl-guest/{ => agent-hooks/server}/agent-hook-server.ts (88%) rename src/wsl-guest/{ => agent-hooks/server}/agent-hook-spool-ingest.ts (77%) rename src/wsl-guest/{ => agent-hooks/server}/wsl-agent-hook-relay.test.ts (97%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-cached-pane-status.ts (84%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-canonical-status.test.ts (95%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-canonical-status.ts (84%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-event-admission.ts (77%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-interrupt-reconciliation.test.ts (91%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-interrupt-reconciliation.ts (82%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-provider-normalization.test.ts (93%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-provider-normalization.ts (78%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-server-claude-session-continuation.test.ts (88%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-server-codex-side-thread.test.ts (94%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-server-codex-subagent-transcript.test.ts (91%) rename src/wsl-guest/{ => agent-hooks/status}/agent-hook-server-codex-turn-interruption.test.ts (92%) rename src/wsl-guest/{ => agent-hooks/status}/claude-terminal-interrupt-status.ts (71%) rename src/wsl-guest/{ => commands}/preflight-handler.test.ts (94%) rename src/wsl-guest/{ => commands}/preflight-handler.ts (95%) rename src/wsl-guest/{ => commands}/relay-command-env.test.ts (100%) rename src/wsl-guest/{ => commands}/relay-command-env.ts (96%) rename src/wsl-guest/{ => commands}/wsl-hook-fs-bridge.test.ts (94%) rename src/wsl-guest/{ => commands}/wsl-hook-fs-bridge.ts (97%) delete mode 100644 src/wsl-guest/dispatcher-bulk-failed-admission.test.ts delete mode 100644 src/wsl-guest/dispatcher-frame-guard-regressions.test.ts delete mode 100644 src/wsl-guest/dispatcher-per-connection-state-baseline.test.ts delete mode 100644 src/wsl-guest/dispatcher.ts rename src/wsl-guest/{ => plugins/installation}/opencode-startup-prompt-install.test.ts (98%) rename src/wsl-guest/{ => plugins/installation}/plugin-source-limit.ts (100%) rename src/wsl-guest/{ => plugins/installation}/plugin-sources.ts (100%) rename src/wsl-guest/{ => plugins/installation}/wsl-install-plugins-handler.test.ts (99%) rename src/wsl-guest/{ => plugins/installation}/wsl-install-plugins-handler.ts (96%) rename src/wsl-guest/{ => plugins/opencode}/opencode-canonical-config.ts (81%) rename src/wsl-guest/{ => plugins/opencode}/opencode-overlay-mirror.ts (94%) rename src/wsl-guest/{ => plugins/overlays}/claude-agent-view-plugin.ts (86%) rename src/wsl-guest/{ => plugins/overlays}/omp-config-root.test.ts (93%) rename src/wsl-guest/{ => plugins/overlays}/omp-status-extension.ts (100%) rename src/wsl-guest/{ => plugins/overlays}/plugin-overlay-env.test.ts (97%) rename src/wsl-guest/{ => plugins/overlays}/plugin-overlay-env.ts (90%) rename src/wsl-guest/{ => plugins/overlays}/plugin-overlay.test.ts (100%) rename src/wsl-guest/{ => plugins/overlays}/plugin-overlay.ts (94%) delete mode 100644 src/wsl-guest/relay-frame-decoder.ts delete mode 100644 src/wsl-guest/relay-hot-path-operation-counts.test.ts rename src/wsl-guest/{ => transport/connection}/client-request-aborts.ts (100%) rename src/wsl-guest/{ => transport/connection}/dispatcher-capacity-signals.ts (96%) rename src/wsl-guest/{ => transport/connection}/dispatcher-client-close-cause.test.ts (90%) rename src/wsl-guest/{ => transport/connection}/dispatcher-client-lifecycle.ts (95%) rename src/wsl-guest/{ => transport/connection}/dispatcher-client-state.ts (90%) rename src/wsl-guest/{ => transport/connection}/dispatcher-contract.ts (91%) rename src/wsl-guest/{ => transport/connection}/dispatcher-frame-codec.ts (92%) create mode 100644 src/wsl-guest/transport/connection/dispatcher-frame-guard-regressions.test.ts rename src/wsl-guest/{ => transport/connection}/dispatcher-rpc-cancel-id-coercion.test.ts (92%) rename src/wsl-guest/{ => transport/connection}/dispatcher-rpc-routing.ts (95%) rename src/wsl-guest/{ => transport/connection}/dispatcher-silent-client-reaper.test.ts (95%) rename src/wsl-guest/{ => transport/connection}/dispatcher-structured-error.test.ts (93%) rename src/wsl-guest/{ => transport/connection}/dispatcher-timeout.test.ts (91%) rename src/wsl-guest/{ => transport/publication}/dispatcher-bulk-admission.test.ts (100%) rename src/wsl-guest/{ => transport/publication}/dispatcher-bulk-retirement-admission.test.ts (100%) rename src/wsl-guest/{ => transport/publication}/dispatcher-bulk-write-test-harness.ts (94%) rename src/wsl-guest/{ => transport/publication}/dispatcher-capacity-degradation.test.ts (93%) rename src/wsl-guest/{ => transport/publication}/dispatcher-json-payload.test.ts (97%) rename src/wsl-guest/{ => transport/publication}/dispatcher-notification-ownership.test.ts (100%) rename src/wsl-guest/{ => transport/publication}/dispatcher-producer-capacity.ts (100%) rename src/wsl-guest/{ => transport/publication}/dispatcher-producer-transport.ts (91%) rename src/wsl-guest/{ => transport/publication}/dispatcher-pty-publication.ts (96%) rename src/wsl-guest/{ => transport/publication}/dispatcher.test.ts (84%) rename src/wsl-guest/{dispatcher-notification-publication.ts => transport/publication/dispatcher.ts} (96%) rename src/wsl-guest/{ => transport/wire}/protocol-backpressure.test.ts (97%) rename src/wsl-guest/{ => transport/wire}/protocol-handshake.test.ts (94%) rename src/wsl-guest/{ => transport/wire}/protocol-json-payload.test.ts (54%) rename src/wsl-guest/{ => transport/wire}/protocol.ts (92%) create mode 100644 src/wsl-guest/transport/wire/relay-frame-decoder.ts rename src/wsl-guest/{ => transport/writer}/dispatcher-client-writer.test.ts (98%) rename src/wsl-guest/{ => transport/writer}/dispatcher-client-writer.ts (96%) rename src/wsl-guest/{ => transport/writer}/dispatcher-writer-admission.test.ts (100%) rename src/wsl-guest/{ => transport/writer}/dispatcher-writer-admission.ts (100%) rename src/wsl-guest/{ => transport/writer}/dispatcher-writer-drain-arm.ts (100%) rename src/wsl-guest/{ => transport/writer}/dispatcher-writer-lane-scheduler.ts (100%) rename src/wsl-guest/{ => transport/writer}/dispatcher-writer-sink.ts (100%) rename src/wsl-guest/{ => transport/writer}/legacy-relay-publication-ledger.test.ts (100%) rename src/wsl-guest/{ => transport/writer}/legacy-relay-publication-ledger.ts (100%) diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index 74d59db41c9..c647a955b37 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -223,8 +223,8 @@ 2 test-no-real-time-wait:src/shared/remote-runtime/control/remote-runtime-shared-control-auth-refusal.test.ts 2 test-no-real-time-wait:src/shared/startup-command-staging.node-pty.test.ts 4 test-no-real-time-wait:src/shared/terminal/pty/ingress/pty-reply-echo-shapes.node-pty.test.ts -1 test-no-real-time-wait:src/wsl-guest/agent-hook-canonical-status.test.ts -5 test-no-real-time-wait:src/wsl-guest/agent-hook-server.test.ts +1 test-no-real-time-wait:src/wsl-guest/agent-hooks/status/agent-hook-canonical-status.test.ts +5 test-no-real-time-wait:src/wsl-guest/agent-hooks/server/agent-hook-server.test.ts 1 test-no-real-time-wait:tests/e2e/accumulated-workspace-status-fixture.ts 1 test-no-real-time-wait:tests/e2e/agent-session-live-force-exit-resume.spec.ts 3 test-no-real-time-wait:tests/e2e/agent-session-log-tail-stability.spec.ts @@ -541,10 +541,9 @@ 2 test-no-wall-clock:src/shared/startup-command-staging.node-pty.test.ts 1 test-no-wall-clock:src/shared/startup-command-staging.test.ts 2 test-no-wall-clock:src/shared/terminal/pty/ingress/pty-reply-echo-shapes.node-pty.test.ts -10 test-no-wall-clock:src/wsl-guest/agent-hook-integration.test.ts -1 test-no-wall-clock:src/wsl-guest/agent-hook-opencode-tui-identity.test.ts -1 test-no-wall-clock:src/wsl-guest/agent-hook-server.test.ts -1 test-no-wall-clock:src/wsl-guest/relay-idle-process-owner.test.ts +10 test-no-wall-clock:src/wsl-guest/agent-hooks/publication/agent-hook-integration.test.ts +1 test-no-wall-clock:src/wsl-guest/agent-hooks/server/agent-hook-server.test.ts +1 test-no-wall-clock:src/wsl-guest/agent-hooks/presence/relay-idle-process-owner.test.ts 1 test-no-wall-clock:tests/e2e/cross-version-wire/cross-version-web-device-key.unit.test.ts 2 test-no-wall-clock:tests/e2e/cross-version-wire/legacy-relay-bridge.unit.test.ts 4 test-no-wall-clock:tests/e2e/cross-version-wire/release-checkout.unit.test.ts diff --git a/config/process-host-direct-import-baseline.txt b/config/process-host-direct-import-baseline.txt index 4916031423b..72f773160d3 100644 --- a/config/process-host-direct-import-baseline.txt +++ b/config/process-host-direct-import-baseline.txt @@ -73,4 +73,4 @@ src/shared/fish-binary-requirement.ts src/shared/process-table-ps-capture.ts src/shared/terminal/pty/native-process/pty-slave-line-discipline-echo.ts src/shared/terminal/pty/native-process/shell-process-readiness.ts -src/wsl-guest/preflight-handler.ts +src/wsl-guest/commands/preflight-handler.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index c9adc5d9fbf..2077fe4a6fe 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -8855,32 +8855,32 @@ "commands": [ "pnpm exec vitest run --config config/vitest.config.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts", - "pnpm exec vitest run --config config/vitest.config.ts src/wsl-guest/protocol-backpressure.test.ts --reporter=dot", - "pnpm exec vitest run --config config/vitest.config.ts src/wsl-guest/dispatcher.test.ts --reporter=dot", - "pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/protocol/ssh-multiplexer-transport-writer.test.ts src/main/ssh/protocol/ssh-channel-multiplexer.test.ts src/wsl-guest/dispatcher-client-writer.test.ts --reporter=dot", - "pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/protocol/ssh-multiplexer-transport-writer.test.ts src/main/ssh/protocol/ssh-channel-multiplexer.test.ts src/main/ssh/protocol/ssh-channel-multiplexer-backpressure.test.ts src/wsl-guest/dispatcher-client-writer.test.ts --reporter=dot", + "pnpm exec vitest run --config config/vitest.config.ts src/wsl-guest/transport/wire/protocol-backpressure.test.ts --reporter=dot", + "pnpm exec vitest run --config config/vitest.config.ts src/wsl-guest/transport/publication/dispatcher.test.ts --reporter=dot", + "pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/protocol/ssh-multiplexer-transport-writer.test.ts src/main/ssh/protocol/ssh-channel-multiplexer.test.ts src/wsl-guest/transport/writer/dispatcher-client-writer.test.ts --reporter=dot", + "pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/protocol/ssh-multiplexer-transport-writer.test.ts src/main/ssh/protocol/ssh-channel-multiplexer.test.ts src/main/ssh/protocol/ssh-channel-multiplexer-backpressure.test.ts src/wsl-guest/transport/writer/dispatcher-client-writer.test.ts --reporter=dot", "pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/protocol/ssh-multiplexer-transport-writer.test.ts src/main/ssh/protocol/ssh-channel-multiplexer-backpressure.test.ts --reporter=dot", "pnpm exec vitest run --config config/vitest.config.ts src/main/agent-hooks/wsl-relay/wsl-hook-relay-sentinel.test.ts --reporter=dot" ], "testFiles": [ "src/main/runtime/construction/orca-runtime.test.ts", - "src/wsl-guest/protocol-backpressure.test.ts", - "src/wsl-guest/dispatcher.test.ts", + "src/wsl-guest/transport/wire/protocol-backpressure.test.ts", + "src/wsl-guest/transport/publication/dispatcher.test.ts", "src/main/ssh/protocol/ssh-multiplexer-transport-writer.test.ts", "src/main/ssh/protocol/ssh-channel-multiplexer.test.ts", "src/main/ssh/protocol/ssh-channel-multiplexer-backpressure.test.ts", - "src/wsl-guest/dispatcher-client-writer.test.ts", + "src/wsl-guest/transport/writer/dispatcher-client-writer.test.ts", "src/main/agent-hooks/wsl-relay/wsl-hook-relay-sentinel.test.ts" ], "assertionRefs": [ { - "file": "src/wsl-guest/dispatcher.test.ts", + "file": "src/wsl-guest/transport/publication/dispatcher.test.ts", "assertions": [ "a saturated legacy primary remains required backpressure and is never detached as an additional subscriber" ] }, { - "file": "src/wsl-guest/protocol-backpressure.test.ts", + "file": "src/wsl-guest/transport/wire/protocol-backpressure.test.ts", "assertions": [ "relay decoder accepts one maximum frame plus 1 MiB partial input and rejects one extra byte", "a throwing continuation clears retained input, releases one pause epoch, and publishes one typed ownership error" @@ -8903,7 +8903,7 @@ ] }, { - "file": "src/wsl-guest/dispatcher-client-writer.test.ts", + "file": "src/wsl-guest/transport/writer/dispatcher-client-writer.test.ts", "assertions": [ "encoded producer frames stay below high-water-minus-reserve capacity", "one fixed-size filesystem compatibility frame is admitted only on an empty sink", @@ -8925,7 +8925,8 @@ "command": "pnpm exec vitest run --config config/vitest.config.ts src/wsl-guest/protocol-backpressure.test.ts --reporter=dot", "result": "passed", "durationSeconds": 2.24, - "summary": "Sixteen deterministic SSH relay/session/source/decoder files passed 192 tests, including exit-sealed private recovery, retained stale-transfer cancellation authority, exact private-frame proof watermarks, one retirement record across 10,000 same-PTY token rotations, stale-owner fallback, and scheduler rejection isolation; no live topology was exercised." + "summary": "Sixteen deterministic SSH relay/session/source/decoder files passed 192 tests, including exit-sealed private recovery, retained stale-transfer cancellation authority, exact private-frame proof watermarks, one retirement record across 10,000 same-PTY token rotations, stale-owner fallback, and scheduler rejection isolation; no live topology was exercised.", + "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/wsl-guest/transport/wire/protocol-backpressure.test.ts --reporter=dot" } ], "runtimeBudget": { diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 99963c77012..6fc0be9f98e 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -6874,52 +6874,49 @@ "src/shared/wsl-login-shell-fish-version.test.ts": 148, "src/shared/wsl-login-shell-nushell.test.ts": 66, "src/shared/zod-salvage-absence.test.ts": 96, - "src/wsl-guest/agent-hook-canonical-status.test.ts": 2770, - "src/wsl-guest/agent-hook-envelope-publication.test.ts": 348, - "src/wsl-guest/agent-hook-integration.test.ts": 1827, - "src/wsl-guest/agent-hook-interrupt-reconciliation.test.ts": 586, - "src/wsl-guest/agent-hook-opencode-tui-identity.test.ts": 551, - "src/wsl-guest/agent-hook-provider-normalization.test.ts": 489, - "src/wsl-guest/agent-hook-server-claude-owed-notification-expiry.test.ts": 792, - "src/wsl-guest/agent-hook-server-claude-session-continuation.test.ts": 462, - "src/wsl-guest/agent-hook-server-codex-side-thread.test.ts": 786, - "src/wsl-guest/agent-hook-server-codex-subagent-transcript.test.ts": 1589, - "src/wsl-guest/agent-hook-server-codex-turn-interruption.test.ts": 1786, - "src/wsl-guest/agent-hook-server.test.ts": 1651, - "src/wsl-guest/dispatcher-bulk-admission.test.ts": 390, - "src/wsl-guest/dispatcher-bulk-failed-admission.test.ts": 181, - "src/wsl-guest/dispatcher-bulk-retirement-admission.test.ts": 2204, - "src/wsl-guest/dispatcher-capacity-degradation.test.ts": 313, - "src/wsl-guest/dispatcher-client-close-cause.test.ts": 132, - "src/wsl-guest/dispatcher-client-writer.test.ts": 70, - "src/wsl-guest/dispatcher-frame-guard-regressions.test.ts": 170, - "src/wsl-guest/dispatcher-json-payload.test.ts": 239, - "src/wsl-guest/dispatcher-notification-ownership.test.ts": 120, - "src/wsl-guest/dispatcher-per-connection-state-baseline.test.ts": 226, - "src/wsl-guest/dispatcher-rpc-cancel-id-coercion.test.ts": 203, - "src/wsl-guest/dispatcher-silent-client-reaper.test.ts": 195, - "src/wsl-guest/dispatcher-structured-error.test.ts": 203, - "src/wsl-guest/dispatcher-timeout.test.ts": 137, - "src/wsl-guest/dispatcher-writer-admission.test.ts": 109, - "src/wsl-guest/dispatcher.test.ts": 348, - "src/wsl-guest/legacy-relay-publication-ledger.test.ts": 41, - "src/wsl-guest/omp-config-root.test.ts": 254, - "src/wsl-guest/opencode-startup-prompt-install.test.ts": 290, - "src/wsl-guest/plugin-overlay-env.test.ts": 130, - "src/wsl-guest/plugin-overlay.test.ts": 263, - "src/wsl-guest/preflight-handler.test.ts": 95, - "src/wsl-guest/protocol-backpressure.test.ts": 126, - "src/wsl-guest/protocol-handshake.test.ts": 66, - "src/wsl-guest/protocol-json-payload.test.ts": 144, - "src/wsl-guest/relay-agent-presence.test.ts": 518, - "src/wsl-guest/relay-command-env.test.ts": 57, - "src/wsl-guest/relay-hot-path-operation-counts.test.ts": 215, - "src/wsl-guest/relay-http-owner-probe-lifecycle.test.ts": 662, - "src/wsl-guest/relay-idle-process-owner.test.ts": 522, - "src/wsl-guest/relay-nested-same-provider-owner.test.ts": 415, - "src/wsl-guest/wsl-agent-hook-relay.test.ts": 471, - "src/wsl-guest/wsl-hook-fs-bridge.test.ts": 66, - "src/wsl-guest/wsl-install-plugins-handler.test.ts": 283, + "src/wsl-guest/agent-hooks/status/agent-hook-canonical-status.test.ts": 2770, + "src/wsl-guest/agent-hooks/publication/agent-hook-envelope-publication.test.ts": 348, + "src/wsl-guest/agent-hooks/publication/agent-hook-integration.test.ts": 1827, + "src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.test.ts": 586, + "src/wsl-guest/agent-hooks/server/agent-hook-opencode-tui-identity.test.ts": 551, + "src/wsl-guest/agent-hooks/status/agent-hook-provider-normalization.test.ts": 489, + "src/wsl-guest/agent-hooks/results/agent-hook-server-claude-owed-notification-expiry.test.ts": 792, + "src/wsl-guest/agent-hooks/status/agent-hook-server-claude-session-continuation.test.ts": 462, + "src/wsl-guest/agent-hooks/status/agent-hook-server-codex-side-thread.test.ts": 786, + "src/wsl-guest/agent-hooks/status/agent-hook-server-codex-subagent-transcript.test.ts": 1589, + "src/wsl-guest/agent-hooks/status/agent-hook-server-codex-turn-interruption.test.ts": 1786, + "src/wsl-guest/agent-hooks/server/agent-hook-server.test.ts": 1651, + "src/wsl-guest/transport/publication/dispatcher-bulk-admission.test.ts": 390, + "src/wsl-guest/transport/publication/dispatcher-bulk-retirement-admission.test.ts": 2204, + "src/wsl-guest/transport/publication/dispatcher-capacity-degradation.test.ts": 313, + "src/wsl-guest/transport/connection/dispatcher-client-close-cause.test.ts": 132, + "src/wsl-guest/transport/writer/dispatcher-client-writer.test.ts": 70, + "src/wsl-guest/transport/connection/dispatcher-frame-guard-regressions.test.ts": 170, + "src/wsl-guest/transport/publication/dispatcher-json-payload.test.ts": 239, + "src/wsl-guest/transport/publication/dispatcher-notification-ownership.test.ts": 120, + "src/wsl-guest/transport/connection/dispatcher-rpc-cancel-id-coercion.test.ts": 203, + "src/wsl-guest/transport/connection/dispatcher-silent-client-reaper.test.ts": 195, + "src/wsl-guest/transport/connection/dispatcher-structured-error.test.ts": 203, + "src/wsl-guest/transport/connection/dispatcher-timeout.test.ts": 137, + "src/wsl-guest/transport/writer/dispatcher-writer-admission.test.ts": 109, + "src/wsl-guest/transport/publication/dispatcher.test.ts": 348, + "src/wsl-guest/transport/writer/legacy-relay-publication-ledger.test.ts": 41, + "src/wsl-guest/plugins/overlays/omp-config-root.test.ts": 254, + "src/wsl-guest/plugins/installation/opencode-startup-prompt-install.test.ts": 290, + "src/wsl-guest/plugins/overlays/plugin-overlay-env.test.ts": 130, + "src/wsl-guest/plugins/overlays/plugin-overlay.test.ts": 263, + "src/wsl-guest/commands/preflight-handler.test.ts": 95, + "src/wsl-guest/transport/wire/protocol-backpressure.test.ts": 126, + "src/wsl-guest/transport/wire/protocol-handshake.test.ts": 66, + "src/wsl-guest/transport/wire/protocol-json-payload.test.ts": 144, + "src/wsl-guest/agent-hooks/presence/relay-agent-presence.test.ts": 518, + "src/wsl-guest/commands/relay-command-env.test.ts": 57, + "src/wsl-guest/agent-hooks/presence/relay-http-owner-probe-lifecycle.test.ts": 662, + "src/wsl-guest/agent-hooks/presence/relay-idle-process-owner.test.ts": 522, + "src/wsl-guest/agent-hooks/presence/relay-nested-same-provider-owner.test.ts": 415, + "src/wsl-guest/agent-hooks/server/wsl-agent-hook-relay.test.ts": 471, + "src/wsl-guest/commands/wsl-hook-fs-bridge.test.ts": 66, + "src/wsl-guest/plugins/installation/wsl-install-plugins-handler.test.ts": 283, "tests/e2e/SmartWorkspaceSourceResultList.linear-auth.unit.test.ts": 1976, "tests/e2e/SmartWorkspaceSourceResultList.linear-read-error.unit.test.ts": 3757, "tests/e2e/agent-launch-setup-choice.unit.test.ts": 2657, diff --git a/config/scripts/performance/terminal-stream/relay-json-payload-benchmark.ts b/config/scripts/performance/terminal-stream/relay-json-payload-benchmark.ts index 6c4de9edf95..2db777f2c39 100644 --- a/config/scripts/performance/terminal-stream/relay-json-payload-benchmark.ts +++ b/config/scripts/performance/terminal-stream/relay-json-payload-benchmark.ts @@ -1,15 +1,14 @@ import assert from 'node:assert/strict' import { performance } from 'node:perf_hooks' import { platform, release } from 'node:os' +import { HEADER_LENGTH, MAX_MESSAGE_SIZE } from '../../../../src/shared/relay-frame-decoder' import { - HEADER_LENGTH, - MAX_MESSAGE_SIZE, MessageType, encodeFrame, encodePreparedJsonRpcFrame, prepareJsonRpcPayload, type JsonRpcNotification -} from '../../../../src/wsl-guest/protocol' +} from '../../../../src/wsl-guest/transport/wire/protocol' type BenchmarkCase = { name: string diff --git a/src/main/agent-hooks/installation/opencode-plugin-settings.ts b/src/main/agent-hooks/installation/opencode-plugin-settings.ts index 6818153fb7a..20c7819a8d1 100644 --- a/src/main/agent-hooks/installation/opencode-plugin-settings.ts +++ b/src/main/agent-hooks/installation/opencode-plugin-settings.ts @@ -1,4 +1,4 @@ -import type { PluginSources } from '../../../wsl-guest/plugin-sources' +import type { PluginSources } from '../../../wsl-guest/plugins/installation/plugin-sources' import { isAgentStatusHooksEnabledForAgent } from '../../../shared/agent-hook-listener/configuration/agent-status-hooks-setting' import type { ManagedHookDetectionSettings } from './managed-hook-detection-commands' diff --git a/src/main/agent-hooks/server/testing/http/server-startup-lifecycle.test.ts b/src/main/agent-hooks/server/testing/http/server-startup-lifecycle.test.ts index e81d00e1006..d774ef9f348 100644 --- a/src/main/agent-hooks/server/testing/http/server-startup-lifecycle.test.ts +++ b/src/main/agent-hooks/server/testing/http/server-startup-lifecycle.test.ts @@ -3,7 +3,7 @@ import { existsSync, mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { AgentHookServer } from '../../agent-hook-server' -import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hook-server' +import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hooks/server/agent-hook-server' import type { drainAgentHookSpoolAsync } from '../../../../../shared/agent-hook-listener/transport/agent-hook-spool' import type * as HookSpoolModule from '../../../../../shared/agent-hook-listener/transport/agent-hook-spool' import { getEndpointFileName } from '../../../../../shared/agent-hook-listener/transport/endpoint-publication' diff --git a/src/main/agent-hooks/server/testing/identity/server-hermes-resume-identity.test.ts b/src/main/agent-hooks/server/testing/identity/server-hermes-resume-identity.test.ts index d53513294c4..5cbc0fd8bdf 100644 --- a/src/main/agent-hooks/server/testing/identity/server-hermes-resume-identity.test.ts +++ b/src/main/agent-hooks/server/testing/identity/server-hermes-resume-identity.test.ts @@ -7,7 +7,7 @@ import { createHookListenerState } from '../../../../../shared/agent-hook-listen import { resumeRecordFromStatus } from '../../../../../shared/agent-session/lifecycle/agent-resume-record-from-status' import { getAgentResumeArgv } from '../../../../../shared/agent-launch/startup/agent-resume-argv' import type { AgentHookRelayEnvelope } from '../../../../../shared/agent-hook-listener/transport/agent-hook-relay' -import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hook-server' +import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hooks/server/agent-hook-server' import { AgentHookServer } from '../../agent-hook-server' import { buildBody, postHookEvent } from '../server.test-fixtures' diff --git a/src/main/agent-hooks/server/testing/identity/server-hook-session-binding.test.ts b/src/main/agent-hooks/server/testing/identity/server-hook-session-binding.test.ts index 2d5ed371a47..3f270fb42f5 100644 --- a/src/main/agent-hooks/server/testing/identity/server-hook-session-binding.test.ts +++ b/src/main/agent-hooks/server/testing/identity/server-hook-session-binding.test.ts @@ -14,8 +14,8 @@ import { import { readAgentProcessPresence } from '../../../../../shared/agent-status/process/agent-process-presence' import { AGENT_STATUS_2A_CURRENT_PRODUCER_MODE } from '../../../../../shared/agent-status/transport/agent-status-legacy-adapter' import { getDefaultWorkspaceSession } from '../../../../../shared/constants' -import { buildRelayHookEnvelope } from '../../../../../wsl-guest/agent-hook-envelope-build' -import { RelayProviderHookNormalization } from '../../../../../wsl-guest/agent-hook-provider-normalization' +import { buildRelayHookEnvelope } from '../../../../../wsl-guest/agent-hooks/publication/agent-hook-envelope-build' +import { RelayProviderHookNormalization } from '../../../../../wsl-guest/agent-hooks/status/agent-hook-provider-normalization' import { hookRowPaneResumeRecord } from '../../../../runtime/session-lifecycle/projection/hook-row-pane-resume-record' import { hookStatusResumeCommand, diff --git a/src/main/agent-hooks/server/testing/identity/server-opencode-writer-observation.test.ts b/src/main/agent-hooks/server/testing/identity/server-opencode-writer-observation.test.ts index ff1f3b0170c..fdc6f1a2025 100644 --- a/src/main/agent-hooks/server/testing/identity/server-opencode-writer-observation.test.ts +++ b/src/main/agent-hooks/server/testing/identity/server-opencode-writer-observation.test.ts @@ -4,7 +4,7 @@ import { } from '../../../../runtime/terminal/identity/agent-status-observed-pane-identity' import { OPEN_CODE_WRITER_TOPOLOGY } from '../../cli-writer-observation' import { toAgentStatusIpcPayload } from '../../server-status-identity' -import { buildRelayHookEnvelope } from '../../../../../wsl-guest/agent-hook-envelope-build' +import { buildRelayHookEnvelope } from '../../../../../wsl-guest/agent-hooks/publication/agent-hook-envelope-build' import { afterEach, describe, expect, it, vi } from 'vitest' import { AgentHookServer } from '../../agent-hook-server' import { buildBody, PANE, postHookEvent } from '../server.test-fixtures' diff --git a/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-kill.test.ts b/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-kill.test.ts index 79d2fb290f8..04965f84f54 100644 --- a/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-kill.test.ts +++ b/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-kill.test.ts @@ -2,7 +2,7 @@ import { appendFile, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/pro import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hook-server' +import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hooks/server/agent-hook-server' import { selectFreshExplicitAgentStatus } from '../../../../runtime/workspaces/process-listing/runtime-hook-agent-row-selection' import { selectRuntimeHookAgentRowForPane } from '../../../../runtime/session-lifecycle/projection/runtime-mobile-agent-status-projection' import { buildRunningAgentChildRowModels } from '../../../../../shared/running-agent-child-rows' diff --git a/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-recovery.test.ts b/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-recovery.test.ts index a72dcf5b80f..1311a436db7 100644 --- a/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-recovery.test.ts +++ b/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-recovery.test.ts @@ -4,7 +4,7 @@ import { tmpdir } from 'node:os' import { createServer } from 'node:http' import { join } from 'node:path' import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hook-server' +import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hooks/server/agent-hook-server' import { AgentHookServer } from '../../agent-hook-server' import { buildBody, PANE, postHookEvent } from '../server.test-fixtures' import { ClaudeHelperKillPoller } from '../../../../../shared/agent-hook-listener/providers/claude-helper-kill-poller' diff --git a/src/main/agent-hooks/server/testing/interruptions/server-relayed-claude-cancel.test.ts b/src/main/agent-hooks/server/testing/interruptions/server-relayed-claude-cancel.test.ts index cef90654fd8..a1225889913 100644 --- a/src/main/agent-hooks/server/testing/interruptions/server-relayed-claude-cancel.test.ts +++ b/src/main/agent-hooks/server/testing/interruptions/server-relayed-claude-cancel.test.ts @@ -3,7 +3,7 @@ import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hook-server' +import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hooks/server/agent-hook-server' import { AgentHookServer, _internals } from '../../agent-hook-server' import { buildBody, PANE } from '../server.test-fixtures' import { hookAt, loadCapture, type CapturedHook } from './claude-cancel-capture.test-fixture' diff --git a/src/main/agent-hooks/server/testing/presence/server-hook-root-provenance.test.ts b/src/main/agent-hooks/server/testing/presence/server-hook-root-provenance.test.ts index d926d208476..d9abbd754e6 100644 --- a/src/main/agent-hooks/server/testing/presence/server-hook-root-provenance.test.ts +++ b/src/main/agent-hooks/server/testing/presence/server-hook-root-provenance.test.ts @@ -6,8 +6,8 @@ import { PANE } from '../server.test-fixtures' import '../../../../../shared/agent-hook-listener/presence/hook-process-root-evidence' -import '../../../../../wsl-guest/agent-hook-server' -import '../../../../../wsl-guest/relay-pane-root' +import '../../../../../wsl-guest/agent-hooks/server/agent-hook-server' +import '../../../../../wsl-guest/agent-hooks/presence/relay-pane-root' import '../../../observations/local-tmux-managed-pty' import { diff --git a/src/main/agent-hooks/server/testing/providers/codex/server-codex-session-start.test.ts b/src/main/agent-hooks/server/testing/providers/codex/server-codex-session-start.test.ts index 953728ce3f0..3a970cfc422 100644 --- a/src/main/agent-hooks/server/testing/providers/codex/server-codex-session-start.test.ts +++ b/src/main/agent-hooks/server/testing/providers/codex/server-codex-session-start.test.ts @@ -2,8 +2,8 @@ import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { expect, it, vi } from 'vitest' -import { RelayAgentHookServer } from '../../../../../../wsl-guest/agent-hook-server' -import { buildCachedPaneReplayEnvelope } from '../../../../../../wsl-guest/agent-hook-cached-pane-status' +import { RelayAgentHookServer } from '../../../../../../wsl-guest/agent-hooks/server/agent-hook-server' +import { buildCachedPaneReplayEnvelope } from '../../../../../../wsl-guest/agent-hooks/status/agent-hook-cached-pane-status' import type { AgentHookEventPayload } from '../../../../../../shared/agent-hook-listener/listener-event' import type { AgentHookRelayEnvelope } from '../../../../../../shared/agent-hook-listener/transport/agent-hook-relay' import { AgentHookServer } from '../../../agent-hook-server' diff --git a/src/main/agent-hooks/server/testing/remote-ingress/server-ingest-remote-codex-transcript.test.ts b/src/main/agent-hooks/server/testing/remote-ingress/server-ingest-remote-codex-transcript.test.ts index 9a841ef9f90..318492b700d 100644 --- a/src/main/agent-hooks/server/testing/remote-ingress/server-ingest-remote-codex-transcript.test.ts +++ b/src/main/agent-hooks/server/testing/remote-ingress/server-ingest-remote-codex-transcript.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { buildCachedPaneReplayEnvelope } from '../../../../../wsl-guest/agent-hook-cached-pane-status' +import { buildCachedPaneReplayEnvelope } from '../../../../../wsl-guest/agent-hooks/status/agent-hook-cached-pane-status' import type { AgentHookEventPayload } from '../../../../../shared/agent-hook-listener/listener-event' import { AgentHookServer, _internals } from '../../agent-hook-server' import { PANE } from '../server.test-fixtures' diff --git a/src/main/agent-hooks/server/testing/replay/server-relayed-claude-permission-replay.test.ts b/src/main/agent-hooks/server/testing/replay/server-relayed-claude-permission-replay.test.ts index 71462710607..171bc1bfe2b 100644 --- a/src/main/agent-hooks/server/testing/replay/server-relayed-claude-permission-replay.test.ts +++ b/src/main/agent-hooks/server/testing/replay/server-relayed-claude-permission-replay.test.ts @@ -3,7 +3,7 @@ import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hook-server' +import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hooks/server/agent-hook-server' import type { AgentHookRelayEnvelope } from '../../../../../shared/agent-hook-listener/transport/agent-hook-relay' import { AgentHookServer, _internals } from '../../agent-hook-server' import { buildBody, PANE } from '../server.test-fixtures' diff --git a/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts b/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts index 4bf152658d2..4b98d52430b 100644 --- a/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts +++ b/src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts @@ -2,7 +2,7 @@ import { readFileSync, mkdtempSync, rmSync } from 'node:fs' import { join } from 'node:path' import { tmpdir } from 'node:os' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hook-server' +import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hooks/server/agent-hook-server' import { AgentHookServer, _internals } from '../../agent-hook-server' import { createRuntimeAutomationRunTerminalObserver } from '../../../../automations/observation/runtime-terminal-run-observer' import { diff --git a/src/main/agent-hooks/wsl-relay/wsl-guest-plugin-install.ts b/src/main/agent-hooks/wsl-relay/wsl-guest-plugin-install.ts index 716c24f4303..3aded96d574 100644 --- a/src/main/agent-hooks/wsl-relay/wsl-guest-plugin-install.ts +++ b/src/main/agent-hooks/wsl-relay/wsl-guest-plugin-install.ts @@ -3,7 +3,7 @@ import { AGENT_HOOK_INSTALL_PLUGINS_METHOD } from '../../../shared/agent-hook-li import { isRecord } from '../../../shared/agent-status/child-work/agent-status-child-work-value-guards' import { selectOpenCodePluginSources } from '../installation/opencode-plugin-settings' import type { ManagedHookDetectionSettings } from '../installation/managed-hook-detection-commands' -import type { PluginSources } from '../../../wsl-guest/plugin-sources' +import type { PluginSources } from '../../../wsl-guest/plugins/installation/plugin-sources' type GuestPluginInstallDeps = { pluginSources: () => PluginSources diff --git a/src/main/agent-hooks/wsl-relay/wsl-hook-relay-deps.ts b/src/main/agent-hooks/wsl-relay/wsl-hook-relay-deps.ts index b06abbffb94..8dc04d413f2 100644 --- a/src/main/agent-hooks/wsl-relay/wsl-hook-relay-deps.ts +++ b/src/main/agent-hooks/wsl-relay/wsl-hook-relay-deps.ts @@ -21,7 +21,7 @@ import { import { getPiAgentStatusExtensionSource } from '../../pi/managed-extensions/agent-status/agent-status-extension-source' import { codexHookService } from '../../codex/hooks/installation/hook-service' import type { AgentHookInstallStatus } from '../../../shared/agent-hook-listener/configuration/agent-hook-types' -import type { PluginSources } from '../../../wsl-guest/plugin-sources' +import type { PluginSources } from '../../../wsl-guest/plugins/installation/plugin-sources' import { resolveWslHookRelayBundle, runWslInstallProcess, diff --git a/src/main/agent-hooks/wsl-relay/wsl-hook-relay-guest-install.ts b/src/main/agent-hooks/wsl-relay/wsl-hook-relay-guest-install.ts index 90a0bd09df2..a12e2a01f91 100644 --- a/src/main/agent-hooks/wsl-relay/wsl-hook-relay-guest-install.ts +++ b/src/main/agent-hooks/wsl-relay/wsl-hook-relay-guest-install.ts @@ -10,7 +10,7 @@ import { installWslGuestHooks } from './wsl-hook-fs-adapter' import { REINSTALL_MIN_INTERVAL_MS, type WslHookRelayManagerDeps } from './wsl-hook-relay-deps' import type { SshChannelMultiplexer } from '../../ssh/protocol/ssh-channel-multiplexer' import { openCodePluginSettingsKey } from '../installation/opencode-plugin-settings' -import type { PluginSources } from '../../../wsl-guest/plugin-sources' +import type { PluginSources } from '../../../wsl-guest/plugins/installation/plugin-sources' type GuestInstallDeps = { installHooks: typeof installRemoteManagedAgentHooks diff --git a/src/main/agent-hooks/wsl-relay/wsl-hook-relay-manager.test.ts b/src/main/agent-hooks/wsl-relay/wsl-hook-relay-manager.test.ts index 57d9ecb2877..ceb267c19f1 100644 --- a/src/main/agent-hooks/wsl-relay/wsl-hook-relay-manager.test.ts +++ b/src/main/agent-hooks/wsl-relay/wsl-hook-relay-manager.test.ts @@ -4,8 +4,8 @@ import { join } from 'node:path' import type { ChildProcessWithoutNullStreams } from 'node:child_process' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { RelayDispatcher } from '../../../wsl-guest/dispatcher' -import { registerWslHookFsHandlers } from '../../../wsl-guest/wsl-hook-fs-bridge' +import { RelayDispatcher } from '../../../wsl-guest/transport/publication/dispatcher' +import { registerWslHookFsHandlers } from '../../../wsl-guest/commands/wsl-hook-fs-bridge' import { SshChannelMultiplexer } from '../../ssh/protocol/ssh-channel-multiplexer' import type { MultiplexerTransport } from '../../ssh/protocol/ssh-multiplexer-transport-writer' import { createWslHookSftpAdapter } from './wsl-hook-fs-adapter' diff --git a/src/main/agent-hooks/wsl-relay/wsl-opencode-settings-lifetime.test.ts b/src/main/agent-hooks/wsl-relay/wsl-opencode-settings-lifetime.test.ts index 3e9d284e87b..a2406361d3a 100644 --- a/src/main/agent-hooks/wsl-relay/wsl-opencode-settings-lifetime.test.ts +++ b/src/main/agent-hooks/wsl-relay/wsl-opencode-settings-lifetime.test.ts @@ -4,9 +4,9 @@ import type { ChildProcessWithoutNullStreams } from 'node:child_process' import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { RelayDispatcher } from '../../../wsl-guest/dispatcher' -import { PluginOverlayManager } from '../../../wsl-guest/plugin-overlay' -import { createInstallPluginsHandler } from '../../../wsl-guest/wsl-install-plugins-handler' +import { RelayDispatcher } from '../../../wsl-guest/transport/publication/dispatcher' +import { PluginOverlayManager } from '../../../wsl-guest/plugins/overlays/plugin-overlay' +import { createInstallPluginsHandler } from '../../../wsl-guest/plugins/installation/wsl-install-plugins-handler' import { WslHookRelayManager } from './wsl-hook-relay-manager' import { REINSTALL_MIN_INTERVAL_MS } from './wsl-hook-relay-deps' import type { MultiplexerTransport } from '../../ssh/protocol/ssh-multiplexer-transport-writer' diff --git a/src/wsl-guest/agent-hook-result-retry-scheduler-muse-poll.test.ts b/src/wsl-guest/agent-hook-result-retry-scheduler-muse-poll.test.ts deleted file mode 100644 index 14dafd2b479..00000000000 --- a/src/wsl-guest/agent-hook-result-retry-scheduler-muse-poll.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { afterEach, expect, it, vi } from 'vitest' -import { appendFileSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { AgentHookResultRetryScheduler } from './agent-hook-result-retry-scheduler' -import { createHookListenerState } from '../shared/agent-hook-listener/listener-state' -import { normalizeHookPayload } from '../shared/agent-hook-listener/normalization/normalize-hook-payload' -import { upsertBoundedAgentHookStatus } from '../shared/agent-hook-listener/state/agent-hook-status-cache' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' - -const PANE_KEY = makePaneKey('tab-1', '11111111-1111-4111-8111-111111111111') -const SESSION_ID = '01a0caa3-0e77-7d41-bad7-46283a45633d' - -let dataHome: string | undefined -afterEach(() => { - vi.useRealTimers() - vi.unstubAllEnvs() - if (dataHome) { - rmSync(dataHome, { recursive: true, force: true }) - } -}) - -function createSessionLog(home: string): string { - const date = new Date(Number.parseInt(SESSION_ID.replace(/-/g, '').slice(0, 12), 16)) - const dir = join( - home, - 'muse', - 'sessions', - String(date.getFullYear()), - String(date.getMonth() + 1).padStart(2, '0'), - String(date.getDate()).padStart(2, '0'), - SESSION_ID - ) - mkdirSync(dir, { recursive: true }) - const logPath = join(dir, 'session.jsonl') - writeFileSync(logPath, '') - return logPath -} - -// A killed or quiet session writes no hook, so the poll backs off; a hook the listener ignores -// (PermissionRequest) must still restore the 1s poll so a log-only question shows promptly. -it('an ignored Muse hook after a quiet spell surfaces a log-only question within a second', async () => { - vi.useFakeTimers() - dataHome = mkdtempSync(join(tmpdir(), 'relay-muse-poll-')) - vi.stubEnv('XDG_DATA_HOME', dataHome) - const logPath = createSessionLog(dataHome) - const state = createHookListenerState() - const store = (event: NonNullable>) => { - upsertBoundedAgentHookStatus(state, event) - return state.lastStatusByPaneKey.get(event.paneKey) - } - const scheduler = new AgentHookResultRetryScheduler({ - state, - env: 'production', - isListening: () => true, - applyEvent: (event) => store(event) - }) - const body = { - paneKey: PANE_KEY, - tabId: 'tab-1', - worktreeId: 'wt-1', - payload: { - hook_event_name: 'UserPromptSubmit', - prompt: 'ask my favorite color', - session_id: SESSION_ID, - turn_id: 'e495d1a5-59aa-47b4-8efb-a1bd75509afc', - cwd: '/tmp/ws' - } - } - const hook = normalizeHookPayload(state, 'muse', body, 'production') - const stored = hook && store(hook) - if (!stored) { - throw new Error('missing Muse hook row') - } - scheduler.scheduleTranscriptPoll('muse', body, stored) - - await vi.advanceTimersByTimeAsync(31 * 60_000) - appendFileSync( - logPath, - `${JSON.stringify({ - payload: { - kind: 'run', - event: { - kind: 'user_input_prompt_requested', - prompt_id: '01a0caa3-a25a-7810-8229-4de04b2e7ca3', - questions: [{ id: 'fav_color', question: 'What is your favorite color?' }] - } - } - })}\n` - ) - scheduler.rearmIgnoredMuseHook({ - ...body, - payload: { ...body.payload, hook_event_name: 'PermissionRequest' } - }) - await vi.advanceTimersByTimeAsync(1_000) - expect(state.lastStatusByPaneKey.get(PANE_KEY)?.payload.state).toBe('waiting') - scheduler.clearAll() -}) diff --git a/src/wsl-guest/relay-agent-presence.test.ts b/src/wsl-guest/agent-hooks/presence/relay-agent-presence.test.ts similarity index 86% rename from src/wsl-guest/relay-agent-presence.test.ts rename to src/wsl-guest/agent-hooks/presence/relay-agent-presence.test.ts index fb40190840f..295eee215c7 100644 --- a/src/wsl-guest/relay-agent-presence.test.ts +++ b/src/wsl-guest/agent-hooks/presence/relay-agent-presence.test.ts @@ -2,23 +2,23 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { mkdtemp, rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' -import type * as RetrySchedulerModule from './agent-hook-result-retry-scheduler' -import type { AgentHookResultRetryHost } from './agent-hook-result-retry-scheduler' -import { RelayAgentHookServer } from './agent-hook-server' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' +import type * as RetrySchedulerModule from '../results/agent-hook-result-retry-scheduler' +import type { AgentHookResultRetryHost } from '../results/agent-hook-result-retry-scheduler' +import { RelayAgentHookServer } from '../server/agent-hook-server' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' const probe = vi.hoisted(() => vi.fn(async (): Promise<'live' | 'unverifiable' | 'exited'> => 'unverifiable') ) -vi.mock('../shared/agent-status/process/agent-process-presence-probe', () => ({ +vi.mock('../../../shared/agent-status/process/agent-process-presence-probe', () => ({ probeAgentProcessPresence: probe })) const retryHosts = vi.hoisted(() => { const hosts: AgentHookResultRetryHost[] = [] return hosts }) -vi.mock('./agent-hook-result-retry-scheduler', async (importOriginal) => { +vi.mock('../results/agent-hook-result-retry-scheduler', async (importOriginal) => { const actual = await importOriginal() return { AgentHookResultRetryScheduler: class extends actual.AgentHookResultRetryScheduler { diff --git a/src/wsl-guest/relay-agent-presence.ts b/src/wsl-guest/agent-hooks/presence/relay-agent-presence.ts similarity index 87% rename from src/wsl-guest/relay-agent-presence.ts rename to src/wsl-guest/agent-hooks/presence/relay-agent-presence.ts index 1acf9e07654..131a9b971f7 100644 --- a/src/wsl-guest/relay-agent-presence.ts +++ b/src/wsl-guest/agent-hooks/presence/relay-agent-presence.ts @@ -1,10 +1,10 @@ import { ClaudeSessionContinuationTracker, resolvePaneClaudeContinuation -} from '../shared/agent-hook-listener/claude-session-continuation' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' -import type { HookListenerState } from '../shared/agent-hook-listener/listener-state' -import { probeAgentProcessPresence } from '../shared/agent-status/process/agent-process-presence-probe' +} from '../../../shared/agent-hook-listener/claude-session-continuation' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' +import type { HookListenerState } from '../../../shared/agent-hook-listener/listener-state' +import { probeAgentProcessPresence } from '../../../shared/agent-status/process/agent-process-presence-probe' export class RelayAgentPresence { private readonly pending = new WeakMap>() diff --git a/src/wsl-guest/relay-hook-root-acquisition.test.ts b/src/wsl-guest/agent-hooks/presence/relay-hook-root-acquisition.test.ts similarity index 88% rename from src/wsl-guest/relay-hook-root-acquisition.test.ts rename to src/wsl-guest/agent-hooks/presence/relay-hook-root-acquisition.test.ts index 65616e96819..05d275f6f0d 100644 --- a/src/wsl-guest/relay-hook-root-acquisition.test.ts +++ b/src/wsl-guest/agent-hooks/presence/relay-hook-root-acquisition.test.ts @@ -2,16 +2,16 @@ import { afterEach, expect, it, vi } from 'vitest' import { mkdtemp, rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { RelayAgentHookServer } from './agent-hook-server' +import { RelayAgentHookServer } from '../server/agent-hook-server' import { createRelayPaneRootResolver } from './relay-pane-root' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' -import type { probeAgentProcessPresence } from '../shared/agent-status/process/agent-process-presence-probe' -import { readHookRootEvidence } from '../shared/agent-hook-listener/presence/hook-process-root-evidence' -import type { ProcessTableRow } from '../shared/process-table-snapshot' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' +import type { probeAgentProcessPresence } from '../../../shared/agent-status/process/agent-process-presence-probe' +import { readHookRootEvidence } from '../../../shared/agent-hook-listener/presence/hook-process-root-evidence' +import type { ProcessTableRow } from '../../../shared/process-table-snapshot' const probe = vi.hoisted(() => vi.fn(async () => 'live')) -vi.mock('../shared/agent-status/process/agent-process-presence-probe', () => ({ +vi.mock('../../../shared/agent-status/process/agent-process-presence-probe', () => ({ probeAgentProcessPresence: probe })) afterEach(() => probe.mockReset().mockResolvedValue('live')) diff --git a/src/wsl-guest/relay-http-owner-probe-lifecycle.test.ts b/src/wsl-guest/agent-hooks/presence/relay-http-owner-probe-lifecycle.test.ts similarity index 86% rename from src/wsl-guest/relay-http-owner-probe-lifecycle.test.ts rename to src/wsl-guest/agent-hooks/presence/relay-http-owner-probe-lifecycle.test.ts index 3bfc75edb7f..79c86b65080 100644 --- a/src/wsl-guest/relay-http-owner-probe-lifecycle.test.ts +++ b/src/wsl-guest/agent-hooks/presence/relay-http-owner-probe-lifecycle.test.ts @@ -2,13 +2,13 @@ import { afterEach, expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { RelayAgentHookServer } from './agent-hook-server' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import type { probeAgentProcessPresence } from '../shared/agent-status/process/agent-process-presence-probe' +import { RelayAgentHookServer } from '../server/agent-hook-server' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import type { probeAgentProcessPresence } from '../../../shared/agent-status/process/agent-process-presence-probe' const probe = vi.hoisted(() => vi.fn(async () => 'live')) -vi.mock('../shared/agent-status/process/agent-process-presence-probe', () => ({ +vi.mock('../../../shared/agent-status/process/agent-process-presence-probe', () => ({ probeAgentProcessPresence: probe })) afterEach(() => probe.mockReset().mockResolvedValue('live')) diff --git a/src/wsl-guest/relay-idle-process-owner.test.ts b/src/wsl-guest/agent-hooks/presence/relay-idle-process-owner.test.ts similarity index 95% rename from src/wsl-guest/relay-idle-process-owner.test.ts rename to src/wsl-guest/agent-hooks/presence/relay-idle-process-owner.test.ts index e0f6483aa99..83eebabae67 100644 --- a/src/wsl-guest/relay-idle-process-owner.test.ts +++ b/src/wsl-guest/agent-hooks/presence/relay-idle-process-owner.test.ts @@ -2,13 +2,13 @@ import { afterEach, expect, it, vi } from 'vitest' import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { RelayAgentHookServer } from './agent-hook-server' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' -import type { probeAgentProcessPresence } from '../shared/agent-status/process/agent-process-presence-probe' +import { RelayAgentHookServer } from '../server/agent-hook-server' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' +import type { probeAgentProcessPresence } from '../../../shared/agent-status/process/agent-process-presence-probe' const probe = vi.hoisted(() => vi.fn(async () => 'live')) -vi.mock('../shared/agent-status/process/agent-process-presence-probe', () => ({ +vi.mock('../../../shared/agent-status/process/agent-process-presence-probe', () => ({ probeAgentProcessPresence: probe })) afterEach(() => probe.mockReset().mockResolvedValue('live')) diff --git a/src/wsl-guest/relay-nested-same-provider-owner.test.ts b/src/wsl-guest/agent-hooks/presence/relay-nested-same-provider-owner.test.ts similarity index 84% rename from src/wsl-guest/relay-nested-same-provider-owner.test.ts rename to src/wsl-guest/agent-hooks/presence/relay-nested-same-provider-owner.test.ts index ccabcaef023..949031e3075 100644 --- a/src/wsl-guest/relay-nested-same-provider-owner.test.ts +++ b/src/wsl-guest/agent-hooks/presence/relay-nested-same-provider-owner.test.ts @@ -2,13 +2,13 @@ import { afterEach, expect, it, vi } from 'vitest' import { mkdtemp, rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { RelayAgentHookServer } from './agent-hook-server' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' -import type { probeAgentProcessPresence } from '../shared/agent-status/process/agent-process-presence-probe' +import { RelayAgentHookServer } from '../server/agent-hook-server' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' +import type { probeAgentProcessPresence } from '../../../shared/agent-status/process/agent-process-presence-probe' const probe = vi.hoisted(() => vi.fn(async () => 'live')) -vi.mock('../shared/agent-status/process/agent-process-presence-probe', () => ({ +vi.mock('../../../shared/agent-status/process/agent-process-presence-probe', () => ({ probeAgentProcessPresence: probe })) afterEach(() => probe.mockReset().mockResolvedValue('live')) diff --git a/src/wsl-guest/relay-pane-root.ts b/src/wsl-guest/agent-hooks/presence/relay-pane-root.ts similarity index 85% rename from src/wsl-guest/relay-pane-root.ts rename to src/wsl-guest/agent-hooks/presence/relay-pane-root.ts index 607cc05a5a3..d834b8a6e01 100644 --- a/src/wsl-guest/relay-pane-root.ts +++ b/src/wsl-guest/agent-hooks/presence/relay-pane-root.ts @@ -1,6 +1,6 @@ -import { probeAgentProcessPresence } from '../shared/agent-status/process/agent-process-presence-probe' -import type { HookPaneProcessIo } from '../shared/agent-hook-listener/presence/hook-process-root-evidence' -import { readWslGuestRoot, wslGuestRootIdentity } from '../shared/wsl-guest-root-marker' +import { probeAgentProcessPresence } from '../../../shared/agent-status/process/agent-process-presence-probe' +import type { HookPaneProcessIo } from '../../../shared/agent-hook-listener/presence/hook-process-root-evidence' +import { readWslGuestRoot, wslGuestRootIdentity } from '../../../shared/wsl-guest-root-marker' const METHOD_NOT_FOUND = -32601 diff --git a/src/wsl-guest/relay-tmux-hook-owner.ts b/src/wsl-guest/agent-hooks/presence/relay-tmux-hook-owner.ts similarity index 69% rename from src/wsl-guest/relay-tmux-hook-owner.ts rename to src/wsl-guest/agent-hooks/presence/relay-tmux-hook-owner.ts index 3f06a89f068..355ed303cf3 100644 --- a/src/wsl-guest/relay-tmux-hook-owner.ts +++ b/src/wsl-guest/agent-hooks/presence/relay-tmux-hook-owner.ts @@ -1,9 +1,12 @@ -import type { AgentStatusStore } from '../shared/agent-status/store/agent-status-store' -import { commitTmuxSelectedStatus, commitTmuxUnavailable } from '../shared/tmux-selected-status' -import { TmuxAgentHookOwner } from '../shared/tmux-agent-hook-owner' -import type { TmuxManagedPty } from '../shared/tmux-agent-hook-contract' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' -import type { AgentHookUnavailableEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' +import type { AgentStatusStore } from '../../../shared/agent-status/store/agent-status-store' +import { + commitTmuxSelectedStatus, + commitTmuxUnavailable +} from '../../../shared/tmux-selected-status' +import { TmuxAgentHookOwner } from '../../../shared/tmux-agent-hook-owner' +import type { TmuxManagedPty } from '../../../shared/tmux-agent-hook-contract' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' +import type { AgentHookUnavailableEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' export function createRelayTmuxHookOwner(options: { getRoot?: (paneKey: string) => Promise diff --git a/src/wsl-guest/agent-hook-envelope-build.ts b/src/wsl-guest/agent-hooks/publication/agent-hook-envelope-build.ts similarity index 88% rename from src/wsl-guest/agent-hook-envelope-build.ts rename to src/wsl-guest/agent-hooks/publication/agent-hook-envelope-build.ts index 7089e92549d..65f07ebcb6c 100644 --- a/src/wsl-guest/agent-hook-envelope-build.ts +++ b/src/wsl-guest/agent-hooks/publication/agent-hook-envelope-build.ts @@ -1,11 +1,11 @@ // Build SSH hook envelopes from parsed events/raw bodies; publication owns shedding and redelivery. -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' import type { AgentHookRelayEnvelope, AgentHookSource -} from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { hasSeparateHookSessionAdmission } from '../shared/agent-hook-listener/pane-provider-session' -import { pickHostTurnEnd } from '../shared/agent-status/contracts/agent-status-types' +} from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { hasSeparateHookSessionAdmission } from '../../../shared/agent-hook-listener/pane-provider-session' +import { pickHostTurnEnd } from '../../../shared/agent-status/contracts/agent-status-types' // Why: cap metadata to prevent a misbehaving CLI growing the cache unboundedly. const MAX_HOOK_META_LEN = 64 diff --git a/src/wsl-guest/agent-hook-envelope-publication.test.ts b/src/wsl-guest/agent-hooks/publication/agent-hook-envelope-publication.test.ts similarity index 97% rename from src/wsl-guest/agent-hook-envelope-publication.test.ts rename to src/wsl-guest/agent-hooks/publication/agent-hook-envelope-publication.test.ts index ed1d16aeb0a..33f0c5889f3 100644 --- a/src/wsl-guest/agent-hook-envelope-publication.test.ts +++ b/src/wsl-guest/agent-hooks/publication/agent-hook-envelope-publication.test.ts @@ -1,12 +1,13 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' -import { RelayDispatcher, type RelayClientSinkOptions } from './dispatcher' +import { RelayDispatcher } from '../../transport/publication/dispatcher' +import type { RelayClientSinkOptions } from '../../transport/writer/dispatcher-writer-sink' import { publishAgentHookEnvelope } from './agent-hook-envelope-publication' import { AGENT_HOOK_NOTIFICATION_METHOD, createShedSubagentsField -} from '../shared/agent-hook-listener/transport/agent-hook-relay' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import type { AgentSubagentSnapshot } from '../shared/agent-status/contracts/agent-status-subagent-snapshot' +} from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import type { AgentSubagentSnapshot } from '../../../shared/agent-status/contracts/agent-status-subagent-snapshot' type BoundedClient = { frames: Buffer[] diff --git a/src/wsl-guest/agent-hook-envelope-publication.ts b/src/wsl-guest/agent-hooks/publication/agent-hook-envelope-publication.ts similarity index 97% rename from src/wsl-guest/agent-hook-envelope-publication.ts rename to src/wsl-guest/agent-hooks/publication/agent-hook-envelope-publication.ts index 52659b6212f..903c3b57674 100644 --- a/src/wsl-guest/agent-hook-envelope-publication.ts +++ b/src/wsl-guest/agent-hooks/publication/agent-hook-envelope-publication.ts @@ -4,9 +4,9 @@ import { createShedSubagentsField, type AgentHookUnavailableEnvelope, type AgentHookRelayEnvelope -} from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { fitInteractivePrompt } from '../shared/agent-status/contracts/agent-status-field-normalization' -import type { RelayDispatcher } from './dispatcher' +} from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { fitInteractivePrompt } from '../../../shared/agent-status/contracts/agent-status-field-normalization' +import type { RelayDispatcher } from '../../transport/publication/dispatcher' // Shed reconstructible fields first; keep state/paneKey and preserve interactivePrompt longest so waiting panes remain answerable. // Mark shed rosters on the wire so clients restore cached subagents instead of permitting hibernation. diff --git a/src/wsl-guest/agent-hook-integration.test.ts b/src/wsl-guest/agent-hooks/publication/agent-hook-integration.test.ts similarity index 96% rename from src/wsl-guest/agent-hook-integration.test.ts rename to src/wsl-guest/agent-hooks/publication/agent-hook-integration.test.ts index e96cb764a55..177540875f4 100644 --- a/src/wsl-guest/agent-hook-integration.test.ts +++ b/src/wsl-guest/agent-hooks/publication/agent-hook-integration.test.ts @@ -4,15 +4,15 @@ import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { SshChannelMultiplexer } from '../main/ssh/protocol/ssh-channel-multiplexer' -import type { MultiplexerTransport } from '../main/ssh/protocol/ssh-multiplexer-transport-writer' -import { RelayDispatcher } from './dispatcher' -import { RelayAgentHookServer } from './agent-hook-server' +import { SshChannelMultiplexer } from '../../../main/ssh/protocol/ssh-channel-multiplexer' +import type { MultiplexerTransport } from '../../../main/ssh/protocol/ssh-multiplexer-transport-writer' +import { RelayDispatcher } from '../../transport/publication/dispatcher' +import { RelayAgentHookServer } from '../server/agent-hook-server' import { AGENT_HOOK_NOTIFICATION_METHOD, AGENT_HOOK_REQUEST_REPLAY_METHOD -} from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { AgentHookServer } from '../main/agent-hooks/server/agent-hook-server' +} from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { AgentHookServer } from '../../../main/agent-hooks/server/agent-hook-server' import { publishAgentHookEnvelope } from './agent-hook-envelope-publication' const LEAF_7 = '77777777-7777-4777-8777-777777777777' diff --git a/src/wsl-guest/agent-hook-result-retry-scheduler.ts b/src/wsl-guest/agent-hooks/results/agent-hook-result-retry-scheduler.ts similarity index 89% rename from src/wsl-guest/agent-hook-result-retry-scheduler.ts rename to src/wsl-guest/agent-hooks/results/agent-hook-result-retry-scheduler.ts index 9d2d6924b71..fd52f7bdf72 100644 --- a/src/wsl-guest/agent-hook-result-retry-scheduler.ts +++ b/src/wsl-guest/agent-hooks/results/agent-hook-result-retry-scheduler.ts @@ -1,15 +1,15 @@ -import { ClaudeHelperKillPoller } from '../shared/agent-hook-listener/providers/claude-helper-kill-poller' -import { pollCodexTranscriptStatus } from '../shared/agent-hook-listener/providers/codex-transcript-poll' +import { ClaudeHelperKillPoller } from '../../../shared/agent-hook-listener/providers/claude-helper-kill-poller' +import { pollCodexTranscriptStatus } from '../../../shared/agent-hook-listener/providers/codex-transcript-poll' // Retry late transcript results against the same cached event; one owner cancels both timer families before cache teardown. import { hasPendingAgentResultText, preparePendingGrokResultDiscovery -} from '../shared/agent-hook-listener/grok-result-discovery' -import { normalizeHookPayload } from '../shared/agent-hook-listener/normalization/normalize-hook-payload' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' -import type { HookListenerState } from '../shared/agent-hook-listener/listener-state' -import type { AgentHookSource } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { pickHostTurnEnd } from '../shared/agent-status/contracts/agent-status-types' +} from '../../../shared/agent-hook-listener/grok-result-discovery' +import { normalizeHookPayload } from '../../../shared/agent-hook-listener/normalization/normalize-hook-payload' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' +import type { HookListenerState } from '../../../shared/agent-hook-listener/listener-state' +import type { AgentHookSource } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { pickHostTurnEnd } from '../../../shared/agent-status/contracts/agent-status-types' import { museHookPollPane, museLogOffset, @@ -17,9 +17,9 @@ import { shouldPollHookTranscript, hookTranscriptWatchPath, transcriptPollUpdate -} from '../shared/agent-hook-listener/transcript-poll-policy' -import { AgentTranscriptPollScheduler } from '../shared/agent-hook-listener/transcripts/agent-transcript-poll-scheduler' -import { ClaudeOwedNotificationExpiryTimers } from '../shared/agent-hook-listener/providers/claude-owed-notification-expiry-timers' +} from '../../../shared/agent-hook-listener/transcript-poll-policy' +import { AgentTranscriptPollScheduler } from '../../../shared/agent-hook-listener/transcripts/agent-transcript-poll-scheduler' +import { ClaudeOwedNotificationExpiryTimers } from '../../../shared/agent-hook-listener/providers/claude-owed-notification-expiry-timers' const ASSISTANT_MESSAGE_RETRY_ATTEMPTS = 5 const ASSISTANT_MESSAGE_RETRY_MS = 50 diff --git a/src/wsl-guest/agent-hook-server-claude-owed-notification-expiry.test.ts b/src/wsl-guest/agent-hooks/results/agent-hook-server-claude-owed-notification-expiry.test.ts similarity index 89% rename from src/wsl-guest/agent-hook-server-claude-owed-notification-expiry.test.ts rename to src/wsl-guest/agent-hooks/results/agent-hook-server-claude-owed-notification-expiry.test.ts index 449dbc59249..da65dec02e0 100644 --- a/src/wsl-guest/agent-hook-server-claude-owed-notification-expiry.test.ts +++ b/src/wsl-guest/agent-hooks/results/agent-hook-server-claude-owed-notification-expiry.test.ts @@ -3,10 +3,10 @@ import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { CLAUDE_OWED_TASK_NOTIFICATION_LEASE_MS } from '../shared/agent-hook-listener/providers/claude-owed-task-notifications' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' -import { RelayAgentHookServer } from './agent-hook-server' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { CLAUDE_OWED_TASK_NOTIFICATION_LEASE_MS } from '../../../shared/agent-hook-listener/providers/claude-owed-task-notifications' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' +import { RelayAgentHookServer } from '../server/agent-hook-server' const PANE_KEY = makePaneKey('tab-1', '11111111-1111-4111-8111-111111111111') const SHELL = { id: 'b1', type: 'shell', status: 'running' } diff --git a/src/wsl-guest/agent-hook-endpoint-coordinates.ts b/src/wsl-guest/agent-hooks/server/agent-hook-endpoint-coordinates.ts similarity index 96% rename from src/wsl-guest/agent-hook-endpoint-coordinates.ts rename to src/wsl-guest/agent-hooks/server/agent-hook-endpoint-coordinates.ts index d92e8eba6b8..dde40695af5 100644 --- a/src/wsl-guest/agent-hook-endpoint-coordinates.ts +++ b/src/wsl-guest/agent-hooks/server/agent-hook-endpoint-coordinates.ts @@ -5,7 +5,7 @@ import { homedir } from 'node:os' import { ORCA_HOOK_PROTOCOL_VERSION, ORCA_HOOK_RAW_JSON_TRANSPORT -} from '../shared/agent-hook-listener/configuration/agent-hook-types' +} from '../../../shared/agent-hook-listener/configuration/agent-hook-types' // Use a per-user relay directory so shared hosts isolate endpoint files with mode 0700. const RELAY_HOOKS_DIR_NAME = '.orca-relay' diff --git a/src/wsl-guest/agent-hook-opencode-tui-identity.test.ts b/src/wsl-guest/agent-hooks/server/agent-hook-opencode-tui-identity.test.ts similarity index 71% rename from src/wsl-guest/agent-hook-opencode-tui-identity.test.ts rename to src/wsl-guest/agent-hooks/server/agent-hook-opencode-tui-identity.test.ts index d18dfe8dbbe..951818857aa 100644 --- a/src/wsl-guest/agent-hook-opencode-tui-identity.test.ts +++ b/src/wsl-guest/agent-hooks/server/agent-hook-opencode-tui-identity.test.ts @@ -1,18 +1,10 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { RelayAgentHookServer } from './agent-hook-server' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' -import { drainAgentHookSpoolAsync } from '../shared/agent-hook-listener/transport/agent-hook-spool' -import { createHookListenerState } from '../shared/agent-hook-listener/listener-state' -import { - bindOpenCodeSession, - trackOpenCodePaneLaunchToken -} from '../shared/agent-hook-listener/opencode-session-registry' -import { ingestRelayHookSpoolRecord } from './agent-hook-spool-ingest' -import { buildRelayHookEnvelope } from './agent-hook-envelope-build' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' const PANE_A = makePaneKey('tab-a', 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa') const PANE_B = makePaneKey('tab-b', 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb') @@ -180,58 +172,6 @@ describe('legacy TUI identity admitted by the execution-host relay', () => { expect(forward.mock.calls[0][0]).toMatchObject({ paneKey: PANE_C, launchToken: 'unknown-old' }) }) - it('fences real spool replay before borrowing owner identity and retains legacy metadata', async () => { - const state = createHookListenerState() - bindOpenCodeSession(state, 'ses_b', { paneKey: PANE_B, boundAt: 1, basis: 'tui' }) - trackOpenCodePaneLaunchToken(state, PANE_B, 'creator-live') - authority.set(PANE_C, 'viewer-new') - const spool = join(dir, 'spool') - mkdirSync(spool) - const file = join(spool, 'viewer.jsonl') - const record = { - paneKey: PANE_C, - source: 'opencode', - launchToken: 'viewer-old', - opencodeTui: 1, - receivedAt: Date.now(), - payload: { hook_event_name: 'SessionIdle', sessionID: 'ses_b' } - } - const writeRecord = (value: unknown) => writeFileSync(file, `${JSON.stringify(value)}\n`) - const host: Parameters[3] = { - apply: (event, source, env, version) => - forward(buildRelayHookEnvelope(event, source, env, version)), - isPaneSurfaceRetired: (paneKey: string) => retired.has(paneKey), - getAgentLaunchToken: (paneKey: string) => authority.get(paneKey) - } - const drain = () => - drainAgentHookSpoolAsync({ - endpointDir: dir, - getPersistedLaunchTokenHash: () => undefined, - ingest: (item) => ingestRelayHookSpoolRecord(item, state, 'remote', host) - }) - for (const launchToken of ['viewer-old', undefined, '']) { - writeRecord({ ...record, launchToken }) - expect(await drain()).toBe(1) - expect(readFileSync(file).length).toBe(0) - } - expect(forward).not.toHaveBeenCalled() - writeRecord({ ...record, launchToken: 'viewer-new' }) - await drain() - expect(forward.mock.calls[0][0]).toMatchObject({ paneKey: PANE_B, launchToken: 'creator-live' }) - forward.mockClear() - writeRecord({ - ...record, - opencodeTui: undefined, - opencodeSharedServer: 1, - payload: { hook_event_name: 'SessionIdle', sessionID: 'ses_unknown' } - }) - await drain() - expect(forward).not.toHaveBeenCalled() - writeRecord({ ...record, opencodeMajor: 2 }) - await drain() - expect(forward.mock.calls[0][0]).toMatchObject({ paneKey: PANE_C, launchToken: 'viewer-old' }) - }) - it('learns no identity from a rejected retired surface and admits its replacement', async () => { retired.add(PANE_B) await post(PANE_B, 'ses_old') diff --git a/src/wsl-guest/agent-hook-request.ts b/src/wsl-guest/agent-hooks/server/agent-hook-request.ts similarity index 77% rename from src/wsl-guest/agent-hook-request.ts rename to src/wsl-guest/agent-hooks/server/agent-hook-request.ts index cc84adf7120..f6d77501d23 100644 --- a/src/wsl-guest/agent-hook-request.ts +++ b/src/wsl-guest/agent-hooks/server/agent-hook-request.ts @@ -1,24 +1,24 @@ -import { checkHookOwnerPresence } from '../shared/agent-hook-listener/presence/agent-hook-owner-presence' -import type { HookRootEvidenceReader } from '../shared/agent-hook-listener/presence/hook-process-root-evidence' +import { checkHookOwnerPresence } from '../../../shared/agent-hook-listener/presence/agent-hook-owner-presence' +import type { HookRootEvidenceReader } from '../../../shared/agent-hook-listener/presence/hook-process-root-evidence' import type { IncomingMessage, ServerResponse } from 'node:http' -import { HOOK_REQUEST_SLOWLORIS_MS } from '../shared/agent-hook-listener/listener-limits' -import { mergeAgentHookRequestHeaders } from '../shared/agent-hook-listener/hook-envelope' -import { readRequestBody } from '../shared/agent-hook-listener/request-body' -import { resolveHookSource } from '../shared/agent-hook-listener/source-routing' -import type { createHookTransportInterferenceTracker } from '../shared/agent-hook-listener/transport/agent-hook-transport-interference' -import { isHookRequestTruncatedError } from '../shared/agent-hook-listener/transport/agent-hook-transport-interference' -import type { HookListenerState } from '../shared/agent-hook-listener/listener-state' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' -import type { AgentHookSource } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import type { AgentHookResultRetryScheduler } from './agent-hook-result-retry-scheduler' -import { hookBodyEnv, hookBodyVersion } from './agent-hook-envelope-build' -import { bindOpenCodeTuiSession } from '../shared/agent-hook-listener/opencode-session-registry' -import type { RelayProviderHookNormalization } from './agent-hook-provider-normalization' +import { HOOK_REQUEST_SLOWLORIS_MS } from '../../../shared/agent-hook-listener/listener-limits' +import { mergeAgentHookRequestHeaders } from '../../../shared/agent-hook-listener/hook-envelope' +import { readRequestBody } from '../../../shared/agent-hook-listener/request-body' +import { resolveHookSource } from '../../../shared/agent-hook-listener/source-routing' +import type { createHookTransportInterferenceTracker } from '../../../shared/agent-hook-listener/transport/agent-hook-transport-interference' +import { isHookRequestTruncatedError } from '../../../shared/agent-hook-listener/transport/agent-hook-transport-interference' +import type { HookListenerState } from '../../../shared/agent-hook-listener/listener-state' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' +import type { AgentHookSource } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import type { AgentHookResultRetryScheduler } from '../results/agent-hook-result-retry-scheduler' +import { hookBodyEnv, hookBodyVersion } from '../publication/agent-hook-envelope-build' +import { bindOpenCodeTuiSession } from '../../../shared/agent-hook-listener/opencode-session-registry' +import type { RelayProviderHookNormalization } from '../status/agent-hook-provider-normalization' import { relayHookRootAcquisition, type RelayHookAdmissionOptions -} from './agent-hook-event-admission' -import { CLAUDE_AGENT_VIEW_PATH } from '../shared/claude-agent-view' +} from '../status/agent-hook-event-admission' +import { CLAUDE_AGENT_VIEW_PATH } from '../../../shared/claude-agent-view' export async function handleRelayHookRequest( req: IncomingMessage, diff --git a/src/wsl-guest/agent-hook-server-bind.ts b/src/wsl-guest/agent-hooks/server/agent-hook-server-bind.ts similarity index 93% rename from src/wsl-guest/agent-hook-server-bind.ts rename to src/wsl-guest/agent-hooks/server/agent-hook-server-bind.ts index a1eb8695d94..0ffd1823f3f 100644 --- a/src/wsl-guest/agent-hook-server-bind.ts +++ b/src/wsl-guest/agent-hooks/server/agent-hook-server-bind.ts @@ -1,5 +1,5 @@ import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http' -import { listenForAgentHooks } from '../shared/agent-hook-listener/transport/agent-hook-startup' +import { listenForAgentHooks } from '../../../shared/agent-hook-listener/transport/agent-hook-startup' export async function bindRelayHookServer(options: { preferredPort: number diff --git a/src/wsl-guest/agent-hook-server-contract.ts b/src/wsl-guest/agent-hooks/server/agent-hook-server-contract.ts similarity index 84% rename from src/wsl-guest/agent-hook-server-contract.ts rename to src/wsl-guest/agent-hooks/server/agent-hook-server-contract.ts index bb728aceae9..891179ff5a7 100644 --- a/src/wsl-guest/agent-hook-server-contract.ts +++ b/src/wsl-guest/agent-hooks/server/agent-hook-server-contract.ts @@ -1,9 +1,9 @@ import type { AgentHookRelayEnvelope, AgentHookUnavailableEnvelope -} from '../shared/agent-hook-listener/transport/agent-hook-relay' -import type { TmuxManagedPty } from '../shared/tmux-agent-hook-contract' -import type { HookRootEvidenceReader } from '../shared/agent-hook-listener/presence/hook-process-root-evidence' +} from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import type { TmuxManagedPty } from '../../../shared/tmux-agent-hook-contract' +import type { HookRootEvidenceReader } from '../../../shared/agent-hook-listener/presence/hook-process-root-evidence' export type RelayHookForward = (envelope: AgentHookRelayEnvelope) => void diff --git a/src/wsl-guest/agent-hook-server.test.ts b/src/wsl-guest/agent-hooks/server/agent-hook-server.test.ts similarity index 97% rename from src/wsl-guest/agent-hook-server.test.ts rename to src/wsl-guest/agent-hooks/server/agent-hook-server.test.ts index cb432c5db41..74ad0c47374 100644 --- a/src/wsl-guest/agent-hook-server.test.ts +++ b/src/wsl-guest/agent-hooks/server/agent-hook-server.test.ts @@ -3,10 +3,10 @@ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'nod import { tmpdir } from 'node:os' import { join } from 'node:path' import { RelayAgentHookServer } from './agent-hook-server' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' -import * as agentHookListener from '../shared/agent-hook-listener/grok-result-discovery' -import { HOOK_REQUEST_MAX_BYTES } from '../shared/agent-hook-listener/request-body' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' +import * as agentHookListener from '../../../shared/agent-hook-listener/grok-result-discovery' +import { HOOK_REQUEST_MAX_BYTES } from '../../../shared/agent-hook-listener/request-body' const LEAF_ID = '11111111-1111-4111-8111-111111111111' const PANE_KEY = makePaneKey('tab-1', LEAF_ID) diff --git a/src/wsl-guest/agent-hook-server.ts b/src/wsl-guest/agent-hooks/server/agent-hook-server.ts similarity index 88% rename from src/wsl-guest/agent-hook-server.ts rename to src/wsl-guest/agent-hooks/server/agent-hook-server.ts index 10cc6d78d53..2ac948284d2 100644 --- a/src/wsl-guest/agent-hook-server.ts +++ b/src/wsl-guest/agent-hooks/server/agent-hook-server.ts @@ -1,19 +1,20 @@ -import { AgentHookStartup } from '../shared/agent-hook-listener/transport/agent-hook-startup' +import { AgentHookStartup } from '../../../shared/agent-hook-listener/transport/agent-hook-startup' import { bindRelayHookServer } from './agent-hook-server-bind' import { inferRelayClaudeInterrupt, type RelayInterruptHost -} from './agent-hook-interrupt-reconciliation' -import { applyRelayHookEvent, type RelayHookAdmissionOptions } from './agent-hook-event-admission' -import { RelayProviderHookNormalization } from './agent-hook-provider-normalization' -import { RelayAgentHookCanonicalStatus } from './agent-hook-canonical-status' +} from '../status/agent-hook-interrupt-reconciliation' +import { applyRelayHookEvent } from '../status/agent-hook-event-admission' +import type { RelayHookAdmissionOptions } from '../status/agent-hook-event-admission' +import { RelayProviderHookNormalization } from '../status/agent-hook-provider-normalization' +import { RelayAgentHookCanonicalStatus } from '../status/agent-hook-canonical-status' import type { RelayHookForward, RelayHookServerOptions, RelayHookServerStartOptions } from './agent-hook-server-contract' import { handleRelayHookRequest } from './agent-hook-request' -import { RelayAgentPresence } from './relay-agent-presence' +import { RelayAgentPresence } from '../presence/relay-agent-presence' import type { createServer } from 'node:http' import { randomUUID } from 'node:crypto' import { join } from 'node:path' @@ -21,32 +22,32 @@ import { join } from 'node:path' import { ORCA_HOOK_PROTOCOL_VERSION, ORCA_HOOK_RAW_JSON_TRANSPORT -} from '../shared/agent-hook-listener/configuration/agent-hook-types' +} from '../../../shared/agent-hook-listener/configuration/agent-hook-types' import { clearAllListenerCaches, clearPaneCacheState, createHookListenerState -} from '../shared/agent-hook-listener/listener-state' +} from '../../../shared/agent-hook-listener/listener-state' import { getEndpointFileName, writeEndpointFile -} from '../shared/agent-hook-listener/transport/endpoint-publication' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' +} from '../../../shared/agent-hook-listener/transport/endpoint-publication' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' import { createHookTransportInterferenceTracker, describeHookTransportInterference -} from '../shared/agent-hook-listener/transport/agent-hook-transport-interference' -import { REMOTE_AGENT_HOOK_ENV } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import type { AgentHookSource } from '../shared/agent-hook-listener/transport/agent-hook-relay' +} from '../../../shared/agent-hook-listener/transport/agent-hook-transport-interference' +import { REMOTE_AGENT_HOOK_ENV } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import type { AgentHookSource } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' import { buildRelayHookPtyEnv, defaultEndpointDir } from './agent-hook-endpoint-coordinates' import { drainRelayHookSpool } from './agent-hook-spool-ingest' -import { AgentHookResultRetryScheduler } from './agent-hook-result-retry-scheduler' +import { AgentHookResultRetryScheduler } from '../results/agent-hook-result-retry-scheduler' import { buildCachedPaneReplayEnvelope, selectReplayableCachedPanes, type CachedPaneEnvelopeMeta -} from './agent-hook-cached-pane-status' -import { createRelayClaudeTerminalInterrupts } from './claude-terminal-interrupt-status' +} from '../status/agent-hook-cached-pane-status' +import { createRelayClaudeTerminalInterrupts } from '../status/claude-terminal-interrupt-status' export class RelayAgentHookServer extends RelayAgentHookCanonicalStatus { private server: ReturnType | null = null diff --git a/src/wsl-guest/agent-hook-spool-ingest.ts b/src/wsl-guest/agent-hooks/server/agent-hook-spool-ingest.ts similarity index 77% rename from src/wsl-guest/agent-hook-spool-ingest.ts rename to src/wsl-guest/agent-hooks/server/agent-hook-spool-ingest.ts index f5b045cad21..9ce51c8e21f 100644 --- a/src/wsl-guest/agent-hook-spool-ingest.ts +++ b/src/wsl-guest/agent-hooks/server/agent-hook-spool-ingest.ts @@ -1,25 +1,25 @@ import { relayHookRootAcquisition, type RelayHookAdmissionOptions -} from './agent-hook-event-admission' +} from '../status/agent-hook-event-admission' import { admitRelayOpenCodeTui } from './agent-hook-request' -import { checkHookOwnerPresence } from '../shared/agent-hook-listener/presence/agent-hook-owner-presence' -import type { HookRootEvidenceReader } from '../shared/agent-hook-listener/presence/hook-process-root-evidence' -import { normalizeHookPayload } from '../shared/agent-hook-listener/normalization/normalize-hook-payload' -import type { HookListenerState } from '../shared/agent-hook-listener/listener-state' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' +import { checkHookOwnerPresence } from '../../../shared/agent-hook-listener/presence/agent-hook-owner-presence' +import type { HookRootEvidenceReader } from '../../../shared/agent-hook-listener/presence/hook-process-root-evidence' +import { normalizeHookPayload } from '../../../shared/agent-hook-listener/normalization/normalize-hook-payload' +import type { HookListenerState } from '../../../shared/agent-hook-listener/listener-state' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' import { isAgentHookSource, type AgentHookSource -} from '../shared/agent-hook-listener/transport/agent-hook-relay' +} from '../../../shared/agent-hook-listener/transport/agent-hook-relay' import { buildSpoolHookBody, drainAgentHookSpoolAsync, type SpoolRecord -} from '../shared/agent-hook-listener/transport/agent-hook-spool' -import { hookBodyEnv, hookBodyVersion } from './agent-hook-envelope-build' -import { hookRootAnswerDeadline } from '../shared/agent-hook-listener/presence/hook-root-attempts' -import type { RelayProviderHookNormalization } from './agent-hook-provider-normalization' +} from '../../../shared/agent-hook-listener/transport/agent-hook-spool' +import { hookBodyEnv, hookBodyVersion } from '../publication/agent-hook-envelope-build' +import { hookRootAnswerDeadline } from '../../../shared/agent-hook-listener/presence/hook-root-attempts' +import type { RelayProviderHookNormalization } from '../status/agent-hook-provider-normalization' type RelayHookSpoolHost = { isCurrent?: () => boolean diff --git a/src/wsl-guest/wsl-agent-hook-relay.test.ts b/src/wsl-guest/agent-hooks/server/wsl-agent-hook-relay.test.ts similarity index 97% rename from src/wsl-guest/wsl-agent-hook-relay.test.ts rename to src/wsl-guest/agent-hooks/server/wsl-agent-hook-relay.test.ts index ad875bec173..6c1602241f9 100644 --- a/src/wsl-guest/wsl-agent-hook-relay.test.ts +++ b/src/wsl-guest/agent-hooks/server/wsl-agent-hook-relay.test.ts @@ -5,8 +5,8 @@ import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { RelayAgentHookServer } from './agent-hook-server' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' -import { sanitizeWslHookInstanceKey } from '../shared/wsl-hook-relay-contract' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' +import { sanitizeWslHookInstanceKey } from '../../../shared/wsl-hook-relay-contract' const LEAF_ID = '22222222-2222-4222-8222-222222222222' const PANE_KEY = makePaneKey('tab-2', LEAF_ID) diff --git a/src/wsl-guest/agent-hook-cached-pane-status.ts b/src/wsl-guest/agent-hooks/status/agent-hook-cached-pane-status.ts similarity index 84% rename from src/wsl-guest/agent-hook-cached-pane-status.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-cached-pane-status.ts index 2e8f5fe5529..d52383fa49b 100644 --- a/src/wsl-guest/agent-hook-cached-pane-status.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-cached-pane-status.ts @@ -1,10 +1,10 @@ -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' import type { AgentHookRelayEnvelope, AgentHookSource -} from '../shared/agent-hook-listener/transport/agent-hook-relay' -import type { ClaudeSettledRequest } from '../shared/agent-hook-listener/claude-settled-permission-requests' -import { buildRelayHookEnvelope } from './agent-hook-envelope-build' +} from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import type { ClaudeSettledRequest } from '../../../shared/agent-hook-listener/claude-settled-permission-requests' +import { buildRelayHookEnvelope } from '../publication/agent-hook-envelope-build' export type CachedPaneEnvelopeMeta = { source: AgentHookSource diff --git a/src/wsl-guest/agent-hook-canonical-status.test.ts b/src/wsl-guest/agent-hooks/status/agent-hook-canonical-status.test.ts similarity index 95% rename from src/wsl-guest/agent-hook-canonical-status.test.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-canonical-status.test.ts index 24c2b8e0104..9bd280d7ecb 100644 --- a/src/wsl-guest/agent-hook-canonical-status.test.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-canonical-status.test.ts @@ -2,15 +2,15 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { RelayAgentHookServer } from './agent-hook-server' +import { RelayAgentHookServer } from '../server/agent-hook-server' import { TMUX_TEST_PANE, TMUX_TEST_ROOT, TMUX_TEST_ROWS, tmuxTestBody -} from '../shared/tmux-status.test-fixture' +} from '../../../shared/tmux-status.test-fixture' const probe = vi.hoisted(() => vi.fn()) -vi.mock('../shared/tmux-host-attachment-probe', () => ({ probeTmuxHostAttachments: probe })) +vi.mock('../../../shared/tmux-host-attachment-probe', () => ({ probeTmuxHostAttachments: probe })) class CanonicalTestServer extends RelayAgentHookServer { snapshot() { return this.canonicalStatusStore.getSnapshot() diff --git a/src/wsl-guest/agent-hook-canonical-status.ts b/src/wsl-guest/agent-hooks/status/agent-hook-canonical-status.ts similarity index 84% rename from src/wsl-guest/agent-hook-canonical-status.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-canonical-status.ts index 04e1df184cc..61af78070ec 100644 --- a/src/wsl-guest/agent-hook-canonical-status.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-canonical-status.ts @@ -1,16 +1,16 @@ import { randomUUID } from 'node:crypto' -import { createAgentStatusStore } from '../shared/agent-status/store/agent-status-store' -import type { TmuxAgentHookOwner } from '../shared/tmux-agent-hook-owner' +import { createAgentStatusStore } from '../../../shared/agent-status/store/agent-status-store' +import type { TmuxAgentHookOwner } from '../../../shared/tmux-agent-hook-owner' import { isTmuxInnerSubject, readTmuxUnavailable, tmuxCanonicalStatusEvent -} from '../shared/tmux-selected-status' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' -import type { AgentHookSource } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { createRelayTmuxHookOwner } from './relay-tmux-hook-owner' -import { buildRelayHookEnvelope } from './agent-hook-envelope-build' -import type { RelayHookServerOptions } from './agent-hook-server-contract' +} from '../../../shared/tmux-selected-status' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' +import type { AgentHookSource } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { createRelayTmuxHookOwner } from '../presence/relay-tmux-hook-owner' +import { buildRelayHookEnvelope } from '../publication/agent-hook-envelope-build' +import type { RelayHookServerOptions } from '../server/agent-hook-server-contract' /** The hook server owns the authority; legacy ingress cannot write canonical pane subjects. */ export class RelayAgentHookCanonicalStatus { diff --git a/src/wsl-guest/agent-hook-event-admission.ts b/src/wsl-guest/agent-hooks/status/agent-hook-event-admission.ts similarity index 77% rename from src/wsl-guest/agent-hook-event-admission.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-event-admission.ts index b202864ad9d..984efd78cbd 100644 --- a/src/wsl-guest/agent-hook-event-admission.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-event-admission.ts @@ -1,24 +1,24 @@ -import { previewHookPresence } from '../shared/agent-hook-listener/presence/agent-hook-presence-transition' +import { previewHookPresence } from '../../../shared/agent-hook-listener/presence/agent-hook-presence-transition' import { hasOtherAgentProcessOwner, isSameAgentProcess -} from '../shared/agent-status/process/agent-process-presence' +} from '../../../shared/agent-status/process/agent-process-presence' import { readHookProcessEvidence, restateHookRoot -} from '../shared/agent-hook-listener/presence/agent-hook-owner-presence' -import type { HookRootAcquisition } from '../shared/agent-hook-listener/presence/hook-root-attempts' -import type { HookRootEvidenceReader } from '../shared/agent-hook-listener/presence/hook-process-root-evidence' -import { sameAgentStatusTerminalOwner } from '../shared/agent-status/identity/agent-status-terminal-owner' -import { cacheRelayLegacyAgentStatus } from '../shared/agent-status/transport/agent-status-legacy-relay-cache' -import type { HookListenerState } from '../shared/agent-hook-listener/listener-state' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' -import type { AgentHookSource } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { retainClaudeSettledRequests } from '../shared/agent-hook-listener/claude-settled-permission-requests' -import { reduceHostTurnEnd } from '../shared/agent-hook-listener/state/agent-hook-turn-end-timing' -import { previousCodexTurnContext } from '../shared/agent-hook-listener/providers/codex-session-start' -import { buildRelayHookEnvelope } from './agent-hook-envelope-build' -import type { RelayHookForward } from './agent-hook-server-contract' +} from '../../../shared/agent-hook-listener/presence/agent-hook-owner-presence' +import type { HookRootAcquisition } from '../../../shared/agent-hook-listener/presence/hook-root-attempts' +import type { HookRootEvidenceReader } from '../../../shared/agent-hook-listener/presence/hook-process-root-evidence' +import { sameAgentStatusTerminalOwner } from '../../../shared/agent-status/identity/agent-status-terminal-owner' +import { cacheRelayLegacyAgentStatus } from '../../../shared/agent-status/transport/agent-status-legacy-relay-cache' +import type { HookListenerState } from '../../../shared/agent-hook-listener/listener-state' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' +import type { AgentHookSource } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { retainClaudeSettledRequests } from '../../../shared/agent-hook-listener/claude-settled-permission-requests' +import { reduceHostTurnEnd } from '../../../shared/agent-hook-listener/state/agent-hook-turn-end-timing' +import { previousCodexTurnContext } from '../../../shared/agent-hook-listener/providers/codex-session-start' +import { buildRelayHookEnvelope } from '../publication/agent-hook-envelope-build' +import type { RelayHookForward } from '../server/agent-hook-server-contract' import { MAX_CACHED_PANES, type CachedPaneEnvelopeMeta } from './agent-hook-cached-pane-status' import { reconcileRelayClaudeCancel, diff --git a/src/wsl-guest/agent-hook-interrupt-reconciliation.test.ts b/src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.test.ts similarity index 91% rename from src/wsl-guest/agent-hook-interrupt-reconciliation.test.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.test.ts index bfeba56e4f1..e5128524247 100644 --- a/src/wsl-guest/agent-hook-interrupt-reconciliation.test.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.test.ts @@ -1,13 +1,13 @@ -import '../main/agent-hooks/server/testing/memory-agent-identity-store.test-fixture' +import '../../../main/agent-hooks/server/testing/memory-agent-identity-store.test-fixture' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { describe, expect, it, vi } from 'vitest' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import type { RemoteAgentInterruptRequest } from '../shared/agent-hook-listener/transport/agent-hook-interrupt-reconciliation' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' -import { AGENT_STATUS_STALE_AFTER_MS } from '../shared/agent-status/contracts/agent-status-freshness' -import { RelayAgentHookServer } from './agent-hook-server' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import type { RemoteAgentInterruptRequest } from '../../../shared/agent-hook-listener/transport/agent-hook-interrupt-reconciliation' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' +import { AGENT_STATUS_STALE_AFTER_MS } from '../../../shared/agent-status/contracts/agent-status-freshness' +import { RelayAgentHookServer } from '../server/agent-hook-server' const PANE = makePaneKey('tab-1', '11111111-1111-4111-8111-111111111111') diff --git a/src/wsl-guest/agent-hook-interrupt-reconciliation.ts b/src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.ts similarity index 82% rename from src/wsl-guest/agent-hook-interrupt-reconciliation.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.ts index a68d3e1a7fa..94a1bdb7b89 100644 --- a/src/wsl-guest/agent-hook-interrupt-reconciliation.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.ts @@ -1,22 +1,22 @@ import { randomUUID } from 'node:crypto' -import { AGENT_STATUS_STALE_AFTER_MS } from '../shared/agent-status/contracts/agent-status-freshness' -import { isRecord } from '../shared/agent-status/child-work/agent-status-child-work-value-guards' -import { normalizeAgentProviderSession } from '../shared/agent-session/lifecycle/agent-session-resume' -import { normalizeHostTurnRevision } from '../shared/agent-hook-listener/transport/agent-hook-interrupt-reconciliation' +import { AGENT_STATUS_STALE_AFTER_MS } from '../../../shared/agent-status/contracts/agent-status-freshness' +import { isRecord } from '../../../shared/agent-status/child-work/agent-status-child-work-value-guards' +import { normalizeAgentProviderSession } from '../../../shared/agent-session/lifecycle/agent-session-resume' +import { normalizeHostTurnRevision } from '../../../shared/agent-hook-listener/transport/agent-hook-interrupt-reconciliation' import { opensNewTurn, restatesAnotherPrompt, resolveCancelVerdictLatch, type CancelVerdictLatchDecision -} from '../shared/agent-hook-listener/state/agent-hook-cancel-verdict-latch' +} from '../../../shared/agent-hook-listener/state/agent-hook-cancel-verdict-latch' import { markClaudeLeadTurnInterrupted, setClaudeMainAgentTurnState -} from '../shared/agent-hook-listener/providers/claude-roster-state' -import { claudeRowHasUnlistedLiveWork } from '../shared/agent-hook-listener/providers/claude-pane-hold-evidence' -import type { HookListenerState } from '../shared/agent-hook-listener/listener-state' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' -import type { AgentHookSource } from '../shared/agent-hook-listener/transport/agent-hook-relay' +} from '../../../shared/agent-hook-listener/providers/claude-roster-state' +import { claudeRowHasUnlistedLiveWork } from '../../../shared/agent-hook-listener/providers/claude-pane-hold-evidence' +import type { HookListenerState } from '../../../shared/agent-hook-listener/listener-state' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' +import type { AgentHookSource } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' import type { CachedPaneEnvelopeMeta } from './agent-hook-cached-pane-status' export type RelayInterruptHost = { diff --git a/src/wsl-guest/agent-hook-provider-normalization.test.ts b/src/wsl-guest/agent-hooks/status/agent-hook-provider-normalization.test.ts similarity index 93% rename from src/wsl-guest/agent-hook-provider-normalization.test.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-provider-normalization.test.ts index 5b8edac5967..7cf594b7d41 100644 --- a/src/wsl-guest/agent-hook-provider-normalization.test.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-provider-normalization.test.ts @@ -2,12 +2,12 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { RelayAgentHookServer } from './agent-hook-server' +import { RelayAgentHookServer } from '../server/agent-hook-server' import type { AgentHookRelayEnvelope, AgentHookSource -} from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' +} from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' const PANE = makePaneKey('tab-1', '11111111-1111-4111-8111-111111111111') diff --git a/src/wsl-guest/agent-hook-provider-normalization.ts b/src/wsl-guest/agent-hooks/status/agent-hook-provider-normalization.ts similarity index 78% rename from src/wsl-guest/agent-hook-provider-normalization.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-provider-normalization.ts index 3e6da952cf8..262b9084e51 100644 --- a/src/wsl-guest/agent-hook-provider-normalization.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-provider-normalization.ts @@ -1,19 +1,19 @@ -import { hasOtherAgentProcessOwner } from '../shared/agent-status/process/agent-process-presence' +import { hasOtherAgentProcessOwner } from '../../../shared/agent-status/process/agent-process-presence' import { isOwnerProviderGuestHook, readHookProcessVerdict -} from '../shared/agent-hook-listener/presence/agent-hook-owner-presence' -import { retainHookRootContext } from '../shared/agent-hook-listener/pane-provider-session' -import { sameAgentStatusTerminalOwner } from '../shared/agent-status/identity/agent-status-terminal-owner' -import { normalizeHookPayload } from '../shared/agent-hook-listener/normalization/normalize-hook-payload' +} from '../../../shared/agent-hook-listener/presence/agent-hook-owner-presence' +import { retainHookRootContext } from '../../../shared/agent-hook-listener/pane-provider-session' +import { sameAgentStatusTerminalOwner } from '../../../shared/agent-status/identity/agent-status-terminal-owner' +import { normalizeHookPayload } from '../../../shared/agent-hook-listener/normalization/normalize-hook-payload' import { clearPaneCacheState, createHookListenerState, type HookListenerState -} from '../shared/agent-hook-listener/listener-state' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' -import type { AgentHookSource } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { cacheRelayLegacyAgentStatus } from '../shared/agent-status/transport/agent-status-legacy-relay-cache' +} from '../../../shared/agent-hook-listener/listener-state' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' +import type { AgentHookSource } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { cacheRelayLegacyAgentStatus } from '../../../shared/agent-status/transport/agent-status-legacy-relay-cache' import { MAX_CACHED_PANES } from './agent-hook-cached-pane-status' /** Forward every provider; only the client knows about desktop-only owner completion. */ diff --git a/src/wsl-guest/agent-hook-server-claude-session-continuation.test.ts b/src/wsl-guest/agent-hooks/status/agent-hook-server-claude-session-continuation.test.ts similarity index 88% rename from src/wsl-guest/agent-hook-server-claude-session-continuation.test.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-server-claude-session-continuation.test.ts index 8de47c664ef..1b847e94d5c 100644 --- a/src/wsl-guest/agent-hook-server-claude-session-continuation.test.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-server-claude-session-continuation.test.ts @@ -3,13 +3,13 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { RelayAgentHookServer } from './agent-hook-server' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import type { probeAgentProcessPresence } from '../shared/agent-status/process/agent-process-presence-probe' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' +import { RelayAgentHookServer } from '../server/agent-hook-server' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import type { probeAgentProcessPresence } from '../../../shared/agent-status/process/agent-process-presence-probe' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' const probe = vi.hoisted(() => vi.fn(async () => 'live')) -vi.mock('../shared/agent-status/process/agent-process-presence-probe', () => ({ +vi.mock('../../../shared/agent-status/process/agent-process-presence-probe', () => ({ probeAgentProcessPresence: probe })) diff --git a/src/wsl-guest/agent-hook-server-codex-side-thread.test.ts b/src/wsl-guest/agent-hooks/status/agent-hook-server-codex-side-thread.test.ts similarity index 94% rename from src/wsl-guest/agent-hook-server-codex-side-thread.test.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-server-codex-side-thread.test.ts index ccc0b75ed9a..bf724a6a659 100644 --- a/src/wsl-guest/agent-hook-server-codex-side-thread.test.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-server-codex-side-thread.test.ts @@ -2,9 +2,9 @@ import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { expect, it } from 'vitest' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { RelayAgentHookServer } from './agent-hook-server' -import { AgentHookServer } from '../main/agent-hooks/server/agent-hook-server' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { RelayAgentHookServer } from '../server/agent-hook-server' +import { AgentHookServer } from '../../../main/agent-hooks/server/agent-hook-server' const PANE_KEY = 'tab-1:11111111-1111-4111-8111-111111111111' diff --git a/src/wsl-guest/agent-hook-server-codex-subagent-transcript.test.ts b/src/wsl-guest/agent-hooks/status/agent-hook-server-codex-subagent-transcript.test.ts similarity index 91% rename from src/wsl-guest/agent-hook-server-codex-subagent-transcript.test.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-server-codex-subagent-transcript.test.ts index bddb0d4c7a3..07c75b0d4a7 100644 --- a/src/wsl-guest/agent-hook-server-codex-subagent-transcript.test.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-server-codex-subagent-transcript.test.ts @@ -3,9 +3,9 @@ import { appendFileSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { RelayAgentHookServer } from './agent-hook-server' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { makePaneKey } from '../shared/workspace-layout/stable-pane-id' +import { RelayAgentHookServer } from '../server/agent-hook-server' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' const PANE_KEY = makePaneKey('tab-1', '11111111-1111-4111-8111-111111111111') const CHILD_ID = '019fa65f-3144-7151-9c02-cff7a28f316f' diff --git a/src/wsl-guest/agent-hook-server-codex-turn-interruption.test.ts b/src/wsl-guest/agent-hooks/status/agent-hook-server-codex-turn-interruption.test.ts similarity index 92% rename from src/wsl-guest/agent-hook-server-codex-turn-interruption.test.ts rename to src/wsl-guest/agent-hooks/status/agent-hook-server-codex-turn-interruption.test.ts index dce3104861d..1fa78b46350 100644 --- a/src/wsl-guest/agent-hook-server-codex-turn-interruption.test.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-server-codex-turn-interruption.test.ts @@ -2,9 +2,9 @@ import { expect, it, vi } from 'vitest' import { appendFileSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import type { AgentHookRelayEnvelope } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { RelayAgentHookServer } from './agent-hook-server' -import { AgentHookServer } from '../main/agent-hooks/server/agent-hook-server' +import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' +import { RelayAgentHookServer } from '../server/agent-hook-server' +import { AgentHookServer } from '../../../main/agent-hooks/server/agent-hook-server' const PANE_KEY = 'tab-1:11111111-1111-4111-8111-111111111111' const line = (payload: unknown): string => `${JSON.stringify({ type: 'event_msg', payload })}\n` diff --git a/src/wsl-guest/claude-terminal-interrupt-status.ts b/src/wsl-guest/agent-hooks/status/claude-terminal-interrupt-status.ts similarity index 71% rename from src/wsl-guest/claude-terminal-interrupt-status.ts rename to src/wsl-guest/agent-hooks/status/claude-terminal-interrupt-status.ts index d56db6e5e21..a426acac378 100644 --- a/src/wsl-guest/claude-terminal-interrupt-status.ts +++ b/src/wsl-guest/agent-hooks/status/claude-terminal-interrupt-status.ts @@ -1,7 +1,7 @@ -import { ClaudeTerminalInterruptTracker } from '../shared/claude-terminal-interrupt' -import type { HookListenerState } from '../shared/agent-hook-listener/listener-state' -import type { AgentHookEventPayload } from '../shared/agent-hook-listener/listener-event' -import { AGENT_STATUS_STALE_AFTER_MS } from '../shared/agent-status/contracts/agent-status-freshness' +import { ClaudeTerminalInterruptTracker } from '../../../shared/claude-terminal-interrupt' +import type { HookListenerState } from '../../../shared/agent-hook-listener/listener-state' +import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' +import { AGENT_STATUS_STALE_AFTER_MS } from '../../../shared/agent-status/contracts/agent-status-freshness' import { applyRelayClaudeInterrupt, type RelayInterruptHost diff --git a/src/wsl-guest/preflight-handler.test.ts b/src/wsl-guest/commands/preflight-handler.test.ts similarity index 94% rename from src/wsl-guest/preflight-handler.test.ts rename to src/wsl-guest/commands/preflight-handler.test.ts index d935e89297d..567c7691231 100644 --- a/src/wsl-guest/preflight-handler.test.ts +++ b/src/wsl-guest/commands/preflight-handler.test.ts @@ -1,6 +1,6 @@ -import type * as WslAvailability from '../main/wsl/discovery/wsl-availability' +import type * as WslAvailability from '../../main/wsl/discovery/wsl-availability' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { buildPosixCommandPathLookupScript } from '../shared/posix-command-path-lookup' +import { buildPosixCommandPathLookupScript } from '../../shared/posix-command-path-lookup' const { execFileAsyncMock, runProcessMock } = vi.hoisted(() => ({ execFileAsyncMock: vi.fn(), @@ -26,19 +26,19 @@ vi.mock('child_process', () => { return { execFile: execFileWithPromisify } }) -vi.mock('../main/pty/shell-startup/pwsh', () => ({ +vi.mock('../../main/pty/shell-startup/pwsh', () => ({ isPwshAvailableAsync: isPwshAvailableAsyncMock })) -vi.mock('../main/wsl/discovery/wsl-availability', async (importOriginal) => ({ +vi.mock('../../main/wsl/discovery/wsl-availability', async (importOriginal) => ({ ...(await importOriginal()), ...(await (() => ({ isWslAvailableAsync: isWslAvailableAsyncMock }))()) })) -vi.mock('../main/wsl/discovery/distro-catalog', () => ({ +vi.mock('../../main/wsl/discovery/distro-catalog', () => ({ listWslDistrosAsync: listWslDistrosAsyncMock })) -vi.mock('../main/pty/shell-startup/git-bash', () => ({ +vi.mock('../../main/pty/shell-startup/git-bash', () => ({ isGitBashAvailable: isGitBashAvailableMock })) vi.mock('@orca/process-host', () => ({ runProcess: runProcessMock })) diff --git a/src/wsl-guest/preflight-handler.ts b/src/wsl-guest/commands/preflight-handler.ts similarity index 95% rename from src/wsl-guest/preflight-handler.ts rename to src/wsl-guest/commands/preflight-handler.ts index d043db27b02..72927bda567 100644 --- a/src/wsl-guest/preflight-handler.ts +++ b/src/wsl-guest/commands/preflight-handler.ts @@ -2,13 +2,13 @@ import { execFile } from 'node:child_process' import { userInfo } from 'node:os' import { promisify } from 'node:util' import path, { win32 } from 'node:path' -import type { RelayDispatcher } from './dispatcher' +import type { RelayDispatcher } from '../transport/publication/dispatcher' import { buildRelayCommandEnv } from './relay-command-env' -import { isPwshAvailableAsync } from '../main/pty/shell-startup/pwsh' -import { isWslAvailableAsync } from '../main/wsl/discovery/wsl-availability' -import { listWslDistrosAsync } from '../main/wsl/discovery/distro-catalog' -import { isGitBashAvailable } from '../main/pty/shell-startup/git-bash' -import { buildPosixCommandPathLookupScript } from '../shared/posix-command-path-lookup' +import { isPwshAvailableAsync } from '../../main/pty/shell-startup/pwsh' +import { isWslAvailableAsync } from '../../main/wsl/discovery/wsl-availability' +import { listWslDistrosAsync } from '../../main/wsl/discovery/distro-catalog' +import { isGitBashAvailable } from '../../main/pty/shell-startup/git-bash' +import { buildPosixCommandPathLookupScript } from '../../shared/posix-command-path-lookup' import { runProcess } from '@orca/process-host' const execFileAsync = promisify(execFile) diff --git a/src/wsl-guest/relay-command-env.test.ts b/src/wsl-guest/commands/relay-command-env.test.ts similarity index 100% rename from src/wsl-guest/relay-command-env.test.ts rename to src/wsl-guest/commands/relay-command-env.test.ts diff --git a/src/wsl-guest/relay-command-env.ts b/src/wsl-guest/commands/relay-command-env.ts similarity index 96% rename from src/wsl-guest/relay-command-env.ts rename to src/wsl-guest/commands/relay-command-env.ts index 575771871c8..b9216d9062f 100644 --- a/src/wsl-guest/relay-command-env.ts +++ b/src/wsl-guest/commands/relay-command-env.ts @@ -1,7 +1,7 @@ import { homedir } from 'node:os' import { posix, win32 } from 'node:path' -import { gitCredentialPromptGuardEnv } from '../shared/git/commands/git-credential-prompt-env' -import { UNTRANSLATED_GIT_OUTPUT_ENV } from '../shared/git/commands/git-output-locale' +import { gitCredentialPromptGuardEnv } from '../../shared/git/commands/git-credential-prompt-env' +import { UNTRANSLATED_GIT_OUTPUT_ENV } from '../../shared/git/commands/git-output-locale' const POSIX_RELAY_PATH_FALLBACKS = ['/usr/local/bin', '/opt/homebrew/bin', '/usr/bin', '/bin'] const WINDOWS_RELAY_PATH_FALLBACKS = [ diff --git a/src/wsl-guest/wsl-hook-fs-bridge.test.ts b/src/wsl-guest/commands/wsl-hook-fs-bridge.test.ts similarity index 94% rename from src/wsl-guest/wsl-hook-fs-bridge.test.ts rename to src/wsl-guest/commands/wsl-hook-fs-bridge.test.ts index 4fdbfd28a87..875a45a2c40 100644 --- a/src/wsl-guest/wsl-hook-fs-bridge.test.ts +++ b/src/wsl-guest/commands/wsl-hook-fs-bridge.test.ts @@ -5,8 +5,9 @@ import { tmpdir } from 'node:os' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { registerWslHookFsHandlers } from './wsl-hook-fs-bridge' -import type { MethodHandler, RelayDispatcher, RequestContext } from './dispatcher' -import { WSL_HOOK_FS_METHODS, type WslFsResult } from '../shared/wsl-hook-relay-contract' +import type { MethodHandler, RequestContext } from '../transport/connection/dispatcher-contract' +import type { RelayDispatcher } from '../transport/publication/dispatcher' +import { WSL_HOOK_FS_METHODS, type WslFsResult } from '../../shared/wsl-hook-relay-contract' describe.skipIf(process.platform === 'win32')('registerWslHookFsHandlers (WSL fs bridge)', () => { let home: string diff --git a/src/wsl-guest/wsl-hook-fs-bridge.ts b/src/wsl-guest/commands/wsl-hook-fs-bridge.ts similarity index 97% rename from src/wsl-guest/wsl-hook-fs-bridge.ts rename to src/wsl-guest/commands/wsl-hook-fs-bridge.ts index 870d5778cbb..5e52198c9d6 100644 --- a/src/wsl-guest/wsl-hook-fs-bridge.ts +++ b/src/wsl-guest/commands/wsl-hook-fs-bridge.ts @@ -2,12 +2,12 @@ import { promises as fs } from 'node:fs' import { posix } from 'node:path' -import type { RelayDispatcher } from './dispatcher' +import type { RelayDispatcher } from '../transport/publication/dispatcher' import { WSL_HOOK_FS_METHODS, type WslFsFailure, type WslFsResult -} from '../shared/wsl-hook-relay-contract' +} from '../../shared/wsl-hook-relay-contract' function failure(err: unknown): WslFsFailure { const e = err as NodeJS.ErrnoException diff --git a/src/wsl-guest/dispatcher-bulk-failed-admission.test.ts b/src/wsl-guest/dispatcher-bulk-failed-admission.test.ts deleted file mode 100644 index 3d207b0b1b9..00000000000 --- a/src/wsl-guest/dispatcher-bulk-failed-admission.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { expect, it } from 'vitest' -import { RelayDispatcher } from './dispatcher' -import type { PreparedRelayFrame, RelayClient } from './dispatcher-contract' -import type { SinkWriteSettlement } from './dispatcher-client-writer' - -class FailedAdmissionDispatcher extends RelayDispatcher { - attempts = 0 - - wake(): void { - this.notifyLegacyCapacityIfLow() - } - - listeners(): number { - return this.legacyCapacityListeners.size - } - - protected override publishPreparedToClient( - client: RelayClient, - frame: PreparedRelayFrame, - lane: 'interactive' | 'ordinary' | 'fixed-bulk' | 'bulk', - onSettled: (result: SinkWriteSettlement) => void = () => {} - ): boolean { - this.attempts++ - if (this.attempts === 1) { - return false - } - if (this.attempts === 2) { - this.notifyLegacyCapacityIfLow() - return false - } - if (this.attempts > 6) { - throw new Error('Admission retry did not remain bounded') - } - return super.publishPreparedToClient(client, frame, lane, onSettled) - } -} - -it('preserves a capacity wake during a failed admission without needing another event', async () => { - let writes = 0 - const dispatcher = new FailedAdmissionDispatcher(() => { - writes++ - return true - }) - try { - const completion = dispatcher.notifyBulk('git.responseChunk', { streamId: 1, seq: 0 }) - await new Promise((resolve) => setImmediate(resolve)) - expect(dispatcher.attempts).toBe(1) - dispatcher.wake() - expect(dispatcher.attempts).toBe(3) - await completion - expect(writes).toBe(1) - expect(dispatcher.listeners()).toBe(0) - } finally { - dispatcher.dispose() - } -}) diff --git a/src/wsl-guest/dispatcher-frame-guard-regressions.test.ts b/src/wsl-guest/dispatcher-frame-guard-regressions.test.ts deleted file mode 100644 index 6574579d4ee..00000000000 --- a/src/wsl-guest/dispatcher-frame-guard-regressions.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { RelayDispatcher } from './dispatcher' -import type { RelayClient } from './dispatcher-contract' -import type { JsonRpcNotification } from './protocol' - -type DispatcherInternals = { - primaryClient: RelayClient - estimateFrameBytes: (msg: JsonRpcNotification) => number - enqueueFrame: (client: RelayClient, msg: JsonRpcNotification, lane: string) => boolean -} - -describe('RelayDispatcher frame guards', () => { - it('returns zero for invalid active-client limits without encoding', () => { - const dispatcher = new RelayDispatcher(() => true, { - writableHighWaterMark: () => 1024 * 1024, - writableLength: () => 0 - }) - try { - const spy = vi.spyOn(dispatcher as unknown as DispatcherInternals, 'estimateFrameBytes') - for (const limit of [0, -1, Number.NaN]) { - expect(dispatcher.maxLegacyPtyDataChars({ id: 'pty-1' }, 'hello', limit)).toBe(0) - } - expect(spy).not.toHaveBeenCalled() - } finally { - dispatcher.dispose() - } - }) - - it('does not estimate frames after disposal', () => { - const dispatcher = new RelayDispatcher(() => true) - const internals = dispatcher as unknown as DispatcherInternals - const spy = vi.spyOn(internals, 'estimateFrameBytes') - const msg: JsonRpcNotification = { - jsonrpc: '2.0', - method: 'pty.data', - params: { - data: { - toJSON: () => { - throw new Error('must not serialize') - } - } - } - } - dispatcher.dispose() - expect(internals.enqueueFrame(internals.primaryClient, msg, 'ordinary')).toBe(false) - expect(spy).not.toHaveBeenCalled() - }) - - it('does not serialize notifications without an active client', () => { - const dispatcher = new RelayDispatcher(() => true) - try { - dispatcher.invalidateClient() - - expect(() => - dispatcher.notify('test.event', { - data: { - toJSON: () => { - throw new Error('must not serialize') - } - } - }) - ).not.toThrow() - } finally { - dispatcher.dispose() - } - }) - - it('does not serialize pty.data rejected by every active client', () => { - const dispatcher = new RelayDispatcher(() => true) - const unregister = dispatcher.registerPtyDataPublicationAdmission(() => false) - try { - expect(() => - dispatcher.notify('pty.data', { - data: { - toJSON: () => { - throw new Error('must not serialize') - } - } - }) - ).not.toThrow() - } finally { - unregister() - dispatcher.dispose() - } - }) -}) diff --git a/src/wsl-guest/dispatcher-per-connection-state-baseline.test.ts b/src/wsl-guest/dispatcher-per-connection-state-baseline.test.ts deleted file mode 100644 index 04d97ece6e8..00000000000 --- a/src/wsl-guest/dispatcher-per-connection-state-baseline.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { describe, expect, it, vi, afterEach } from 'vitest' -import { RelayDispatcher } from './dispatcher' - -// Count retained containers after churn; exact state counts prove release without timing-dependent assertions. - -type Probed = { - attachClient: RelayDispatcher['attachClient'] - detachClient: (id: number) => void - onClientCapacity: (id: number, listener: () => void) => (() => void) | null - clients: Map - requestHandlers: Map - notificationHandlers: Map - requestAborts: { - byClient: Map> - create: (clientId: number, requestId: number) => unknown - } - publicationLedger: { clientBytes: Map; aggregateBytes: number } - pendingRelayRequests: Map - clientDetachListeners: Set - disposeListeners: Set - legacyCapacityListeners: Set - clientCapacityListeners: Map - ptyDataPublicationAdmission: unknown - keepaliveTimer: unknown - activeClients: () => unknown[] - tryPublishToClients: (clients: unknown[], msg: unknown, lane: string) => boolean - dispose: () => void -} - -function countAbortControllers(d: Probed): number { - let total = 0 - for (const bucket of d.requestAborts.byClient.values()) { - total += bucket.size - } - return total -} - -function census(d: Probed) { - return { - clients: d.clients.size, - requestHandlers: d.requestHandlers.size, - notificationHandlers: d.notificationHandlers.size, - requestAbortControllers: countAbortControllers(d), - ledgerClientBytes: d.publicationLedger.clientBytes.size, - ledgerAggregateBytes: d.publicationLedger.aggregateBytes, - pendingRelayRequests: d.pendingRelayRequests.size, - clientDetachListeners: d.clientDetachListeners.size, - disposeListeners: d.disposeListeners.size, - legacyCapacityListeners: d.legacyCapacityListeners.size, - clientCapacityListeners: d.clientCapacityListeners.size, - ptyDataPublicationAdmission: d.ptyDataPublicationAdmission === null ? 'null' : 'set', - keepaliveTimer: d.keepaliveTimer === null ? 'null' : 'armed' - } -} - -function newDispatcher(): Probed { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Probed names the protected containers this census counts. RelayDispatcher really has them; the compiler just will not hand them out. - return new RelayDispatcher(() => {}) as unknown as Probed -} - -const CLIENTS_PER_CYCLE = 100 - -describe('relay dispatcher per-connection state', () => { - afterEach(() => vi.useRealTimers()) - - it('returns every per-connection container to baseline across repeated churn', () => { - vi.useFakeTimers() - const d = newDispatcher() - const baseline = census(d) - - for (let cycle = 0; cycle < 3; cycle++) { - const ids: number[] = [] - for (let i = 0; i < CLIENTS_PER_CYCLE; i++) { - ids.push(d.attachClient(() => true, { supportsWriteCallback: true })) - } - for (const id of ids) { - d.onClientCapacity(id, () => {}) - d.requestAborts.create(id, 1) - } - - expect(census(d).clients).toBe(CLIENTS_PER_CYCLE + 1) - expect(census(d).clientCapacityListeners).toBe(CLIENTS_PER_CYCLE) - expect(census(d).requestAbortControllers).toBe(CLIENTS_PER_CYCLE) - - expect( - d.tryPublishToClients( - d.activeClients(), - { jsonrpc: '2.0', method: 'pty.data', params: { d: 'x'.repeat(256) } }, - 'bulk' - ) - ).toBe(true) - // Pending sink callbacks retain publication leases until detach closes each writer. - expect(census(d).ledgerClientBytes).toBe(CLIENTS_PER_CYCLE) - expect(census(d).ledgerAggregateBytes).toBeGreaterThan(baseline.ledgerAggregateBytes) - for (const id of ids) { - d.detachClient(id) - } - expect(census(d)).toEqual(baseline) - } - d.dispose() - }) -}) diff --git a/src/wsl-guest/dispatcher.ts b/src/wsl-guest/dispatcher.ts deleted file mode 100644 index dbf70abd906..00000000000 --- a/src/wsl-guest/dispatcher.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { RelayDispatcherNotificationPublication } from './dispatcher-notification-publication' - -export type { - RelayClientSinkOptions, - RelayClientWrite, - SinkWriteSettlement -} from './dispatcher-client-writer' -export type { - MethodHandler, - NotificationHandler, - PtyDataPublicationAdmission, - RelayClientSessionIdentity, - RelayClientSourceOptions, - RequestContext -} from './dispatcher-contract' - -export class RelayDispatcher extends RelayDispatcherNotificationPublication {} diff --git a/src/wsl-guest/opencode-startup-prompt-install.test.ts b/src/wsl-guest/plugins/installation/opencode-startup-prompt-install.test.ts similarity index 98% rename from src/wsl-guest/opencode-startup-prompt-install.test.ts rename to src/wsl-guest/plugins/installation/opencode-startup-prompt-install.test.ts index 71cb373511e..8c0e0036107 100644 --- a/src/wsl-guest/opencode-startup-prompt-install.test.ts +++ b/src/wsl-guest/plugins/installation/opencode-startup-prompt-install.test.ts @@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { PluginOverlayManager } from './plugin-overlay' +import { PluginOverlayManager } from '../overlays/plugin-overlay' import { createInstallPluginsHandler } from './wsl-install-plugins-handler' let root: string diff --git a/src/wsl-guest/plugin-source-limit.ts b/src/wsl-guest/plugins/installation/plugin-source-limit.ts similarity index 100% rename from src/wsl-guest/plugin-source-limit.ts rename to src/wsl-guest/plugins/installation/plugin-source-limit.ts diff --git a/src/wsl-guest/plugin-sources.ts b/src/wsl-guest/plugins/installation/plugin-sources.ts similarity index 100% rename from src/wsl-guest/plugin-sources.ts rename to src/wsl-guest/plugins/installation/plugin-sources.ts diff --git a/src/wsl-guest/wsl-install-plugins-handler.test.ts b/src/wsl-guest/plugins/installation/wsl-install-plugins-handler.test.ts similarity index 99% rename from src/wsl-guest/wsl-install-plugins-handler.test.ts rename to src/wsl-guest/plugins/installation/wsl-install-plugins-handler.test.ts index a1d972cc903..16085d9947a 100644 --- a/src/wsl-guest/wsl-install-plugins-handler.test.ts +++ b/src/wsl-guest/plugins/installation/wsl-install-plugins-handler.test.ts @@ -4,7 +4,7 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { describe, expect, it } from 'vitest' -import { PluginOverlayManager } from './plugin-overlay' +import { PluginOverlayManager } from '../overlays/plugin-overlay' import { createInstallPluginsHandler } from './wsl-install-plugins-handler' import { PLUGIN_SOURCE_MAX_BYTES } from './plugin-source-limit' diff --git a/src/wsl-guest/wsl-install-plugins-handler.ts b/src/wsl-guest/plugins/installation/wsl-install-plugins-handler.ts similarity index 96% rename from src/wsl-guest/wsl-install-plugins-handler.ts rename to src/wsl-guest/plugins/installation/wsl-install-plugins-handler.ts index 3ab0ef18325..f2161d225cb 100644 --- a/src/wsl-guest/wsl-install-plugins-handler.ts +++ b/src/wsl-guest/plugins/installation/wsl-install-plugins-handler.ts @@ -1,14 +1,14 @@ // Cache host-shipped sources and install guest overlays without binding the hook listener. import { existsSync } from 'node:fs' -import { getRelayOpenCodePluginPath, type PluginOverlayManager } from './plugin-overlay' -import { resolveOpenCodeSourceConfigDir } from './plugin-overlay-env' -import { resolveOpenCodeConfigDirectory } from '../shared/opencode-config-directory' +import { getRelayOpenCodePluginPath, type PluginOverlayManager } from '../overlays/plugin-overlay' +import { resolveOpenCodeSourceConfigDir } from '../overlays/plugin-overlay-env' +import { resolveOpenCodeConfigDirectory } from '../../../shared/opencode-config-directory' import { assertPluginSourceUnderByteCap } from './plugin-source-limit' import { sanitizeWslHookInstanceKey, WSL_HOOK_RELAY_INSTANCE_ENV -} from '../shared/wsl-hook-relay-contract' +} from '../../../shared/wsl-hook-relay-contract' export type InstallPluginsResult = { installed: { diff --git a/src/wsl-guest/opencode-canonical-config.ts b/src/wsl-guest/plugins/opencode/opencode-canonical-config.ts similarity index 81% rename from src/wsl-guest/opencode-canonical-config.ts rename to src/wsl-guest/plugins/opencode/opencode-canonical-config.ts index 21ed93a3d76..7e86b6741b6 100644 --- a/src/wsl-guest/opencode-canonical-config.ts +++ b/src/wsl-guest/plugins/opencode/opencode-canonical-config.ts @@ -1,9 +1,9 @@ import { existsSync, mkdirSync } from 'node:fs' import { isAbsolute, join, relative, resolve } from 'node:path' -import { resolveOpenCodeConfigDirectory } from '../shared/opencode-config-directory' -import { isInstalledOpenCodePluginCurrent } from '../shared/opencode-installed-plugin' -import { writeCanonicalOpenCodePluginAtomically } from '../shared/opencode-plugin-atomic-write' -import { writeOpenCodeTuiPlugin } from '../shared/opencode-tui-plugin-install' +import { resolveOpenCodeConfigDirectory } from '../../../shared/opencode-config-directory' +import { isInstalledOpenCodePluginCurrent } from '../../../shared/opencode-installed-plugin' +import { writeCanonicalOpenCodePluginAtomically } from '../../../shared/opencode-plugin-atomic-write' +import { writeOpenCodeTuiPlugin } from '../../../shared/opencode-tui-plugin-install' const RELAY_HOOKS_DIR = '.orca-relay' diff --git a/src/wsl-guest/opencode-overlay-mirror.ts b/src/wsl-guest/plugins/opencode/opencode-overlay-mirror.ts similarity index 94% rename from src/wsl-guest/opencode-overlay-mirror.ts rename to src/wsl-guest/plugins/opencode/opencode-overlay-mirror.ts index 2e1582c02da..266180cbbcb 100644 --- a/src/wsl-guest/opencode-overlay-mirror.ts +++ b/src/wsl-guest/plugins/opencode/opencode-overlay-mirror.ts @@ -1,6 +1,6 @@ import { mkdirSync, readdirSync, realpathSync, statSync } from 'node:fs' import { join } from 'node:path' -import { mirrorEntry } from '../main/pty/scrollback/overlay-mirror' +import { mirrorEntry } from '../../../main/pty/scrollback/overlay-mirror' export function mirrorOpenCodeConfig( sourceDir: string, diff --git a/src/wsl-guest/claude-agent-view-plugin.ts b/src/wsl-guest/plugins/overlays/claude-agent-view-plugin.ts similarity index 86% rename from src/wsl-guest/claude-agent-view-plugin.ts rename to src/wsl-guest/plugins/overlays/claude-agent-view-plugin.ts index 67703d21390..7bf2c666700 100644 --- a/src/wsl-guest/claude-agent-view-plugin.ts +++ b/src/wsl-guest/plugins/overlays/claude-agent-view-plugin.ts @@ -3,8 +3,8 @@ import { join } from 'node:path' import { installClaudeAgentView, removeClaudeAgentView -} from '../main/claude/agent-view/agent-view-install' -import { ensureClaudeAgentViewPlugin } from '../main/claude/agent-view/agent-view-plugin' +} from '../../../main/claude/agent-view/agent-view-install' +import { ensureClaudeAgentViewPlugin } from '../../../main/claude/agent-view/agent-view-plugin' export class RelayClaudeAgentViewPlugin { private source = '' diff --git a/src/wsl-guest/omp-config-root.test.ts b/src/wsl-guest/plugins/overlays/omp-config-root.test.ts similarity index 93% rename from src/wsl-guest/omp-config-root.test.ts rename to src/wsl-guest/plugins/overlays/omp-config-root.test.ts index ff805f3513d..12120ab7856 100644 --- a/src/wsl-guest/omp-config-root.test.ts +++ b/src/wsl-guest/plugins/overlays/omp-config-root.test.ts @@ -4,8 +4,8 @@ import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { PluginOverlayManager } from './plugin-overlay' import { resolveOmpConfigDirName, resolvePiSourceAgentDir } from './plugin-overlay-env' -import { __resetShellStartupEnvCache } from '../main/pty/shell-startup/shell-startup-env' -import { resetLoginShellEnvironmentCacheForTests } from '../main/startup/shell-environment/login-shell-environment' +import { __resetShellStartupEnvCache } from '../../../main/pty/shell-startup/shell-startup-env' +import { resetLoginShellEnvironmentCacheForTests } from '../../../main/startup/shell-environment/login-shell-environment' describe('relay OMP config root', () => { let home: string diff --git a/src/wsl-guest/omp-status-extension.ts b/src/wsl-guest/plugins/overlays/omp-status-extension.ts similarity index 100% rename from src/wsl-guest/omp-status-extension.ts rename to src/wsl-guest/plugins/overlays/omp-status-extension.ts diff --git a/src/wsl-guest/plugin-overlay-env.test.ts b/src/wsl-guest/plugins/overlays/plugin-overlay-env.test.ts similarity index 97% rename from src/wsl-guest/plugin-overlay-env.test.ts rename to src/wsl-guest/plugins/overlays/plugin-overlay-env.test.ts index 23024008a7d..1201c354775 100644 --- a/src/wsl-guest/plugin-overlay-env.test.ts +++ b/src/wsl-guest/plugins/overlays/plugin-overlay-env.test.ts @@ -2,7 +2,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it } from 'vitest' -import { __resetShellStartupEnvCache } from '../main/pty/shell-startup/shell-startup-env' +import { __resetShellStartupEnvCache } from '../../../main/pty/shell-startup/shell-startup-env' import { resolveOpenCodeSourceConfigDir, resolvePiSourceAgentDir } from './plugin-overlay-env' describe('plugin overlay env source resolution', () => { diff --git a/src/wsl-guest/plugin-overlay-env.ts b/src/wsl-guest/plugins/overlays/plugin-overlay-env.ts similarity index 90% rename from src/wsl-guest/plugin-overlay-env.ts rename to src/wsl-guest/plugins/overlays/plugin-overlay-env.ts index 57b71d1f445..81b342b76b3 100644 --- a/src/wsl-guest/plugin-overlay-env.ts +++ b/src/wsl-guest/plugins/overlays/plugin-overlay-env.ts @@ -1,10 +1,10 @@ -import { resolveLoginShellEnvironment } from '../main/startup/shell-environment/login-shell-environment' -import { readSessionShellStartupEnvVar } from '../main/pty/shell-startup/shell-startup-env' +import { resolveLoginShellEnvironment } from '../../../main/startup/shell-environment/login-shell-environment' +import { readSessionShellStartupEnvVar } from '../../../main/pty/shell-startup/shell-startup-env' import { PRIMARY_AGENT_DIR_ENV_BY_KIND, SOURCE_AGENT_DIR_ENV_BY_KIND, type PiAgentKind -} from '../shared/pi-agent-kind' +} from '../../../shared/pi-agent-kind' function firstNonEmpty(...values: (string | undefined)[]): string | undefined { return values.find((value) => typeof value === 'string' && value.length > 0) diff --git a/src/wsl-guest/plugin-overlay.test.ts b/src/wsl-guest/plugins/overlays/plugin-overlay.test.ts similarity index 100% rename from src/wsl-guest/plugin-overlay.test.ts rename to src/wsl-guest/plugins/overlays/plugin-overlay.test.ts diff --git a/src/wsl-guest/plugin-overlay.ts b/src/wsl-guest/plugins/overlays/plugin-overlay.ts similarity index 94% rename from src/wsl-guest/plugin-overlay.ts rename to src/wsl-guest/plugins/overlays/plugin-overlay.ts index 819c009d347..f0646652bd5 100644 --- a/src/wsl-guest/plugin-overlay.ts +++ b/src/wsl-guest/plugins/overlays/plugin-overlay.ts @@ -1,32 +1,31 @@ -import type { PluginSources, MaterializePiResult } from './plugin-sources' -export type { PluginSources, MaterializePiResult } from './plugin-sources' +import type { PluginSources, MaterializePiResult } from '../installation/plugin-sources' import { RelayClaudeAgentViewPlugin } from './claude-agent-view-plugin' -import { mirrorOpenCodeConfig } from './opencode-overlay-mirror' +import { mirrorOpenCodeConfig } from '../opencode/opencode-overlay-mirror' import { writeOpenCodeStartupPromptPlugin, OPENCODE_STARTUP_PROMPT_PLUGIN_DIRECTORY -} from '../shared/opencode-startup-prompt-install' -import { resolveOpenCodeConfigDirectory } from '../shared/opencode-config-directory' -import { materializeOmpFreshConfig } from '../shared/agent-launch/startup/omp-fresh-config' +} from '../../../shared/opencode-startup-prompt-install' +import { resolveOpenCodeConfigDirectory } from '../../../shared/opencode-config-directory' +import { materializeOmpFreshConfig } from '../../../shared/agent-launch/startup/omp-fresh-config' // Install remotely because host paths and Electron services are unavailable to the relay. // Receive plugin source at session-ready: relay and desktop update independently, so bundled copies could stay stale. import { createHash } from 'node:crypto' import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' -import { writeOverlayOpenCodePluginAtomically } from '../shared/opencode-plugin-atomic-write' +import { writeOverlayOpenCodePluginAtomically } from '../../../shared/opencode-plugin-atomic-write' import { homedir } from 'node:os' import { join } from 'node:path' -import { safeRemoveOverlay } from '../main/pty/scrollback/overlay-mirror' -import type { PiAgentKind } from '../shared/pi-agent-kind' +import { safeRemoveOverlay } from '../../../main/pty/scrollback/overlay-mirror' +import type { PiAgentKind } from '../../../shared/pi-agent-kind' import { installOpenCodePluginInCanonicalConfig, isRelayOpenCodeOverlayPath, type OpenCodeAgent -} from './opencode-canonical-config' +} from '../opencode/opencode-canonical-config' import { writeRelayOmpStatusExtension } from './omp-status-extension' import { openCodeTuiPluginDirName, writeOpenCodeTuiPlugin -} from '../shared/opencode-tui-plugin-install' +} from '../../../shared/opencode-tui-plugin-install' type LegacyOverlayAgentKind = Exclude const RELAY_HOOKS_DIR = '.orca-relay' const OPENCODE_OVERLAY_SUBDIR = 'opencode-overlays' diff --git a/src/wsl-guest/relay-frame-decoder.ts b/src/wsl-guest/relay-frame-decoder.ts deleted file mode 100644 index 14a60adb9fa..00000000000 --- a/src/wsl-guest/relay-frame-decoder.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { FrameDecoder as SharedFrameDecoder } from '../shared/relay-frame-decoder' -import type { DecodedFrame, FrameDecoderOptions } from '../shared/relay-frame-decoder-contract' - -export { - HEADER_LENGTH, - MAX_MESSAGE_SIZE, - FRAME_DECODER_MAX_FRAMES_PER_TURN, - FRAME_DECODER_MAX_BYTES_PER_TURN, - FRAME_DECODER_MAX_TURN_MS, - FRAME_DECODER_MAX_RETAINED_BYTES -} from '../shared/relay-frame-decoder' -export { - FrameDecoderContinuationError, - type DecodedFrame, - type FrameDecoderOptions -} from '../shared/relay-frame-decoder-contract' - -// Default faults to stderr because standalone relays have no renderer to surface them. -export class FrameDecoder extends SharedFrameDecoder { - constructor( - onFrame: (frame: DecodedFrame) => void, - onError?: (err: Error) => void, - options: FrameDecoderOptions = {} - ) { - super( - onFrame, - onError ?? ((error) => process.stderr.write(`[relay] ${error.message}\n`)), - options - ) - } -} diff --git a/src/wsl-guest/relay-hot-path-operation-counts.test.ts b/src/wsl-guest/relay-hot-path-operation-counts.test.ts deleted file mode 100644 index 28b62a3f79a..00000000000 --- a/src/wsl-guest/relay-hot-path-operation-counts.test.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { describe, expect, it, vi, afterEach } from 'vitest' -import { RelayDispatcher } from './dispatcher' -import { ClientRequestAborts } from './client-request-aborts' - -// Count exact entry visits to verify complexity independently of machine speed or load. - -/** Counts entries yielded by a real Map's iterators without changing the code under test. */ -class CountingMap extends Map { - visits = 0 - getCalls = 0 - - private countingIterator(inner: MapIterator): MapIterator { - const bump = (): void => { - this.visits++ - } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the literal returned here implements next() and [Symbol.iterator](), which is the whole protocol a for..of over this wrapper reaches; no other IterableIterator member is ever called. - return { - next(): IteratorResult { - const r = inner.next() - if (!r.done) { - bump() - } - return r - }, - [Symbol.iterator]() { - return this - } - } as MapIterator - } - - override [Symbol.iterator](): MapIterator<[K, V]> { - return this.countingIterator(super[Symbol.iterator]()) - } - - override values(): MapIterator { - return this.countingIterator(super.values()) - } - - override get(key: K): V | undefined { - this.getCalls++ - return super.get(key) - } -} - -type ProbedDispatcher = { - attachClient: (w: (b: Buffer) => void) => number - clients: Map - publicationLedger: { - clientBytes: Map - readonly retainedBytes: number - readonly relayLowBytes: number - readonly clientHighBytes: number - tryReserve: (m: readonly { clientKey: string; bytes: number }[]) => unknown[] | null - } - notifyLegacyCapacityIfLow: () => void - activeClients: () => unknown[] - activeClientKeys: () => string[] - tryPublishToClients: (clients: unknown[], msg: unknown, lane: string) => boolean - dispose: () => void -} - -/** Reserves through the real lease path until aggregate retention clears the relay low-water mark. */ -function loadLedgerAboveLowWater(d: ProbedDispatcher): void { - const ledger = d.publicationLedger - for (const clientKey of d.activeClientKeys()) { - if (ledger.retainedBytes > ledger.relayLowBytes) { - return - } - ledger.tryReserve([{ clientKey, bytes: ledger.clientHighBytes }]) - } -} - -function dispatcherWithClients(clientCount: number): { - d: ProbedDispatcher - clients: CountingMap - ledger: CountingMap -} { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: ProbedDispatcher names the protected members this census reads. RelayDispatcher really has them; the compiler just will not hand them out. - const d = new RelayDispatcher(() => {}) as unknown as ProbedDispatcher - for (let i = 1; i < clientCount; i++) { - d.attachClient(() => {}) - } - const clients = new CountingMap() - for (const [k, v] of d.clients) { - clients.set(k, v) - } - d.clients = clients - const ledger = new CountingMap() - d.publicationLedger.clientBytes = ledger - clients.visits = 0 - ledger.getCalls = 0 - return { d, clients, ledger } -} - -describe('relay hot-path operation counts', () => { - afterEach(() => vi.useRealTimers()) - - // Per-client abort must visit only its requests; enumerating the client index would restore quadratic churn cost. - it('abortClient visits only the target client, and never enumerates the client index', () => { - const clientCount = 40 - const inFlightPerClient = 4 - const aborts = new ClientRequestAborts() - for (let c = 1; c <= clientCount; c++) { - for (let r = 1; r <= inFlightPerClient; r++) { - aborts.create(c, r) - } - } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: byClient is the private index this test exists to measure; the shape mirrors its declaration in client-request-aborts.ts. - const byClient = (aborts as unknown as { byClient: Map> }) - .byClient - - // Prove the census sees populated buckets before measuring teardown visits. - expect(byClient.size).toBe(clientCount) - let totalControllers = 0 - for (const bucket of byClient.values()) { - totalControllers += bucket.size - } - expect(totalControllers).toBe(clientCount * inFlightPerClient) - - const index = new CountingMap>() - for (const [k, v] of byClient) { - index.set(k, v) - } - const targetBucket = new CountingMap() - for (const [k, v] of byClient.get(1)!) { - targetBucket.set(k, v) - } - index.set(1, targetBucket) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: swaps the counting stand-in into the same private index read above. - ;(aborts as unknown as { byClient: Map }).byClient = index - index.visits = 0 - targetBucket.visits = 0 - - aborts.abortClient(1) - - expect(targetBucket.visits).toBe(inFlightPerClient) - expect(index.visits).toBe(0) - expect(index.has(1)).toBe(false) - }) - - // Above aggregate low-water, lazy keys avoid enumerating clients that admission never needs. - it('does not enumerate clients at all once the aggregate ceiling answers', () => { - vi.useFakeTimers() - for (const clientCount of [50, 100, 200, 400]) { - const { d, clients, ledger } = dispatcherWithClients(clientCount) - loadLedgerAboveLowWater(d) - // Prove reserve succeeded or an idle ledger could make the loaded-path assertion vacuous. - expect(d.publicationLedger.retainedBytes).toBeGreaterThan(d.publicationLedger.relayLowBytes) - clients.visits = 0 - ledger.getCalls = 0 - - d.notifyLegacyCapacityIfLow() - - expect(clients.visits, `clients enumerated at n=${clientCount}`).toBe(0) - expect(ledger.getCalls, `ledger lookups at n=${clientCount}`).toBe(0) - d.dispose() - } - }) -}) diff --git a/src/wsl-guest/client-request-aborts.ts b/src/wsl-guest/transport/connection/client-request-aborts.ts similarity index 100% rename from src/wsl-guest/client-request-aborts.ts rename to src/wsl-guest/transport/connection/client-request-aborts.ts diff --git a/src/wsl-guest/dispatcher-capacity-signals.ts b/src/wsl-guest/transport/connection/dispatcher-capacity-signals.ts similarity index 96% rename from src/wsl-guest/dispatcher-capacity-signals.ts rename to src/wsl-guest/transport/connection/dispatcher-capacity-signals.ts index 75c7a91dd61..c1eb8dca26d 100644 --- a/src/wsl-guest/dispatcher-capacity-signals.ts +++ b/src/wsl-guest/transport/connection/dispatcher-capacity-signals.ts @@ -1,8 +1,5 @@ -import { - DispatcherClientWriter, - type RelayClientSinkOptions, - type RelayClientWrite -} from './dispatcher-client-writer' +import { DispatcherClientWriter } from '../writer/dispatcher-client-writer' +import type { RelayClientSinkOptions, RelayClientWrite } from '../writer/dispatcher-writer-sink' import type { RelayClient } from './dispatcher-contract' import { RelayDispatcherClientLifecycle } from './dispatcher-client-lifecycle' diff --git a/src/wsl-guest/dispatcher-client-close-cause.test.ts b/src/wsl-guest/transport/connection/dispatcher-client-close-cause.test.ts similarity index 90% rename from src/wsl-guest/dispatcher-client-close-cause.test.ts rename to src/wsl-guest/transport/connection/dispatcher-client-close-cause.test.ts index 862bf1caada..d188a5753d7 100644 --- a/src/wsl-guest/dispatcher-client-close-cause.test.ts +++ b/src/wsl-guest/transport/connection/dispatcher-client-close-cause.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it, vi } from 'vitest' -import { RelayDispatcher, type SinkWriteSettlement } from './dispatcher' -import { DISPATCHER_CONTROL_QUEUE_MAX_FRAMES } from './dispatcher-writer-admission' +import { RelayDispatcher } from '../publication/dispatcher' +import type { SinkWriteSettlement } from '../writer/dispatcher-writer-sink' +import { DISPATCHER_CONTROL_QUEUE_MAX_FRAMES } from '../writer/dispatcher-writer-admission' // Detach cause distinguishes observed peer departure from relay-initiated cleanup before shortening PTY owner grace. describe('RelayDispatcher client close cause', () => { diff --git a/src/wsl-guest/dispatcher-client-lifecycle.ts b/src/wsl-guest/transport/connection/dispatcher-client-lifecycle.ts similarity index 95% rename from src/wsl-guest/dispatcher-client-lifecycle.ts rename to src/wsl-guest/transport/connection/dispatcher-client-lifecycle.ts index 9d9736b433e..b08c4b8d3aa 100644 --- a/src/wsl-guest/dispatcher-client-lifecycle.ts +++ b/src/wsl-guest/transport/connection/dispatcher-client-lifecycle.ts @@ -1,10 +1,8 @@ -import { FrameDecoder, KEEPALIVE_SEND_MS, TIMEOUT_MS, encodeKeepAliveFrame } from './protocol' -import type { PtyConsumerCloseCause } from '../shared/terminal/pty/lifecycle/pty-consumer-session-contract' -import type { - DispatcherClientWriter, - RelayClientSinkOptions, - RelayClientWrite -} from './dispatcher-client-writer' +import { FrameDecoder } from '../wire/relay-frame-decoder' +import { KEEPALIVE_SEND_MS, TIMEOUT_MS, encodeKeepAliveFrame } from '../wire/protocol' +import type { PtyConsumerCloseCause } from '../../../shared/terminal/pty/lifecycle/pty-consumer-session-contract' +import type { DispatcherClientWriter } from '../writer/dispatcher-client-writer' +import type { RelayClientSinkOptions, RelayClientWrite } from '../writer/dispatcher-writer-sink' import type { RelayClient, RelayClientSessionIdentity, diff --git a/src/wsl-guest/dispatcher-client-state.ts b/src/wsl-guest/transport/connection/dispatcher-client-state.ts similarity index 90% rename from src/wsl-guest/dispatcher-client-state.ts rename to src/wsl-guest/transport/connection/dispatcher-client-state.ts index af89866e338..ddd9c106984 100644 --- a/src/wsl-guest/dispatcher-client-state.ts +++ b/src/wsl-guest/transport/connection/dispatcher-client-state.ts @@ -1,16 +1,17 @@ -import type { DecodedFrame, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse } from './protocol' +import type { DecodedFrame } from '../../../shared/relay-frame-decoder-contract' +import type { JsonRpcNotification, JsonRpcRequest, JsonRpcResponse } from '../wire/protocol' import { ClientRequestAborts } from './client-request-aborts' -import type { PtyConsumerCloseCause } from '../shared/terminal/pty/lifecycle/pty-consumer-session-contract' +import type { PtyConsumerCloseCause } from '../../../shared/terminal/pty/lifecycle/pty-consumer-session-contract' import { LegacyRelayPublicationLedger, type LegacyPublicationLease -} from './legacy-relay-publication-ledger' +} from '../writer/legacy-relay-publication-ledger' +import type { DispatcherClientWriter } from '../writer/dispatcher-client-writer' import type { - DispatcherClientWriter, RelayClientSinkOptions, RelayClientWrite, SinkWriteSettlement -} from './dispatcher-client-writer' +} from '../writer/dispatcher-writer-sink' import type { MethodHandler, NotificationHandler, diff --git a/src/wsl-guest/dispatcher-contract.ts b/src/wsl-guest/transport/connection/dispatcher-contract.ts similarity index 91% rename from src/wsl-guest/dispatcher-contract.ts rename to src/wsl-guest/transport/connection/dispatcher-contract.ts index 23afb6f3728..460c7f1d34c 100644 --- a/src/wsl-guest/dispatcher-contract.ts +++ b/src/wsl-guest/transport/connection/dispatcher-contract.ts @@ -1,11 +1,12 @@ +import type { FrameDecoder } from '../wire/relay-frame-decoder' import type { - FrameDecoder, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, PreparedJsonRpcPayload -} from './protocol' -import type { DispatcherClientWriter, SinkWriteSettlement } from './dispatcher-client-writer' +} from '../wire/protocol' +import type { DispatcherClientWriter } from '../writer/dispatcher-client-writer' +import type { SinkWriteSettlement } from '../writer/dispatcher-writer-sink' export type RequestContext = { clientId: number diff --git a/src/wsl-guest/dispatcher-frame-codec.ts b/src/wsl-guest/transport/connection/dispatcher-frame-codec.ts similarity index 92% rename from src/wsl-guest/dispatcher-frame-codec.ts rename to src/wsl-guest/transport/connection/dispatcher-frame-codec.ts index 5a25fa1100f..d9b5dc85895 100644 --- a/src/wsl-guest/dispatcher-frame-codec.ts +++ b/src/wsl-guest/transport/connection/dispatcher-frame-codec.ts @@ -1,15 +1,16 @@ +import { HEADER_LENGTH } from '../../../shared/relay-frame-decoder' import { - HEADER_LENGTH, MessageType, encodePreparedJsonRpcFrame, parseJsonRpcMessage, prepareJsonRpcPayload, - type DecodedFrame, type JsonRpcNotification, type JsonRpcRequest, type JsonRpcResponse -} from './protocol' -import type { DispatcherWriterLane, SinkWriteSettlement } from './dispatcher-client-writer' +} from '../wire/protocol' +import type { DecodedFrame } from '../../../shared/relay-frame-decoder-contract' +import type { DispatcherWriterLane } from '../writer/dispatcher-writer-admission' +import type { SinkWriteSettlement } from '../writer/dispatcher-writer-sink' import type { OutgoingJsonRpcMessage, PreparedRelayFrame, RelayClient } from './dispatcher-contract' import { RelayDispatcherCapacitySignals } from './dispatcher-capacity-signals' diff --git a/src/wsl-guest/transport/connection/dispatcher-frame-guard-regressions.test.ts b/src/wsl-guest/transport/connection/dispatcher-frame-guard-regressions.test.ts new file mode 100644 index 00000000000..9931707abb3 --- /dev/null +++ b/src/wsl-guest/transport/connection/dispatcher-frame-guard-regressions.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it, vi } from 'vitest' +import { RelayDispatcher } from '../publication/dispatcher' +import type { RelayClient } from './dispatcher-contract' +import type { JsonRpcNotification } from '../wire/protocol' + +type DispatcherInternals = { + primaryClient: RelayClient + estimateFrameBytes: (msg: JsonRpcNotification) => number + enqueueFrame: (client: RelayClient, msg: JsonRpcNotification, lane: string) => boolean +} + +describe('RelayDispatcher frame guards', () => { + it('returns zero for invalid active-client limits without encoding', () => { + const dispatcher = new RelayDispatcher(() => true, { + writableHighWaterMark: () => 1024 * 1024, + writableLength: () => 0 + }) + try { + const spy = vi.spyOn(dispatcher as unknown as DispatcherInternals, 'estimateFrameBytes') + for (const limit of [0, -1, Number.NaN]) { + expect(dispatcher.maxLegacyPtyDataChars({ id: 'pty-1' }, 'hello', limit)).toBe(0) + } + expect(spy).not.toHaveBeenCalled() + } finally { + dispatcher.dispose() + } + }) +}) diff --git a/src/wsl-guest/dispatcher-rpc-cancel-id-coercion.test.ts b/src/wsl-guest/transport/connection/dispatcher-rpc-cancel-id-coercion.test.ts similarity index 92% rename from src/wsl-guest/dispatcher-rpc-cancel-id-coercion.test.ts rename to src/wsl-guest/transport/connection/dispatcher-rpc-cancel-id-coercion.test.ts index e3d0976aa25..8b5025cd264 100644 --- a/src/wsl-guest/dispatcher-rpc-cancel-id-coercion.test.ts +++ b/src/wsl-guest/transport/connection/dispatcher-rpc-cancel-id-coercion.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { RelayDispatcher } from './dispatcher' -import { encodeFrame, MessageType } from './protocol' +import { RelayDispatcher } from '../publication/dispatcher' +import { encodeFrame, MessageType } from '../wire/protocol' // The codec admits string request ids while rpc.cancel coerces to Number; both must locate the same abort entry. describe('rpc.cancel request-id coercion', () => { diff --git a/src/wsl-guest/dispatcher-rpc-routing.ts b/src/wsl-guest/transport/connection/dispatcher-rpc-routing.ts similarity index 95% rename from src/wsl-guest/dispatcher-rpc-routing.ts rename to src/wsl-guest/transport/connection/dispatcher-rpc-routing.ts index f5795131616..62956c91961 100644 --- a/src/wsl-guest/dispatcher-rpc-routing.ts +++ b/src/wsl-guest/transport/connection/dispatcher-rpc-routing.ts @@ -1,20 +1,18 @@ -import { MAX_TIMER_DELAY_MS, isSafeTimerDelayMs } from '../shared/timer-delay' +import { MAX_TIMER_DELAY_MS, isSafeTimerDelayMs } from '../../../shared/timer-delay' import { SKILL_INSTALL_RPC_ERROR_CODE, SkillInstallFailureSchema -} from '../shared/skill-install-failure' -import { TerminalUnavailableCauseSchema } from '../shared/terminal/runtime-policy/terminal-unavailable-cause' -import { TERMINAL_UNAVAILABLE_ERROR_CODE as TERMINAL_UNAVAILABLE_RPC_ERROR_CODE } from '../shared/runtime-capability-degradation' +} from '../../../shared/skill-install-failure' +import { TerminalUnavailableCauseSchema } from '../../../shared/terminal/runtime-policy/terminal-unavailable-cause' +import { TERMINAL_UNAVAILABLE_ERROR_CODE as TERMINAL_UNAVAILABLE_RPC_ERROR_CODE } from '../../../shared/runtime-capability-degradation' import { RelayErrorCode, type JsonRpcNotification, type JsonRpcRequest, type JsonRpcResponse -} from './protocol' -import { - DISPATCHER_CONTROL_QUEUE_MAX_BYTES, - type SinkWriteSettlement -} from './dispatcher-client-writer' +} from '../wire/protocol' +import { DISPATCHER_CONTROL_QUEUE_MAX_BYTES } from '../writer/dispatcher-writer-admission' +import type { SinkWriteSettlement } from '../writer/dispatcher-writer-sink' import { RELAY_TO_CLIENT_REQUEST_TIMEOUT_MS, RESPONSE_OVER_CAPACITY_MESSAGE, diff --git a/src/wsl-guest/dispatcher-silent-client-reaper.test.ts b/src/wsl-guest/transport/connection/dispatcher-silent-client-reaper.test.ts similarity index 95% rename from src/wsl-guest/dispatcher-silent-client-reaper.test.ts rename to src/wsl-guest/transport/connection/dispatcher-silent-client-reaper.test.ts index 96619d65d1f..dec284eba5f 100644 --- a/src/wsl-guest/dispatcher-silent-client-reaper.test.ts +++ b/src/wsl-guest/transport/connection/dispatcher-silent-client-reaper.test.ts @@ -1,6 +1,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { RelayDispatcher } from './dispatcher' -import { encodeJsonRpcFrame, encodeKeepAliveFrame, KEEPALIVE_SEND_MS, TIMEOUT_MS } from './protocol' +import { RelayDispatcher } from '../publication/dispatcher' +import { + encodeJsonRpcFrame, + encodeKeepAliveFrame, + KEEPALIVE_SEND_MS, + TIMEOUT_MS +} from '../wire/protocol' // Half-open clients must release transport/ownership so abandoned viewers cannot leave PTYs paused indefinitely. describe('RelayDispatcher silent-client reaper', () => { diff --git a/src/wsl-guest/dispatcher-structured-error.test.ts b/src/wsl-guest/transport/connection/dispatcher-structured-error.test.ts similarity index 93% rename from src/wsl-guest/dispatcher-structured-error.test.ts rename to src/wsl-guest/transport/connection/dispatcher-structured-error.test.ts index 11adc7bacb8..ea6e7ec3d9a 100644 --- a/src/wsl-guest/dispatcher-structured-error.test.ts +++ b/src/wsl-guest/transport/connection/dispatcher-structured-error.test.ts @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest' -import { RelayDispatcher } from './dispatcher' -import { encodeJsonRpcFrame, MessageType, type JsonRpcResponse } from './protocol' -import type { TerminalUnavailableCause } from '../shared/terminal/runtime-policy/terminal-unavailable-cause' -import { TERMINAL_UNAVAILABLE_ERROR_CODE as TERMINAL_UNAVAILABLE_RPC_ERROR_CODE } from '../shared/runtime-capability-degradation' +import { RelayDispatcher } from '../publication/dispatcher' +import { encodeJsonRpcFrame, MessageType, type JsonRpcResponse } from '../wire/protocol' +import type { TerminalUnavailableCause } from '../../../shared/terminal/runtime-policy/terminal-unavailable-cause' +import { TERMINAL_UNAVAILABLE_ERROR_CODE as TERMINAL_UNAVAILABLE_RPC_ERROR_CODE } from '../../../shared/runtime-capability-degradation' function decodeResponse(frame: Buffer): JsonRpcResponse | null { if (frame[0] !== MessageType.Regular) { diff --git a/src/wsl-guest/dispatcher-timeout.test.ts b/src/wsl-guest/transport/connection/dispatcher-timeout.test.ts similarity index 91% rename from src/wsl-guest/dispatcher-timeout.test.ts rename to src/wsl-guest/transport/connection/dispatcher-timeout.test.ts index 1d512b2b2d6..69d64f31ba0 100644 --- a/src/wsl-guest/dispatcher-timeout.test.ts +++ b/src/wsl-guest/transport/connection/dispatcher-timeout.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { MAX_TIMER_DELAY_MS } from '../shared/timer-delay' -import { RelayDispatcher } from './dispatcher' +import { MAX_TIMER_DELAY_MS } from '../../../shared/timer-delay' +import { RelayDispatcher } from '../publication/dispatcher' describe('RelayDispatcher request timeout validation', () => { let dispatcher: RelayDispatcher diff --git a/src/wsl-guest/dispatcher-bulk-admission.test.ts b/src/wsl-guest/transport/publication/dispatcher-bulk-admission.test.ts similarity index 100% rename from src/wsl-guest/dispatcher-bulk-admission.test.ts rename to src/wsl-guest/transport/publication/dispatcher-bulk-admission.test.ts diff --git a/src/wsl-guest/dispatcher-bulk-retirement-admission.test.ts b/src/wsl-guest/transport/publication/dispatcher-bulk-retirement-admission.test.ts similarity index 100% rename from src/wsl-guest/dispatcher-bulk-retirement-admission.test.ts rename to src/wsl-guest/transport/publication/dispatcher-bulk-retirement-admission.test.ts diff --git a/src/wsl-guest/dispatcher-bulk-write-test-harness.ts b/src/wsl-guest/transport/publication/dispatcher-bulk-write-test-harness.ts similarity index 94% rename from src/wsl-guest/dispatcher-bulk-write-test-harness.ts rename to src/wsl-guest/transport/publication/dispatcher-bulk-write-test-harness.ts index 29f1c02d4f8..5d86d5f826f 100644 --- a/src/wsl-guest/dispatcher-bulk-write-test-harness.ts +++ b/src/wsl-guest/transport/publication/dispatcher-bulk-write-test-harness.ts @@ -1,8 +1,10 @@ import { Writable } from 'node:stream' import { RelayDispatcher } from './dispatcher' -import type { PreparedRelayFrame, RelayClient } from './dispatcher-contract' -import type { DispatcherWriterLane, SinkWriteSettlement } from './dispatcher-client-writer' -import { FrameDecoder, MessageType, parseJsonRpcMessage } from './protocol' +import type { PreparedRelayFrame, RelayClient } from '../connection/dispatcher-contract' +import type { DispatcherWriterLane } from '../writer/dispatcher-writer-admission' +import type { SinkWriteSettlement } from '../writer/dispatcher-writer-sink' +import { FrameDecoder } from '../wire/relay-frame-decoder' +import { MessageType, parseJsonRpcMessage } from '../wire/protocol' type WrittenFrame = { method: string; seq?: number } type PendingWrite = { frame: WrittenFrame; complete: (error?: Error | null) => void } diff --git a/src/wsl-guest/dispatcher-capacity-degradation.test.ts b/src/wsl-guest/transport/publication/dispatcher-capacity-degradation.test.ts similarity index 93% rename from src/wsl-guest/dispatcher-capacity-degradation.test.ts rename to src/wsl-guest/transport/publication/dispatcher-capacity-degradation.test.ts index 535a7277cf9..0550a7d224c 100644 --- a/src/wsl-guest/dispatcher-capacity-degradation.test.ts +++ b/src/wsl-guest/transport/publication/dispatcher-capacity-degradation.test.ts @@ -1,13 +1,10 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' -import { - RelayDispatcher, - type RelayClientSinkOptions, - type SinkWriteSettlement -} from './dispatcher' -import { DISPATCHER_CONTROL_QUEUE_MAX_FRAMES } from './dispatcher-writer-admission' -import { LEGACY_CLIENT_RETAINED_BYTES_LOW } from './legacy-relay-publication-ledger' +import { RelayDispatcher } from './dispatcher' +import type { RelayClientSinkOptions, SinkWriteSettlement } from '../writer/dispatcher-writer-sink' +import { DISPATCHER_CONTROL_QUEUE_MAX_FRAMES } from '../writer/dispatcher-writer-admission' +import { LEGACY_CLIENT_RETAINED_BYTES_LOW } from '../writer/legacy-relay-publication-ledger' -import { encodeJsonRpcFrame, RelayErrorCode } from './protocol' +import { encodeJsonRpcFrame, RelayErrorCode } from '../wire/protocol' type BoundedClient = { frames: Buffer[] @@ -137,26 +134,6 @@ describe('RelayDispatcher bounded-capacity degradation', () => { } }) - it('logs at most one drop per client generation', () => { - const primary = makeBoundedClient(16384) - const stderr = vi.spyOn(process.stderr, 'write').mockReturnValue(true) - const bounded = new RelayDispatcher(primary.write, primary.options) - try { - const flood = { events: ['x'.repeat(20_000)] } - bounded.notify('fs.changed', flood) - bounded.notify('fs.changed', flood) - expect(stderr).toHaveBeenCalledTimes(1) - - const reattached = makeBoundedClient(16384) - bounded.setWrite(reattached.write, reattached.options) - bounded.notify('fs.changed', flood) - expect(stderr).toHaveBeenCalledTimes(2) - } finally { - stderr.mockRestore() - bounded.dispose() - } - }) - it('publishProducerNotification publishes on the producer lane and never closes', () => { const primary = makeBoundedClient(65536) const secondary = makeBoundedClient(16384) diff --git a/src/wsl-guest/dispatcher-json-payload.test.ts b/src/wsl-guest/transport/publication/dispatcher-json-payload.test.ts similarity index 97% rename from src/wsl-guest/dispatcher-json-payload.test.ts rename to src/wsl-guest/transport/publication/dispatcher-json-payload.test.ts index 4965fe6898a..d11e96e8c16 100644 --- a/src/wsl-guest/dispatcher-json-payload.test.ts +++ b/src/wsl-guest/transport/publication/dispatcher-json-payload.test.ts @@ -1,15 +1,12 @@ import { afterEach, describe, expect, it, vi } from 'vitest' -import type * as ProtocolModule from './protocol' -import { - RelayDispatcher, - type RelayClientSinkOptions, - type SinkWriteSettlement -} from './dispatcher' -import { encodeKeepAliveFrame, type JsonRpcNotification } from './protocol' +import type * as ProtocolModule from '../wire/protocol' +import { RelayDispatcher } from './dispatcher' +import type { RelayClientSinkOptions, SinkWriteSettlement } from '../writer/dispatcher-writer-sink' +import { encodeKeepAliveFrame, type JsonRpcNotification } from '../wire/protocol' const protocolCalls = vi.hoisted(() => ({ preparations: 0, encodes: 0 })) -vi.mock('./protocol', async (importOriginal) => { +vi.mock('../wire/protocol', async (importOriginal) => { const actual = await importOriginal() return { ...actual, diff --git a/src/wsl-guest/dispatcher-notification-ownership.test.ts b/src/wsl-guest/transport/publication/dispatcher-notification-ownership.test.ts similarity index 100% rename from src/wsl-guest/dispatcher-notification-ownership.test.ts rename to src/wsl-guest/transport/publication/dispatcher-notification-ownership.test.ts diff --git a/src/wsl-guest/dispatcher-producer-capacity.ts b/src/wsl-guest/transport/publication/dispatcher-producer-capacity.ts similarity index 100% rename from src/wsl-guest/dispatcher-producer-capacity.ts rename to src/wsl-guest/transport/publication/dispatcher-producer-capacity.ts diff --git a/src/wsl-guest/dispatcher-producer-transport.ts b/src/wsl-guest/transport/publication/dispatcher-producer-transport.ts similarity index 91% rename from src/wsl-guest/dispatcher-producer-transport.ts rename to src/wsl-guest/transport/publication/dispatcher-producer-transport.ts index 5c1b721531c..a86af574547 100644 --- a/src/wsl-guest/dispatcher-producer-transport.ts +++ b/src/wsl-guest/transport/publication/dispatcher-producer-transport.ts @@ -1,11 +1,9 @@ -import { - DEFAULT_PRODUCER_QUEUE_MAX_BYTES, - type SinkWriteSettlement -} from './dispatcher-client-writer' -import type { LegacyPublicationLease } from './legacy-relay-publication-ledger' -import type { JsonRpcNotification } from './protocol' -import type { PreparedRelayFrame, RelayClient } from './dispatcher-contract' -import { RelayDispatcherRpcRouting } from './dispatcher-rpc-routing' +import { DEFAULT_PRODUCER_QUEUE_MAX_BYTES } from '../writer/dispatcher-writer-admission' +import type { SinkWriteSettlement } from '../writer/dispatcher-writer-sink' +import type { LegacyPublicationLease } from '../writer/legacy-relay-publication-ledger' +import type { JsonRpcNotification } from '../wire/protocol' +import type { PreparedRelayFrame, RelayClient } from '../connection/dispatcher-contract' +import { RelayDispatcherRpcRouting } from '../connection/dispatcher-rpc-routing' export abstract class RelayDispatcherProducerTransport extends RelayDispatcherRpcRouting { protected tryPublishToClients( diff --git a/src/wsl-guest/dispatcher-pty-publication.ts b/src/wsl-guest/transport/publication/dispatcher-pty-publication.ts similarity index 96% rename from src/wsl-guest/dispatcher-pty-publication.ts rename to src/wsl-guest/transport/publication/dispatcher-pty-publication.ts index 40eb149f0f2..ba7894c062c 100644 --- a/src/wsl-guest/dispatcher-pty-publication.ts +++ b/src/wsl-guest/transport/publication/dispatcher-pty-publication.ts @@ -1,5 +1,5 @@ -import type { SinkWriteSettlement } from './dispatcher-client-writer' -import type { JsonRpcNotification } from './protocol' +import type { SinkWriteSettlement } from '../writer/dispatcher-writer-sink' +import type { JsonRpcNotification } from '../wire/protocol' import { RelayDispatcherProducerCapacity } from './dispatcher-producer-capacity' export abstract class RelayDispatcherPtyPublication extends RelayDispatcherProducerCapacity { diff --git a/src/wsl-guest/dispatcher.test.ts b/src/wsl-guest/transport/publication/dispatcher.test.ts similarity index 84% rename from src/wsl-guest/dispatcher.test.ts rename to src/wsl-guest/transport/publication/dispatcher.test.ts index 91e8f230cc5..2f57601b83c 100644 --- a/src/wsl-guest/dispatcher.test.ts +++ b/src/wsl-guest/transport/publication/dispatcher.test.ts @@ -1,13 +1,13 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' -import { RelayDispatcher, type SinkWriteSettlement } from './dispatcher' +import { RelayDispatcher } from './dispatcher' +import type { SinkWriteSettlement } from '../writer/dispatcher-writer-sink' -import { relayWriterControlReserve } from './dispatcher-writer-admission' import { encodeJsonRpcFrame, MessageType, type JsonRpcRequest, type JsonRpcNotification -} from './protocol' +} from '../wire/protocol' function decodeFirstFrame(buf: Buffer): { type: number; id: number; ack: number; payload: Buffer } { const type = buf[0] @@ -597,100 +597,4 @@ describe('RelayDispatcher', () => { } }) }) - - describe('legacy PTY chunk sizing', () => { - // Pre-optimization sizing loop, kept verbatim for differential verification. - function referenceMaxChars( - capacities: number[], - params: Record, - data: string, - limit: number - ): number { - if (capacities.length === 0) { - return Math.min(data.length, limit) - } - let low = 0 - let high = Math.min(data.length, limit) - while (low < high) { - const mid = Math.ceil((low + high) / 2) - const msg: JsonRpcNotification = { - jsonrpc: '2.0', - method: 'pty.data', - params: { ...params, data: data.slice(0, mid) } - } - const bytes = encodeJsonRpcFrame(msg, 0, 0).length - if (capacities.every((capacity) => bytes <= capacity)) { - low = mid - } else { - high = mid - 1 - } - } - return low - } - - function makeDispatcher(highWaterMarks: number[]): { - sized: RelayDispatcher - capacities: number[] - } { - const [primaryHwm, ...rest] = highWaterMarks - const sized = new RelayDispatcher(() => true, { - writableHighWaterMark: () => primaryHwm, - writableLength: () => 0 - }) - for (const hwm of rest) { - sized.attachClient(() => true, { - writableHighWaterMark: () => hwm, - writableLength: () => 0 - }) - } - return { - sized, - capacities: highWaterMarks.map((hwm) => Math.max(0, hwm - relayWriterControlReserve(hwm))) - } - } - - function mulberry32(seed: number): () => number { - let a = seed - return () => { - a = (a + 0x6d2b79f5) | 0 - let t = Math.imul(a ^ (a >>> 15), 1 | a) - t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t - return ((t ^ (t >>> 14)) >>> 0) / 4294967296 - } - } - - // Multi-byte UTF-8, astral pairs, lone surrogates, controls, quotes, and backslashes. - const alphabet = 'aZ9 "\\\n\r\t\u0000é߀中𝄞😀𐀀�' - - it('matches the pre-optimization sizing loop across randomized inputs', () => { - const random = mulberry32(0xc0ffee) - const hwmChoices = [1030, 1100, 1250, 1400, 2048, 1024 * 1024] - for (let trial = 0; trial < 300; trial++) { - const length = Math.floor(random() * 240) - let data = '' - for (let i = 0; i < length; i++) { - data += alphabet[Math.floor(random() * alphabet.length)] - } - const clientCount = 1 + Math.floor(random() * 2) - const hwms = Array.from( - { length: clientCount }, - () => hwmChoices[Math.floor(random() * hwmChoices.length)] - ) - const params = { id: `pty-${trial}`, seq: trial } - const { sized, capacities } = makeDispatcher(hwms) - try { - for (const limit of [0, 1, Math.floor(length / 2), length, length + 17]) { - expect(sized.maxLegacyPtyDataChars(params, data, limit)).toBe( - referenceMaxChars(capacities, params, data, limit) - ) - } - expect(sized.maxLegacyPtyDataChars(params, data)).toBe( - referenceMaxChars(capacities, params, data, data.length) - ) - } finally { - sized.dispose() - } - } - }) - }) }) diff --git a/src/wsl-guest/dispatcher-notification-publication.ts b/src/wsl-guest/transport/publication/dispatcher.ts similarity index 96% rename from src/wsl-guest/dispatcher-notification-publication.ts rename to src/wsl-guest/transport/publication/dispatcher.ts index 029acb0a823..b192bf130f5 100644 --- a/src/wsl-guest/dispatcher-notification-publication.ts +++ b/src/wsl-guest/transport/publication/dispatcher.ts @@ -1,10 +1,10 @@ -import type { SinkWriteSettlement } from './dispatcher-client-writer' -import type { JsonRpcNotification } from './protocol' +import type { SinkWriteSettlement } from '../writer/dispatcher-writer-sink' +import type { JsonRpcNotification } from '../wire/protocol' import { DROPPED_NOTIFICATION_LOG_KEY_LIMIT, type PreparedRelayFrame, type RelayClient -} from './dispatcher-contract' +} from '../connection/dispatcher-contract' import { RelayDispatcherPtyPublication } from './dispatcher-pty-publication' export abstract class RelayDispatcherNotificationPublication extends RelayDispatcherPtyPublication { @@ -209,3 +209,5 @@ export abstract class RelayDispatcherNotificationPublication extends RelayDispat ) } } + +export class RelayDispatcher extends RelayDispatcherNotificationPublication {} diff --git a/src/wsl-guest/protocol-backpressure.test.ts b/src/wsl-guest/transport/wire/protocol-backpressure.test.ts similarity index 97% rename from src/wsl-guest/protocol-backpressure.test.ts rename to src/wsl-guest/transport/wire/protocol-backpressure.test.ts index 233e3c52032..ad5d64c04e0 100644 --- a/src/wsl-guest/protocol-backpressure.test.ts +++ b/src/wsl-guest/transport/wire/protocol-backpressure.test.ts @@ -1,14 +1,15 @@ import { describe, expect, it, vi } from 'vitest' +import { FrameDecoder } from './relay-frame-decoder' import { - FrameDecoder, FrameDecoderContinuationError, + type DecodedFrame +} from '../../../shared/relay-frame-decoder-contract' +import { FRAME_DECODER_MAX_RETAINED_BYTES, HEADER_LENGTH, - MAX_MESSAGE_SIZE, - MessageType, - encodeFrame, - type DecodedFrame -} from './protocol' + MAX_MESSAGE_SIZE +} from '../../../shared/relay-frame-decoder' +import { MessageType, encodeFrame } from './protocol' function createScheduler(): { schedule: (callback: () => void) => number diff --git a/src/wsl-guest/protocol-handshake.test.ts b/src/wsl-guest/transport/wire/protocol-handshake.test.ts similarity index 94% rename from src/wsl-guest/protocol-handshake.test.ts rename to src/wsl-guest/transport/wire/protocol-handshake.test.ts index 226779776aa..17fd556689f 100644 --- a/src/wsl-guest/protocol-handshake.test.ts +++ b/src/wsl-guest/transport/wire/protocol-handshake.test.ts @@ -1,12 +1,8 @@ import { describe, expect, it } from 'vitest' -import { - MessageType, - HEADER_LENGTH, - FrameDecoder, - encodeHandshakeFrame, - parseHandshakeMessage, - type DecodedFrame -} from './protocol' +import { MessageType, encodeHandshakeFrame, parseHandshakeMessage } from './protocol' +import { HEADER_LENGTH } from '../../../shared/relay-frame-decoder' +import { FrameDecoder } from './relay-frame-decoder' +import type { DecodedFrame } from '../../../shared/relay-frame-decoder-contract' describe('handshake framing', () => { it('round-trips an orca-relay-handshake envelope through the existing framing', () => { @@ -155,9 +151,4 @@ describe('handshake framing', () => { expect(() => parseHandshakeMessage(payload)).toThrow('runtime') } ) - - it('handshake frames use a distinct MessageType from Regular and KeepAlive', () => { - expect(MessageType.Handshake).not.toBe(MessageType.Regular) - expect(MessageType.Handshake).not.toBe(MessageType.KeepAlive) - }) }) diff --git a/src/wsl-guest/protocol-json-payload.test.ts b/src/wsl-guest/transport/wire/protocol-json-payload.test.ts similarity index 54% rename from src/wsl-guest/protocol-json-payload.test.ts rename to src/wsl-guest/transport/wire/protocol-json-payload.test.ts index 044f69fe491..7093e034de4 100644 --- a/src/wsl-guest/protocol-json-payload.test.ts +++ b/src/wsl-guest/transport/wire/protocol-json-payload.test.ts @@ -1,10 +1,7 @@ import { describe, expect, it } from 'vitest' -import * as mainProtocol from '../main/ssh/protocol/remote-frame-protocol' +import * as mainProtocol from '../../../main/ssh/protocol/remote-frame-protocol' +import { HEADER_LENGTH, MAX_MESSAGE_SIZE } from '../../../shared/relay-frame-decoder' import { - HEADER_LENGTH, - MAX_MESSAGE_SIZE, - MessageType, - encodeFrame, encodeJsonRpcFrame, encodePreparedJsonRpcFrame, prepareJsonRpcPayload, @@ -12,31 +9,6 @@ import { } from './protocol' describe('prepared relay JSON payload framing', () => { - it('is byte-equivalent to direct composition for every header field', () => { - const message: JsonRpcNotification = { - jsonrpc: '2.0', - method: 'pty.data', - params: { id: 'pty-1', data: 'héllo "𝄞"\\\n\uD800', seq: 42 } - } - const payload = Buffer.from(JSON.stringify(message), 'utf8') - const relayPrepared = prepareJsonRpcPayload(message) - const mainPrepared = mainProtocol.prepareJsonRpcPayload(message) - - for (const [id, ack] of [ - [0, 0], - [19, 7], - [0xffffffff, 0xfffffffe] - ]) { - const reference = encodeFrame(MessageType.Regular, id, ack, payload) - expect(encodePreparedJsonRpcFrame(relayPrepared, id, ack).equals(reference)).toBe(true) - expect(encodeJsonRpcFrame(message, id, ack).equals(reference)).toBe(true) - expect(mainProtocol.encodePreparedJsonRpcFrame(mainPrepared, id, ack).equals(reference)).toBe( - true - ) - expect(mainProtocol.encodeJsonRpcFrame(message, id, ack).equals(reference)).toBe(true) - } - }) - it('accepts the exact payload maximum and rejects one byte more', () => { const base: JsonRpcNotification = { jsonrpc: '2.0', diff --git a/src/wsl-guest/protocol.ts b/src/wsl-guest/transport/wire/protocol.ts similarity index 92% rename from src/wsl-guest/protocol.ts rename to src/wsl-guest/transport/wire/protocol.ts index fa81becf7a6..8d4ad3a2efb 100644 --- a/src/wsl-guest/protocol.ts +++ b/src/wsl-guest/transport/wire/protocol.ts @@ -1,27 +1,6 @@ // Standalone Electron-free protocol; mirror src/main/ssh/remote-exec/remote-frame-protocol.ts. -import { - FrameDecoder, - FrameDecoderContinuationError, - HEADER_LENGTH, - MAX_MESSAGE_SIZE, - FRAME_DECODER_MAX_FRAMES_PER_TURN, - FRAME_DECODER_MAX_BYTES_PER_TURN, - FRAME_DECODER_MAX_TURN_MS, - FRAME_DECODER_MAX_RETAINED_BYTES -} from './relay-frame-decoder' - -export { - FrameDecoder, - FrameDecoderContinuationError, - HEADER_LENGTH, - MAX_MESSAGE_SIZE, - FRAME_DECODER_MAX_FRAMES_PER_TURN, - FRAME_DECODER_MAX_BYTES_PER_TURN, - FRAME_DECODER_MAX_TURN_MS, - FRAME_DECODER_MAX_RETAINED_BYTES -} -export type { DecodedFrame, FrameDecoderOptions } from './relay-frame-decoder' +import { HEADER_LENGTH, MAX_MESSAGE_SIZE } from '../../../shared/relay-frame-decoder' export const RELAY_VERSION = '0.1.0' export const RELAY_SENTINEL = `ORCA-RELAY v${RELAY_VERSION} READY\n` diff --git a/src/wsl-guest/transport/wire/relay-frame-decoder.ts b/src/wsl-guest/transport/wire/relay-frame-decoder.ts new file mode 100644 index 00000000000..8816a21b9be --- /dev/null +++ b/src/wsl-guest/transport/wire/relay-frame-decoder.ts @@ -0,0 +1,20 @@ +import { FrameDecoder as SharedFrameDecoder } from '../../../shared/relay-frame-decoder' +import type { + DecodedFrame, + FrameDecoderOptions +} from '../../../shared/relay-frame-decoder-contract' + +// Default faults to stderr because standalone relays have no renderer to surface them. +export class FrameDecoder extends SharedFrameDecoder { + constructor( + onFrame: (frame: DecodedFrame) => void, + onError?: (err: Error) => void, + options: FrameDecoderOptions = {} + ) { + super( + onFrame, + onError ?? ((error) => process.stderr.write(`[relay] ${error.message}\n`)), + options + ) + } +} diff --git a/src/wsl-guest/dispatcher-client-writer.test.ts b/src/wsl-guest/transport/writer/dispatcher-client-writer.test.ts similarity index 98% rename from src/wsl-guest/dispatcher-client-writer.test.ts rename to src/wsl-guest/transport/writer/dispatcher-client-writer.test.ts index 75dfee455d5..eaf601efa62 100644 --- a/src/wsl-guest/dispatcher-client-writer.test.ts +++ b/src/wsl-guest/transport/writer/dispatcher-client-writer.test.ts @@ -1,9 +1,7 @@ import { describe, expect, it, vi } from 'vitest' -import { - DispatcherClientWriter, - type DispatcherWriterLane, - type SinkWriteSettlement -} from './dispatcher-client-writer' +import { DispatcherClientWriter } from './dispatcher-client-writer' +import type { DispatcherWriterLane } from './dispatcher-writer-admission' +import type { SinkWriteSettlement } from './dispatcher-writer-sink' type AcceptedWrite = { data: string diff --git a/src/wsl-guest/dispatcher-client-writer.ts b/src/wsl-guest/transport/writer/dispatcher-client-writer.ts similarity index 96% rename from src/wsl-guest/dispatcher-client-writer.ts rename to src/wsl-guest/transport/writer/dispatcher-client-writer.ts index c1494c25cb4..090caeb4d19 100644 --- a/src/wsl-guest/dispatcher-client-writer.ts +++ b/src/wsl-guest/transport/writer/dispatcher-client-writer.ts @@ -14,18 +14,6 @@ import { type SinkWriteSettlement } from './dispatcher-writer-sink' -export { - DEFAULT_PRODUCER_QUEUE_MAX_BYTES, - DISPATCHER_CONTROL_QUEUE_MAX_BYTES, - relayWriterControlReserve -} from './dispatcher-writer-admission' -export type { - RelayClientSinkOptions, - RelayClientWrite, - SinkWriteSettlement -} from './dispatcher-writer-sink' -export type { DispatcherWriterLane } from './dispatcher-writer-admission' - export class DispatcherClientWriter { private readonly admission: DispatcherWriterAdmission private readonly sink: DispatcherWriterSink diff --git a/src/wsl-guest/dispatcher-writer-admission.test.ts b/src/wsl-guest/transport/writer/dispatcher-writer-admission.test.ts similarity index 100% rename from src/wsl-guest/dispatcher-writer-admission.test.ts rename to src/wsl-guest/transport/writer/dispatcher-writer-admission.test.ts diff --git a/src/wsl-guest/dispatcher-writer-admission.ts b/src/wsl-guest/transport/writer/dispatcher-writer-admission.ts similarity index 100% rename from src/wsl-guest/dispatcher-writer-admission.ts rename to src/wsl-guest/transport/writer/dispatcher-writer-admission.ts diff --git a/src/wsl-guest/dispatcher-writer-drain-arm.ts b/src/wsl-guest/transport/writer/dispatcher-writer-drain-arm.ts similarity index 100% rename from src/wsl-guest/dispatcher-writer-drain-arm.ts rename to src/wsl-guest/transport/writer/dispatcher-writer-drain-arm.ts diff --git a/src/wsl-guest/dispatcher-writer-lane-scheduler.ts b/src/wsl-guest/transport/writer/dispatcher-writer-lane-scheduler.ts similarity index 100% rename from src/wsl-guest/dispatcher-writer-lane-scheduler.ts rename to src/wsl-guest/transport/writer/dispatcher-writer-lane-scheduler.ts diff --git a/src/wsl-guest/dispatcher-writer-sink.ts b/src/wsl-guest/transport/writer/dispatcher-writer-sink.ts similarity index 100% rename from src/wsl-guest/dispatcher-writer-sink.ts rename to src/wsl-guest/transport/writer/dispatcher-writer-sink.ts diff --git a/src/wsl-guest/legacy-relay-publication-ledger.test.ts b/src/wsl-guest/transport/writer/legacy-relay-publication-ledger.test.ts similarity index 100% rename from src/wsl-guest/legacy-relay-publication-ledger.test.ts rename to src/wsl-guest/transport/writer/legacy-relay-publication-ledger.test.ts diff --git a/src/wsl-guest/legacy-relay-publication-ledger.ts b/src/wsl-guest/transport/writer/legacy-relay-publication-ledger.ts similarity index 100% rename from src/wsl-guest/legacy-relay-publication-ledger.ts rename to src/wsl-guest/transport/writer/legacy-relay-publication-ledger.ts diff --git a/src/wsl-guest/wsl-agent-hook-relay.ts b/src/wsl-guest/wsl-agent-hook-relay.ts index 625c17e9404..09778bf45c5 100644 --- a/src/wsl-guest/wsl-agent-hook-relay.ts +++ b/src/wsl-guest/wsl-agent-hook-relay.ts @@ -5,21 +5,21 @@ import { AGENT_HOOK_INFER_INTERRUPT_METHOD } from '../shared/agent-hook-listener import { CLAUDE_AGENT_VIEW_RELAY_METHOD } from '../shared/claude-agent-view' import { homedir } from 'node:os' -import { RELAY_SENTINEL } from './protocol' -import { RelayDispatcher } from './dispatcher' -import { RelayAgentHookServer } from './agent-hook-server' -import { registerWslHookFsHandlers } from './wsl-hook-fs-bridge' -import { PluginOverlayManager } from './plugin-overlay' -import { createInstallPluginsHandler } from './wsl-install-plugins-handler' -import { PreflightHandler } from './preflight-handler' +import { RELAY_SENTINEL } from './transport/wire/protocol' +import { RelayDispatcher } from './transport/publication/dispatcher' +import { RelayAgentHookServer } from './agent-hooks/server/agent-hook-server' +import { registerWslHookFsHandlers } from './commands/wsl-hook-fs-bridge' +import { PluginOverlayManager } from './plugins/overlays/plugin-overlay' +import { createInstallPluginsHandler } from './plugins/installation/wsl-install-plugins-handler' +import { PreflightHandler } from './commands/preflight-handler' import { AGENT_HOOK_INSTALL_PLUGINS_METHOD, AGENT_HOOK_REQUEST_REPLAY_METHOD, AGENT_HOOK_PANE_ROOT_METHOD } from '../shared/agent-hook-listener/transport/agent-hook-relay' -import { publishAgentHookEnvelope } from './agent-hook-envelope-publication' +import { publishAgentHookEnvelope } from './agent-hooks/publication/agent-hook-envelope-publication' import { createLocalHookRootEvidenceReader } from '../shared/agent-hook-listener/presence/hook-process-root-evidence-reader' -import { createRelayPaneRootResolver } from './relay-pane-root' +import { createRelayPaneRootResolver } from './agent-hooks/presence/relay-pane-root' import { sanitizeWslHookInstanceKey, WSL_HOOK_RELAY_INSTANCE_ENV, diff --git a/tests/tools/omp-config-root-relay-shell.test.mjs b/tests/tools/omp-config-root-relay-shell.test.mjs index a275cab3e38..d77453e21b0 100644 --- a/tests/tools/omp-config-root-relay-shell.test.mjs +++ b/tests/tools/omp-config-root-relay-shell.test.mjs @@ -4,8 +4,8 @@ import { delimiter, join } from 'node:path' import { afterEach, expect, it, vi } from 'vitest' import { runProcess } from '@orca/process-host' import { resetLoginShellEnvironmentCacheForTests } from '../../src/main/startup/shell-environment/login-shell-environment' -import { PluginOverlayManager } from '../../src/wsl-guest/plugin-overlay' -import { resolveOmpConfigDirName } from '../../src/wsl-guest/plugin-overlay-env' +import { PluginOverlayManager } from '../../src/wsl-guest/plugins/overlays/plugin-overlay' +import { resolveOmpConfigDirName } from '../../src/wsl-guest/plugins/overlays/plugin-overlay-env' import { __resetShellStartupEnvCache } from '../../src/main/pty/shell-startup/shell-startup-env' const fixture = { home: '', shell: '' } const shells = ['bash', 'zsh', 'fish'].map((name) => { From a5f68e9d4f746395688ac77d925ab60da81cb6a7 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 23:00:13 -0800 Subject: [PATCH 028/178] test: remove tab-switch mock projection suite (#41872) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 1 - src/renderer/src/hooks/ipc-tab-switch.test.ts | 398 ------------------ 2 files changed, 399 deletions(-) delete mode 100644 src/renderer/src/hooks/ipc-tab-switch.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 6fc0be9f98e..d866a51759f 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -5464,7 +5464,6 @@ "src/renderer/src/hooks/ipc-events/worktree-event-runtime-activation.test.ts": 1934, "src/renderer/src/hooks/ipc-events/wsl-host-ipc-bridge-capability.test.ts": 1917, "src/renderer/src/hooks/ipc-tab-switch-group-order-hydration.test.ts": 797, - "src/renderer/src/hooks/ipc-tab-switch.test.ts": 839, "src/renderer/src/hooks/macos-tcc-prompt-notice-subscription.test.ts": 52, "src/renderer/src/hooks/metadata-request-cache.test.ts": 144, "src/renderer/src/hooks/programmatic-scroll-marks.test.ts": 47, diff --git a/src/renderer/src/hooks/ipc-tab-switch.test.ts b/src/renderer/src/hooks/ipc-tab-switch.test.ts deleted file mode 100644 index 557b0c3fb28..00000000000 --- a/src/renderer/src/hooks/ipc-tab-switch.test.ts +++ /dev/null @@ -1,398 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' - -const { getStateMock, getActiveTabNavOrderMock } = vi.hoisted(() => ({ - getStateMock: vi.fn(), - getActiveTabNavOrderMock: vi.fn() -})) - -vi.mock('../store', () => ({ - useAppStore: { - getState: getStateMock - } -})) - -vi.mock('@/components/tab-bar/group-tab-order', () => ({ - getActiveTabNavOrder: getActiveTabNavOrderMock -})) - -import { createTabsFocusActions } from '../store/slices/tabs/tabs-focus-actions' -import type { TabsSliceGet, TabsSliceSet } from '../store/slices/tabs/tabs-slice-contract' -import { - handleSwitchRecentTab, - handleSwitchTab, - handleSwitchTabAcrossAllTypes, - handleSwitchTerminalTab -} from './ipc-tab-switch' - -type ActiveTabType = 'terminal' | 'editor' | 'browser' | 'simulator' | 'agent-session' - -type MockGroup = { - id: string - activeTabId: string | null - tabOrder?: string[] - recentTabIds?: string[] -} - -type MockStore = { - activeWorktreeId: string - activeTabType: ActiveTabType - activeTabId: string - activeFileId: string - activeBrowserTabId: string - activeGroupIdByWorktree: Record - groupsByWorktree: Record - tabsByWorktree: Record - unifiedTabsByWorktree: Record< - string, - { - id: string - entityId: string - groupId: string - contentType: 'terminal' | 'editor' | 'browser' | 'simulator' | 'agent-session' - }[] - > - setActiveTab: ReturnType - setActiveFile: ReturnType - setActiveBrowserTab: ReturnType - activateTab: ReturnType - setActiveTabType: ReturnType - getActiveTab: (worktreeId: string) => unknown -} - -function makeStore(activeTabType: ActiveTabType, overrides: Partial = {}): MockStore { - const store: MockStore = { - activeWorktreeId: 'wt-1', - activeTabType, - activeTabId: 'term-1', - activeFileId: 'editor-1', - activeBrowserTabId: 'browser-1', - activeGroupIdByWorktree: { 'wt-1': 'group-1' }, - groupsByWorktree: { 'wt-1': [{ id: 'group-1', activeTabId: 'tab-1' }] }, - tabsByWorktree: {}, - unifiedTabsByWorktree: {}, - setActiveTab: vi.fn(), - setActiveFile: vi.fn(), - setActiveBrowserTab: vi.fn(), - activateTab: vi.fn(), - setActiveTabType: vi.fn(), - getActiveTab: () => null, - ...overrides - } - // Use the real resolver so group selection is exercised instead of supplied by a stub. - store.getActiveTab = createTabsFocusActions( - (() => {}) as unknown as TabsSliceSet, - (() => store) as unknown as TabsSliceGet - ).getActiveTab - return store -} - -describe('handleSwitchTerminalTab', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - it('returns false when no terminal tabs exist', () => { - const store = makeStore('editor') - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([ - { type: 'editor', id: 'editor-1' }, - { type: 'browser', id: 'browser-1' } - ]) - - expect(handleSwitchTerminalTab(1)).toBe(false) - expect(store.setActiveTab).not.toHaveBeenCalled() - expect(store.setActiveTabType).not.toHaveBeenCalled() - }) - - it('falls back to the worktree terminal order when the active group is still empty', () => { - const store = makeStore('terminal') - store.activeTabId = 'term-1' - store.tabsByWorktree = { - 'wt-1': [{ id: 'term-1' }, { id: 'term-2' }] - } - getStateMock.mockReturnValue(store) - // Keyboard-only worktree activation can briefly restore the group before its unified tabs. - getActiveTabNavOrderMock.mockReturnValue([]) - - expect(handleSwitchTerminalTab(1)).toBe(true) - expect(store.setActiveTab).toHaveBeenCalledWith('term-2') - expect(store.setActiveTabType).toHaveBeenCalledWith('terminal', 'wt-1') - }) - - it('falls back when one stale group terminal hides the remaining worktree terminal', () => { - const store = makeStore('terminal') - store.activeTabId = 'term-1' - store.tabsByWorktree = { - 'wt-1': [{ id: 'term-1' }, { id: 'term-2' }] - } - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([{ type: 'terminal', id: 'term-1' }]) - - expect(handleSwitchTerminalTab(1)).toBe(true) - expect(store.setActiveTab).toHaveBeenCalledWith('term-2') - }) - - it('keeps chats in visible order while still reaching a terminal without its unified row', () => { - const store = makeStore('agent-session', { - tabsByWorktree: { 'wt-1': [{ id: 'term-1' }, { id: 'term-2' }] }, - groupsByWorktree: { - 'wt-1': [ - { id: 'group-1', activeTabId: 'chat-tab', tabOrder: ['tab-1', 'chat-tab'] }, - { id: 'group-2', activeTabId: 'other-chat', tabOrder: ['other-chat'] } - ] - }, - unifiedTabsByWorktree: { - 'wt-1': [ - { id: 'tab-1', entityId: 'term-1', groupId: 'group-1', contentType: 'terminal' }, - { id: 'chat-tab', entityId: 'chat', groupId: 'group-1', contentType: 'agent-session' }, - { id: 'other-chat', entityId: 'other', groupId: 'group-2', contentType: 'agent-session' } - ] - } - }) - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([ - { type: 'terminal', id: 'term-1', tabId: 'tab-1' }, - { type: 'agent-session', id: 'chat', tabId: 'chat-tab' } - ]) - store.setActiveTab.mockImplementation((id: string) => { - store.activeTabId = id - }) - store.setActiveTabType.mockImplementation((type: ActiveTabType) => { - store.activeTabType = type - }) - store.activateTab.mockImplementation((tabId: string) => { - store.groupsByWorktree['wt-1'][0].activeTabId = tabId - }) - for (const [direction, expectedId, expectedType] of [ - [1, 'term-2', 'terminal'], - [1, 'term-1', 'terminal'], - [1, 'chat-tab', 'agent-session'], - [-1, 'term-1', 'terminal'], - [-1, 'term-2', 'terminal'], - [-1, 'chat-tab', 'agent-session'] - ] as const) { - expect(handleSwitchTerminalTab(direction)).toBe(true) - if (expectedType === 'agent-session') { - expect(store.activateTab).toHaveBeenLastCalledWith(expectedId) - } else { - expect(store.setActiveTab).toHaveBeenLastCalledWith(expectedId) - } - expect(store.activeTabType).toBe(expectedType) - } - }) - - it('keeps a genuine one-terminal split group a no-op', () => { - const store = makeStore('terminal') - store.activeTabId = 'term-1' - store.tabsByWorktree = { - 'wt-1': [{ id: 'term-1' }, { id: 'term-2' }] - } - store.groupsByWorktree = { - 'wt-1': [ - { id: 'group-1', activeTabId: 'tab-1', tabOrder: ['tab-1'] }, - { id: 'group-2', activeTabId: 'tab-2', tabOrder: ['tab-2'] } - ] - } - store.unifiedTabsByWorktree = { - 'wt-1': [ - { id: 'tab-1', entityId: 'term-1', groupId: 'group-1', contentType: 'terminal' }, - { id: 'tab-2', entityId: 'term-2', groupId: 'group-2', contentType: 'terminal' } - ] - } - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([{ type: 'terminal', id: 'term-1', tabId: 'tab-1' }]) - - expect(handleSwitchTerminalTab(1)).toBe(false) - expect(store.setActiveTab).not.toHaveBeenCalled() - expect(store.setActiveTabType).not.toHaveBeenCalled() - }) - - it('keeps an editor-only active group local when terminals belong to another split', () => { - const store = makeStore('editor') - store.activeFileId = 'editor-1' - store.tabsByWorktree = { - 'wt-1': [{ id: 'term-2' }] - } - store.groupsByWorktree = { - 'wt-1': [ - { id: 'group-1', activeTabId: 'editor-tab', tabOrder: ['editor-tab'] }, - { id: 'group-2', activeTabId: 'terminal-tab', tabOrder: ['terminal-tab'] } - ] - } - store.unifiedTabsByWorktree = { - 'wt-1': [ - { - id: 'editor-tab', - entityId: 'editor-1', - groupId: 'group-1', - contentType: 'editor' - }, - { - id: 'terminal-tab', - entityId: 'term-2', - groupId: 'group-2', - contentType: 'terminal' - } - ] - } - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([ - { type: 'editor', id: 'editor-1', tabId: 'editor-tab' } - ]) - - expect(handleSwitchTerminalTab(1)).toBe(false) - expect(store.setActiveTab).not.toHaveBeenCalled() - expect(store.setActiveTabType).not.toHaveBeenCalled() - }) -}) - -describe('handleSwitchTab', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - it('cycles terminal tabs without jumping to editor or browser tabs', () => { - const store = makeStore('terminal') - store.activeTabId = 'term-1' - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([ - { type: 'terminal', id: 'term-1' }, - { type: 'editor', id: 'file-1', tabId: 'tab-editor-1' }, - { type: 'browser', id: 'browser-1', tabId: 'tab-browser-1' }, - { type: 'terminal', id: 'term-2', tabId: 'tab-terminal-2' } - ]) - - expect(handleSwitchTab(1)).toBe(true) - expect(store.setActiveTab).toHaveBeenCalledWith('term-2') - expect(store.activateTab).toHaveBeenCalledWith('tab-terminal-2') - expect(store.setActiveFile).not.toHaveBeenCalled() - expect(store.setActiveBrowserTab).not.toHaveBeenCalled() - expect(store.setActiveTabType).toHaveBeenCalledWith('terminal', 'wt-1') - }) - - it('cycles editor tabs using the active group tab id', () => { - const store = makeStore('editor') - store.activeFileId = 'file-a' - store.activeGroupIdByWorktree = { 'wt-1': 'group-1' } - store.groupsByWorktree = { 'wt-1': [{ id: 'group-1', activeTabId: 'tab-b' }] } - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([ - { type: 'editor', id: 'file-a', tabId: 'tab-a' }, - { type: 'terminal', id: 'term-1' }, - { type: 'editor', id: 'file-a', tabId: 'tab-b' }, - { type: 'browser', id: 'browser-1', tabId: 'tab-browser-1' }, - { type: 'editor', id: 'file-c', tabId: 'tab-c' } - ]) - - expect(handleSwitchTab(1)).toBe(true) - expect(store.setActiveFile).toHaveBeenCalledWith('file-c') - expect(store.activateTab).toHaveBeenCalledWith('tab-c') - expect(store.setActiveTabType).toHaveBeenCalledWith('editor', 'wt-1') - }) - - it('cycles browser tabs without jumping to other tab types', () => { - const store = makeStore('browser') - store.activeBrowserTabId = 'browser-1' - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([ - { type: 'terminal', id: 'term-1' }, - { type: 'editor', id: 'editor-1', tabId: 'tab-editor-1' }, - { type: 'browser', id: 'browser-1', tabId: 'tab-browser-1' }, - { type: 'browser', id: 'browser-2', tabId: 'tab-browser-2' }, - { type: 'terminal', id: 'term-2' } - ]) - - expect(handleSwitchTab(1)).toBe(true) - expect(store.setActiveBrowserTab).toHaveBeenCalledWith('browser-2') - expect(store.setActiveTab).not.toHaveBeenCalled() - expect(store.setActiveFile).not.toHaveBeenCalled() - expect(store.setActiveTabType).toHaveBeenCalledWith('browser', 'wt-1') - }) - - it('falls back when an editor entry has no unified tab id', () => { - const store = makeStore('editor') - store.activeFileId = 'file-1' - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([ - { type: 'terminal', id: 'term-1' }, - { type: 'editor', id: 'file-1' }, - { type: 'editor', id: 'file-2' } - ]) - - expect(() => handleSwitchTab(1)).not.toThrow() - expect(store.setActiveFile).toHaveBeenCalledWith('file-2') - expect(store.activateTab).not.toHaveBeenCalled() - expect(store.setActiveTabType).toHaveBeenCalledWith('editor', 'wt-1') - }) -}) - -describe('handleSwitchTabAcrossAllTypes', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - it('crosses tab types, e.g. terminal → editor', () => { - const store = makeStore('terminal') - store.activeTabId = 'term-1' - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([ - { type: 'terminal', id: 'term-1' }, - { type: 'editor', id: 'file-1', tabId: 'tab-file-1' }, - { type: 'browser', id: 'browser-1', tabId: 'tab-browser-1' } - ]) - - expect(handleSwitchTabAcrossAllTypes(1)).toBe(true) - expect(store.setActiveFile).toHaveBeenCalledWith('file-1') - expect(store.activateTab).toHaveBeenCalledWith('tab-file-1') - expect(store.setActiveTabType).toHaveBeenCalledWith('editor', 'wt-1') - }) - - it('wraps around across types', () => { - const store = makeStore('browser') - store.activeBrowserTabId = 'browser-1' - store.activeGroupIdByWorktree = {} - store.groupsByWorktree = {} - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([ - { type: 'terminal', id: 'term-1' }, - { type: 'editor', id: 'file-1', tabId: 'tab-file-1' }, - { type: 'browser', id: 'browser-1', tabId: 'tab-browser-1' } - ]) - - expect(handleSwitchTabAcrossAllTypes(1)).toBe(true) - expect(store.setActiveTab).toHaveBeenCalledWith('term-1') - expect(store.setActiveTabType).toHaveBeenCalledWith('terminal', 'wt-1') - }) -}) - -describe('handleSwitchRecentTab', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - it('quick-toggles to the previously focused tab across tab types', () => { - const store = makeStore('editor') - store.activeFileId = 'file-c' - store.groupsByWorktree = { - 'wt-1': [ - { - id: 'group-1', - activeTabId: 'tab-c', - tabOrder: ['tab-a', 'tab-b', 'tab-c'], - recentTabIds: ['tab-a', 'tab-b', 'tab-c'] - } - ] - } - getStateMock.mockReturnValue(store) - getActiveTabNavOrderMock.mockReturnValue([ - { type: 'editor', id: 'file-a', tabId: 'tab-a' }, - { type: 'browser', id: 'browser-b', tabId: 'tab-b' }, - { type: 'editor', id: 'file-c', tabId: 'tab-c' } - ]) - - expect(handleSwitchRecentTab()).toBe(true) - expect(store.setActiveBrowserTab).toHaveBeenCalledWith('browser-b') - expect(store.activateTab).toHaveBeenCalledWith('tab-b') - expect(store.setActiveTabType).toHaveBeenCalledWith('browser', 'wt-1') - }) -}) From 842e4e00770a4e2ebdd8528f8f42a7768ed9297a Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:06:24 -0800 Subject: [PATCH 029/178] perf(native-chat): draw only the visible rows of an opened tool run (#41552) * perf(native-chat): draw only the visible rows of an opened tool run An opened tool run rendered every call it held, so a long autonomous stretch kept thousands of rows, their DOM and their per-row state alive while only a dozen were visible in the run's capped box. The run now owns a keyed window over its rows (the shared TanStack virtualizer plus the existing row+offset scroll anchor): - rows keep today's keys and order; small runs and Find-with-a-query draw every row through the same keyed tree, so nothing remounts at the switch - the reader is remembered as a row and offset (pixel top while unmeasured), following the end only for new work, and forgetting on close as before - the box's cap keeps the measured height of opened details, including ones scrolled out of view; the full task list now remembers its open state - the first/last rows, a diff reveal target, recently interacted rows, the focused row's Tab neighbours and a text selection's span stay drawn Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): finish tool-run follow, keep reveals, bound per-move work Review repairs for the windowed tool run: - a follow started by new work always gets its completion frame, so a result that adds no row cannot leave it armed to chase later detail growth - the row anchor restores only when rows arrive or the pane shows again, so a diff reveal or the reader's own scrolling is not pulled back - opened-detail room is kept incrementally and travels with the run's remembered position across outer windowing - focus neighbours, item keys and window rows no longer cost work per move proportional to the run's length; selection is tracked only while visible - thoughts with no words are not keyboard stops; the restored row stays drawn until the reader moves Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): finish hidden follows, let reveals win, keep draw-all scrolling free Second review repair for the windowed tool run: - a follow is certified only by a real, visible tail measurement, and that measurement wakes it even when no size changed; a hidden pane resumes it - a diff reveal cancels any pending restore and follow on every request - a hidden pane returns to its row (not its reset pixel offset) on show - draw-every-row mode (Find, short runs) rebuilds nothing as the reader scrolls; selection spans re-derive after the pane was hidden - remembered detail heights are copied only when one changes Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): certify a tool-run follow only by a laid-out tail A follow's landing is confirmed only when the last row that draws something has a real, non-zero size; a measurement callback carrying a cached or zero size no longer ends it early, and a run whose rows draw nothing has no landing to wait for. Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): keep draw-all tool runs cheap per scroll and prove follow wake paths Draw-all (Find) no longer records the scroll anchor or rebuilds the selection span on every scroll, and its trailing spacer skips the end scan. Tests now cover a task list crossing the window threshold and Find, and pin both follow-completion paths. Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): skip anchor scans under a wide selection and recover the reader's row after A selection wider than a window keeps its rows drawn, so recording the anchor on each scroll would scan them all; recording now waits, as it does under Find. The reader's row is then taken from the virtualizer's offsets when recording resumes or the pane hides, so showing it again does not return to an older row. Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): type the tool-run selection-span gate and settle the anchor on hide Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): merge duplicate tool-run window import Co-Authored-By: Claude Opus 5.5 (1M context) * chore(native-chat): prune the structured session's compiler bailout after the main sync Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): keep the tool-run measurement callback current without a render-time ref write Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): keep a reader who jumps past an open run's drawn rows where they jumped The window redraws inside the scroll that moves it, and a restore still pending from earlier work could run first and send the reader back to the row they left. The reader's own scroll now points the anchor at their row from the window's offsets before that redraw. Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): re-point the anchor on reader scrolls only while the run is windowed Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): keep a hidden tool run's saved row from its box's reset offset A hidden box resets its offset and may report that as a scroll or as the offset at unmount. Scroll recording (the run's and the anchor hook's) and the hook's unmount record now wait while the pane is hidden, so the row kept at hide time is the one a returning reader lands on. Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): give an opened tool run sole ownership of its reader's place The run and the shared scroll-anchor hook each decided when a saved row still owned the box, and each local exclusion left another path for a stale restore or capture. The run now keeps one row-and-offset place from the window's full measurement model, admits a landing only on mount or show, lets every reader scroll retire pending work first, records nothing while hidden, and takes a reveal before any landing. Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): confirm every tool-run landing correction and model the browser's scroll report in tests A real change (size, row list, the held row's measurement) now grants a landing a fresh, bounded confirmation budget, so a correction made after an earlier budget ran out is still confirmed. A pending landing's own writes no longer re-arm following. The reader harness delivers the scroll event a browser sends after an offset change. Co-Authored-By: Claude Opus 5.5 (1M context) * test(native-chat): keep the reveal-return case on one memory record and before the reveal's scroll report Co-Authored-By: Claude Opus 5.5 (1M context) * test(native-chat): return the reader harness's scroll writes to their earlier behaviour (rig-stop ruling) Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): keep a revealed file's own row as the tool run's saved place A reveal now records the card's row and offset from the page as the card aligns, rather than reading the window's size model, which may still hold that row's collapsed height. Remount tests report the landing's own scroll the way a browser would; the pinned reveal case needs no report. Co-Authored-By: Claude Opus 5.5 (1M context) * fix(native-chat): read an opened tool run's reader place from the rows actually drawn The window's size model can hold a row's old height for a frame after it grows, so a scroll report read through it could record a later row than the reader sees. Capture now takes the row at the box top from the drawn rows' own geometry, found by a binary search over the run's in-flow children, and uses the model only where the top lies in an undrawn gap; a box with no geometry keeps the last place. Co-Authored-By: Claude Opus 5.5 (1M context) * test(native-chat): type the reader-position owner test's rows without assertions Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Opus 5.5 (1M context) --- config/react-compiler-bailout-baseline.txt | 1 + config/react-compiler-uncompiled-files.txt | 2 + .../native-chat/NativeChatResolvedView.tsx | 25 +- .../NativeChatStructuredSession.tsx | 59 +- .../native-chat-disclosure-store.ts | 12 +- .../tools/runs/NativeChatToolRun.tsx | 25 +- ...iveChatToolRunMemberList.identity.test.tsx | 2 + .../runs/NativeChatToolRunMemberList.tsx | 122 ++-- .../NativeChatToolRunMembers.reader.test.tsx | 667 ++++++++++++++++++ .../tools/runs/NativeChatToolRunMembers.tsx | 353 +++++++-- .../NativeChatToolRunMembers.window.test.tsx | 146 ++++ .../tools/runs/native-chat-tool-run-rows.ts | 123 ++++ .../native-chat-tool-run-window-context.tsx | 33 + .../use-native-chat-tool-run-detail-room.ts | 80 +++ .../runs/use-native-chat-tool-run-pins.ts | 200 ++++++ ...ive-chat-tool-run-reader-position.test.tsx | 228 ++++++ ...se-native-chat-tool-run-reader-position.ts | 341 +++++++++ .../runs/use-native-chat-tool-run-window.ts | 106 +++ .../tools/tasks/NativeChatTaskList.tsx | 9 +- 19 files changed, 2366 insertions(+), 168 deletions(-) create mode 100644 src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.reader.test.tsx create mode 100644 src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.window.test.tsx create mode 100644 src/renderer/src/components/native-chat/tools/runs/native-chat-tool-run-rows.ts create mode 100644 src/renderer/src/components/native-chat/tools/runs/native-chat-tool-run-window-context.tsx create mode 100644 src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-detail-room.ts create mode 100644 src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-pins.ts create mode 100644 src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-reader-position.test.tsx create mode 100644 src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-reader-position.ts create mode 100644 src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-window.ts diff --git a/config/react-compiler-bailout-baseline.txt b/config/react-compiler-bailout-baseline.txt index 1c84381c535..f8205003fc2 100644 --- a/config/react-compiler-bailout-baseline.txt +++ b/config/react-compiler-bailout-baseline.txt @@ -250,6 +250,7 @@ 2 src/renderer/src/components/native-chat/session/terminal/read/use-native-chat-live-session.ts 3 src/renderer/src/components/native-chat/session/terminal/read/use-native-chat-retained-session.ts 1 src/renderer/src/components/native-chat/tools/lines/NativeChatToolOutput.tsx +1 src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-window.ts 1 src/renderer/src/components/native-chat/usage/NativeChatAccountUsageCard.tsx 2 src/renderer/src/components/native-chat/usage/use-native-chat-account-usage.ts 1 src/renderer/src/components/native-chat/viewport/find/use-native-chat-find.ts diff --git a/config/react-compiler-uncompiled-files.txt b/config/react-compiler-uncompiled-files.txt index 7c7b729df86..3eb96f759b9 100644 --- a/config/react-compiler-uncompiled-files.txt +++ b/config/react-compiler-uncompiled-files.txt @@ -28,6 +28,8 @@ src/renderer/src/components/editor/combined-diff/scroll-viewport/combined-diff-s src/renderer/src/components/editor/markdown-preview/search/use-markdown-preview-search-reveal.ts src/renderer/src/components/editor/markdown-preview/viewport/use-markdown-preview-scroll-anchor.ts src/renderer/src/components/github-item-dialog/inspect-pull-request/pr-files-combined-diff-body.tsx +src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.tsx +src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-window.ts src/renderer/src/components/right-sidebar/file-explorer/panel/FileExplorerVirtualRows.tsx src/renderer/src/components/right-sidebar/file-explorer/tree/FileExplorerFilesTreePane.tsx src/renderer/src/components/sidebar/worktree-list/viewport/VirtualizedWorktreeViewport.tsx diff --git a/src/renderer/src/components/native-chat/NativeChatResolvedView.tsx b/src/renderer/src/components/native-chat/NativeChatResolvedView.tsx index 959a59b78a8..678bb28d302 100644 --- a/src/renderer/src/components/native-chat/NativeChatResolvedView.tsx +++ b/src/renderer/src/components/native-chat/NativeChatResolvedView.tsx @@ -10,6 +10,7 @@ import { useNativeChatLaunchDraftSignal } from './drafts/use-native-chat-launch- import { useNativeChatRetainedSession } from './session/terminal/read/use-native-chat-retained-session' import { selectNativeChatViewState } from './session/status/native-chat-view-state' import { NativeChatMessageList } from './NativeChatMessageList' +import { NativeChatToolRunWindowProvider } from './tools/runs/native-chat-tool-run-window-context' import { useNativeChatInteractiveSendReveal, useNativeChatRevealLatest @@ -349,17 +350,19 @@ export function NativeChatResolvedView({ ) : viewState.kind === 'empty' ? ( ) : ( - + + + )} {/* A collapsed card stays mounted but hidden, so a partly answered question survives. */} diff --git a/src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx b/src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx index 16d82549415..2ee97beb567 100644 --- a/src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx +++ b/src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx @@ -20,6 +20,7 @@ import { NativeChatEmptyState } from '@/components/native-chat/host-access/Nativ import { NativeChatEmptyPane } from '@/components/native-chat/host-access/NativeChatEmptyPane' import { NativeChatLoadingCue } from '@/components/native-chat/host-access/NativeChatLoadingCue' import { NativeChatMessageList } from '@/components/native-chat/NativeChatMessageList' +import { NativeChatToolRunWindowProvider } from '@/components/native-chat/tools/runs/native-chat-tool-run-window-context' import { NativeChatContextClearFade } from '@/components/native-chat/conversation/projection/NativeChatContextClearFade' import { useStructuredNativeChatSubmitReveal } from '@/components/native-chat/pane/actions/use-structured-native-chat-submit-reveal' import { useStructuredPromptResponseHold } from '@/components/native-chat/prompts/use-structured-prompt-response-hold' @@ -301,34 +302,36 @@ export function NativeChatStructuredSession(props: Omit - - {(timedRows) => ( - - )} - + + + {(timedRows) => ( + + )} + + diff --git a/src/renderer/src/components/native-chat/messages/disclosure/native-chat-disclosure-store.ts b/src/renderer/src/components/native-chat/messages/disclosure/native-chat-disclosure-store.ts index 254f3e048d7..b073fa17249 100644 --- a/src/renderer/src/components/native-chat/messages/disclosure/native-chat-disclosure-store.ts +++ b/src/renderer/src/components/native-chat/messages/disclosure/native-chat-disclosure-store.ts @@ -2,8 +2,16 @@ import { createContext, useCallback, useContext, useMemo, useState } from 'react' -/** Where the reader left an opened run's own scroll box. */ -export type NativeChatRunScroll = { top: number; followsEnd: boolean } +/** A reader's place in an opened run's box: a row, and how far into that row its top edge sits. */ +export type NativeChatRunPosition = { key: string; offset: number } + +/** Where the reader left an opened run's own scroll box: a row and offset, or the end. */ +export type NativeChatRunScroll = { + position: NativeChatRunPosition | null + followsEnd: boolean + /** Measured heights of details opened in rows the window no longer draws, so the box keeps their room. */ + detailHeights: ReadonlyMap +} export type NativeChatDisclosureStore = { read: (key: string) => boolean | undefined diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRun.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRun.tsx index ac01158e0f2..59e4432ce34 100644 --- a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRun.tsx +++ b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRun.tsx @@ -28,7 +28,7 @@ import type { } from '../../subagents/native-chat-subagent-sections' import { NativeChatToolRunIcon } from '../lines/NativeChatToolIcon' import { NativeChatToolRunCallCounts } from './NativeChatToolRunCallCounts' -import { NativeChatToolRunMembers, revealNativeChatToolRunMember } from './NativeChatToolRunMembers' +import { revealNativeChatToolRunMember } from './NativeChatToolRunMembers' import { NativeChatToolRunMemberList } from './NativeChatToolRunMemberList' import { NativeChatBrowserRunPageButton } from './NativeChatBrowserPageButton' import { NativeChatImageAttachments } from '../../media/images/NativeChatImageAttachments' @@ -243,22 +243,19 @@ export function NativeChatToolRun({ className="mb-0 mt-1" /> - - - + />
) diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMemberList.identity.test.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMemberList.identity.test.tsx index dbcbff38e64..7cd45c2fa12 100644 --- a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMemberList.identity.test.tsx +++ b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMemberList.identity.test.tsx @@ -43,6 +43,8 @@ function MemberDisclosureHarness({ blocks={blocks} headerBlocks={blocks} disclosureId="message-1" + followKey={blocks.length} + startsAtEnd={false} /> ) : null} diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMemberList.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMemberList.tsx index 7b15187ab20..35b179b6144 100644 --- a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMemberList.tsx +++ b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMemberList.tsx @@ -1,17 +1,15 @@ -import { useMemo } from 'react' +import { useCallback, useMemo } from 'react' import type { NativeChatBlock, NativeChatToolCallBlock } from '../../../../../../shared/native-chat/native-chat-types' -import { pairNativeChatToolResults } from '../../../../../../shared/native-chat/tools/native-chat-tool-pairing' -import { nativeChatToolLineIdentity } from '../../../../../../shared/native-chat/tools/native-chat-tool-line-identity' import { NativeChatDiffCard } from '../diffs/NativeChatDiffCard' import { NativeChatTaskList } from '../tasks/NativeChatTaskList' import { NativeChatToolLine } from '../lines/NativeChatToolLine' -import { buildEditCards } from '../../../../../../shared/native-chat/tools/native-chat-edit-cards' -import { buildNativeChatTaskListRows } from '../../../../../../shared/native-chat/input/native-chat-task-list-history' import type { NativeChatDiffReveal } from '../../../../../../shared/native-chat/tools/native-chat-turn-diffs' import type { NativeChatToolRunAsides } from './NativeChatToolRun' +import { buildNativeChatToolRunRows, type NativeChatToolRunRow } from './native-chat-tool-run-rows' +import { NativeChatToolRunMembers } from './NativeChatToolRunMembers' /** Mount only while expanded so collapsed runs avoid member pairing and diff work. */ export function NativeChatToolRunMemberList({ @@ -22,7 +20,10 @@ export function NativeChatToolRunMemberList({ revealedDiff, onRevealDiff, disclosureId, - asides + asides, + followKey, + startsAtEnd, + memoryKey }: { /** Every tool block of the run; `headerBlocks` is those minus its asks. */ blocks: NativeChatBlock[] @@ -34,40 +35,44 @@ export function NativeChatToolRunMemberList({ revealedDiff?: NativeChatDiffReveal onRevealDiff?: (element: HTMLElement) => void disclosureId?: string + followKey: number + startsAtEnd: boolean + memoryKey?: string }): React.JSX.Element { - const taskLists = useMemo( + // One flat keyed row list with or without asides, so a row gaining its first never remounts a line. + const rows = useMemo( () => - buildNativeChatTaskListRows(blocks, { - todowrite: previousTodoWrite, - update_plan: previousUpdatePlan + buildNativeChatToolRunRows({ + blocks, + headerBlocks, + asides, + previousTodoWrite, + previousUpdatePlan }), - [blocks, previousTodoWrite, previousUpdatePlan] - ) - const { editCards, consumedResults } = useMemo(() => buildEditCards(blocks), [blocks]) - const { resultByCall, pairedResults } = useMemo( - () => pairNativeChatToolResults(headerBlocks), - [headerBlocks] + [asides, blocks, headerBlocks, previousTodoWrite, previousUpdatePlan] ) - const seen = new Map() - const member = (block: NativeChatBlock, blockIndex: number): React.ReactNode => { - const taskList = taskLists.rows.get(block) - if (taskList) { - return - } - if (taskLists.consumedResults.has(block)) { - return null - } - const edit = editCards.get(block) - if (edit) { - return ( -
- {edit.files.map((file, fileIndex) => ( + const renderRow = useCallback( + (row: NativeChatToolRunRow): React.ReactNode => { + switch (row.kind) { + case 'aside': + return row.element + case 'tasks': + return ( + + ) + case 'edit': + return row.files.map((file, fileIndex) => ( - ))} -
- ) - } - // A result its call now owns is drawn by that call's line, not as a row of its own. - if (consumedResults.has(block) || pairedResults.has(block)) { - return null - } - const lineIdentity = nativeChatToolLineIdentity(block, seen) - return ( - - ) - } + )) + case 'line': + return ( + + ) + } + }, + [disclosureId, onRevealDiff, revealedDiff] + ) - // One flat keyed list with or without asides, so a row gaining its first never remounts a line. return ( - <> - {headerBlocks.flatMap((block, blockIndex) => [ - ...(asides?.before.get(block) ?? []), - member(block, blockIndex) - ])} - {asides?.after} - + ) } diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.reader.test.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.reader.test.tsx new file mode 100644 index 00000000000..f5146e6968c --- /dev/null +++ b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.reader.test.tsx @@ -0,0 +1,667 @@ +// @vitest-environment happy-dom + +import '@testing-library/jest-dom/vitest' +import { useMemo } from 'react' +import { act, cleanup, fireEvent, render } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { NativeChatBlock } from '../../../../../../shared/native-chat/native-chat-types' +import type { NativeChatDiffReveal } from '../../../../../../shared/native-chat/tools/native-chat-turn-diffs' +import { NativeChatToolRun } from './NativeChatToolRun' +import { NativeChatToolRunWindowContext } from './native-chat-tool-run-window-context' +import { + NativeChatDisclosureContext, + useNativeChatDisclosures +} from '../../messages/disclosure/native-chat-disclosure-store' + +const BOX_HEIGHT_PX = 288 +/** Measured rows are taller than the 26px estimate, so a pixel position alone lands on the wrong row. */ +const ROW_HEIGHT_PX = 40 +/** Every row's laid-out height unless a test says otherwise. */ +const layout = { rowPx: ROW_HEIGHT_PX } +const ROW = '[data-native-chat-tool-run-row]' +/** Rows that have grown in place, by row key. */ +const grownRows = new Map() +/** A hidden pane is display:none: nothing in it has a size. */ +const pane = { hidden: false } + +const resizeObservers = new Set() +class StubResizeObserver implements ResizeObserver { + readonly targets = new Set() + constructor(readonly callback: ResizeObserverCallback) { + resizeObservers.add(this) + } + observe(target: Element): void { + this.targets.add(target) + } + unobserve(target: Element): void { + this.targets.delete(target) + } + disconnect(): void { + this.targets.clear() + resizeObservers.delete(this) + } +} + +/** Deliver what a browser's ResizeObserver would after `element` changed size. */ +function resized(element: Element, height: number): void { + for (const observer of resizeObservers) { + if (observer.targets.has(element)) { + const size = { blockSize: height, inlineSize: 400 } + const entry = { + target: element, + contentRect: element.getBoundingClientRect(), + borderBoxSize: [size], + contentBoxSize: [size], + devicePixelContentBoxSize: [size] + } satisfies ResizeObserverEntry + observer.callback([entry], observer) + } + } +} + +function rowHeight(element: Element): number { + return grownRows.get(element.getAttribute('data-native-chat-tool-run-row') ?? '') ?? layout.rowPx +} + +/** happy-dom lays nothing out: stack the run box's children by their stated heights. */ +function layOutRunBox(): void { + const heightOf = (element: Element): number => { + if (pane.hidden) { + return 0 + } + if (element.matches(ROW)) { + return rowHeight(element) + } + return element instanceof HTMLElement ? Number.parseFloat(element.style.height) || 0 : 0 + } + const box = (element: Element): HTMLElement | null => + element.closest('[data-native-chat-tool-run-members]') + vi.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockImplementation(function ( + this: HTMLElement + ) { + if (pane.hidden) { + return 0 + } + if (this.hasAttribute('data-native-chat-tool-run-members')) { + return BOX_HEIGHT_PX + } + return this.matches(ROW) ? rowHeight(this) : 0 + }) + vi.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(400) + vi.spyOn(HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(function ( + this: HTMLElement + ) { + return this.hasAttribute('data-native-chat-tool-run-members') ? BOX_HEIGHT_PX : 0 + }) + vi.spyOn(HTMLElement.prototype, 'scrollHeight', 'get').mockImplementation(function ( + this: HTMLElement + ) { + const content = this.hasAttribute('data-native-chat-tool-run-members') + ? this.firstElementChild + : null + return content ? Array.from(content.children).reduce((sum, c) => sum + heightOf(c), 0) : 0 + }) + // A browser clamps scrollTop to what the box can reach; an unclamped box would hide a wrong scroll. + const scrollTops = new WeakMap() + vi.spyOn(Element.prototype, 'scrollTop', 'get').mockImplementation(function (this: Element) { + return scrollTops.get(this) ?? 0 + }) + vi.spyOn(Element.prototype, 'scrollTop', 'set').mockImplementation(function ( + this: Element, + value: number + ) { + const max = this instanceof HTMLElement ? Math.max(0, this.scrollHeight - this.clientHeight) : 0 + scrollTops.set(this, Math.min(Math.max(0, value), max)) + }) + vi.spyOn(Element.prototype, 'getBoundingClientRect').mockImplementation(function (this: Element) { + const scroller = box(this) + if (!scroller || pane.hidden) { + return DOMRect.fromRect({ x: 0, y: 0, width: 0, height: 0 }) + } + if (this === scroller) { + return DOMRect.fromRect({ x: 0, y: 0, width: 400, height: BOX_HEIGHT_PX }) + } + const row = this.closest(ROW) ?? this + let top = -scroller.scrollTop + for (const child of Array.from(scroller.firstElementChild?.children ?? [])) { + if (child === row) { + return DOMRect.fromRect({ x: 0, y: top, width: 400, height: heightOf(child) }) + } + top += heightOf(child) + } + return DOMRect.fromRect({ x: 0, y: 0, width: 0, height: 0 }) + }) +} + +function calls(count: number): NativeChatBlock[] { + return Array.from({ length: count }, (_, index) => ({ + type: 'tool-call' as const, + name: 'Read', + callId: `call-${index}`, + input: { file_path: `src/file-${index}.ts` } + })) +} + +const PATCH = + '*** Begin Patch\n*** Update File: first.ts\n ctx\n-was\n+now\n*** Update File: second.ts\n@@ -1,1 +1,1 @@\n-a\n+b\n*** End Patch' + +/** A long settled run with one two-file edit far from its top. */ +function editRun(count: number, editAt: number): NativeChatBlock[] { + const blocks = calls(count) + blocks.splice( + editAt, + 0, + { + type: 'tool-call', + name: 'apply_patch', + callId: 'edit-1', + input: { input: PATCH }, + state: 'completed' + }, + { type: 'tool-result', callId: 'edit-1', output: 'Success. Updated the following files' } + ) + return blocks +} + +// Stable, as a real transcript's callback is: a new one each render would make every card reveal again. +const ignoreRevealTarget = (): void => {} + +function Transcript({ + blocks, + mounted = true, + live = false, + visible = true, + find = false, + revealedDiff +}: { + blocks: NativeChatBlock[] + mounted?: boolean + live?: boolean + visible?: boolean + find?: boolean + revealedDiff?: NativeChatDiffReveal +}) { + const disclosures = useNativeChatDisclosures() + const windowFacts = useMemo(() => ({ isVisible: visible, findActive: find }), [visible, find]) + return ( + + + {mounted ? ( + + ) : null} + + + ) +} + +function membersBox(container: HTMLElement): HTMLElement { + const element = container.querySelector('[data-native-chat-tool-run-members]') + if (!element) { + throw new Error('members box did not render') + } + return element +} + +/** The row at the box's top edge and how far into it the box starts. */ +function topRow(container: HTMLElement): { key: string | null; offset: number } { + for (const row of Array.from(container.querySelectorAll(ROW))) { + const rect = row.getBoundingClientRect() + if (rect.bottom > 0) { + return { key: row.getAttribute('data-native-chat-tool-run-row'), offset: -rect.top } + } + } + return { key: null, offset: 0 } +} + +/** Deliver frames and the anchor's idle record on the fake clock: no wall-clock waits. */ +async function settle(): Promise { + for (let pass = 0; pass < 4; pass++) { + await act(async () => { + vi.advanceTimersByTime(250) + }) + } +} + +/** A drawn row's laid-out height changes, as a browser's ResizeObserver reports it. */ +async function growRow(container: HTMLElement, key: string, height: number): Promise { + const row = container.querySelector(`[data-native-chat-tool-run-row="${key}"]`) + if (!row) { + throw new Error(`row ${key} is not drawn`) + } + grownRows.set(key, height) + await act(async () => { + resized(row, height) + }) + await settle() +} + +async function scrollBox(container: HTMLElement, top: number): Promise { + const box = membersBox(container) + await act(async () => { + box.scrollTop = top + fireEvent.scroll(box) + }) + await settle() +} + +/** The browser's report of a landing's own scroll write, which this harness does not send: it may only + * let the window catch up to where the box already is, never move it. */ +async function reportLandingScroll(container: HTMLElement): Promise { + const box = membersBox(container) + const landed = box.scrollTop + await act(async () => { + fireEvent.scroll(box) + }) + await settle() + expect(box.scrollTop).toBe(landed) +} + +/** The browser's first size report for each drawn row, which it sends in the frame a row appears and this + * harness only sends when asked: rows the window drew while it believed the box was scrolling get theirs here. */ +async function reportDrawnRowSizes(container: HTMLElement): Promise { + await act(async () => { + for (const row of Array.from(container.querySelectorAll(ROW))) { + resized(row, rowHeight(row)) + } + }) + await settle() +} + +describe('NativeChatToolRun opened-run reader', () => { + beforeEach(() => { + vi.useFakeTimers({ + toFake: ['setTimeout', 'clearTimeout', 'requestAnimationFrame', 'cancelAnimationFrame'] + }) + grownRows.clear() + layout.rowPx = ROW_HEIGHT_PX + pane.hidden = false + vi.stubGlobal('ResizeObserver', StubResizeObserver) + layOutRunBox() + }) + afterEach(() => { + cleanup() + vi.useRealTimers() + vi.restoreAllMocks() + vi.unstubAllGlobals() + }) + + it('returns to the same row and offset after the transcript unmounts it and work arrives', async () => { + const { container, rerender } = render() + await settle() + await scrollBox(container, 9_013) + const before = topRow(container) + expect(before.key).not.toBeNull() + expect(before.offset).toBeGreaterThan(0) + + rerender() + rerender() + // Evicted again before that return lands: the place it was landing on is what it keeps. + rerender() + rerender() + await settle() + await reportLandingScroll(container) + + const after = topRow(container) + expect(after.key).toBe(before.key) + expect(Math.abs(after.offset - before.offset)).toBeLessThanOrEqual(1) + }) + + it('keeps a revealed edit at the top over the place the reader had scrolled to', async () => { + const blocks = editRun(600, 400) + const { container, rerender } = render() + await settle() + await scrollBox(container, 2_000) + const reveal = { messageId: 'message-1', editKey: 'apply_patch:400', fileIndex: 1 } + + rerender() + await settle() + expect(topRow(container).key).toBe('edit:apply_patch:400') + + await scrollBox(container, 2_000) + rerender() + await settle() + expect(topRow(container).key).toBe('edit:apply_patch:400') + }) + + it('forgets its place when the reader closes and reopens it', async () => { + const { container } = render() + await settle() + await scrollBox(container, 9_013) + const header = container.querySelector('[data-native-chat-tool-run-state]') + if (!header) { + throw new Error('run header did not render') + } + await act(async () => { + fireEvent.click(header) + }) + await settle() + await act(async () => { + fireEvent.click(header) + }) + await settle() + expect(membersBox(container).scrollTop).toBe(0) + expect(topRow(container).key).toBe('call:call-0') + }) + + it('follows new work at the end, but not a detail growing after it lands', async () => { + const { container, rerender } = render() + await settle() + const box = membersBox(container) + const atEnd = (): boolean => box.scrollTop >= box.scrollHeight - BOX_HEIGHT_PX - 1 + expect(atEnd()).toBe(true) + + // A burst larger than the window, then a result that adds no row. + rerender() + await settle() + expect(atEnd()).toBe(true) + const withResult = [ + ...calls(260), + { type: 'tool-result' as const, callId: 'call-259', output: 'done' } + ] + rerender() + await settle() + expect(atEnd()).toBe(true) + + // In-place growth that is not new work leaves the reader where they are. + const landed = box.scrollTop + const tail = container.querySelector('[data-native-chat-tool-run-row="call:call-259"]') + if (!tail) { + throw new Error('tail row is not drawn') + } + grownRows.set('call:call-259', 400) + await act(async () => { + resized(tail, 400) + }) + await settle() + expect(box.scrollTop).toBe(landed) + + // A reader scrolled away stays put when more work arrives. + await scrollBox(container, 1_000) + rerender() + await settle() + expect(box.scrollTop).toBe(1_000) + }) + + it('lets a reveal win over a restore that has not landed yet', async () => { + const blocks = editRun(600, 400) + const { container, rerender } = render() + await settle() + await scrollBox(container, 2_000) + rerender() + rerender() + // No settling: the returning restore is still pending when the reveal arrives. + const reveal = { messageId: 'message-1', editKey: 'apply_patch:400', fileIndex: 1 } + rerender() + await settle() + expect(topRow(container).key).toBe('edit:apply_patch:400') + + // A reveal made in the same render that shows the pane again wins over the place it kept while hidden, + // and is what the run keeps even before the browser reports the reveal's scroll: hidden again at once and + // shown under the same request (so no fresh reveal), it returns to the revealed edit. + await scrollBox(container, 2_000) + pane.hidden = true + rerender( + + ) + pane.hidden = false + rerender() + pane.hidden = true + rerender( + + ) + pane.hidden = false + rerender() + await settle() + // The revealed row stays drawn while its request stands, so no scroll report is needed to see it. + expect(topRow(container).key).toBe('edit:apply_patch:400') + }) + + it('stops following when the reader scrolls before new work has settled', async () => { + const { container, rerender } = render() + await settle() + const box = membersBox(container) + rerender() + await act(async () => { + box.scrollTop = 1_000 + fireEvent.scroll(box) + }) + await settle() + expect(box.scrollTop).toBe(1_000) + }) + + it('finishes a follow that started while the pane was hidden once it shows again', async () => { + const { container, rerender } = render() + await settle() + pane.hidden = true + rerender() + rerender() + await settle() + pane.hidden = false + rerender() + await settle() + const box = membersBox(container) + expect(box.scrollTop).toBe(box.scrollHeight - BOX_HEIGHT_PX) + + // Once landed, growth that is not new work leaves the reader where they are. + const landed = box.scrollTop + await growRow(container, 'call:call-229', 300) + expect(box.scrollTop).toBe(landed) + }) + + it('finishes a follow whose tail first measures exactly as estimated', async () => { + // 26px rows: a live run opens following before any row is drawn; the tail's first real + // measurement then changes no size, so only that measurement can end the follow. + layout.rowPx = 26 + const { container } = render() + await settle() + const box = membersBox(container) + expect(box.scrollTop).toBe(box.scrollHeight - BOX_HEIGHT_PX) + // The follow ended, so growth that is not new work leaves the reader in place. + const landed = box.scrollTop + await growRow(container, 'call:call-199', 300) + expect(box.scrollTop).toBe(landed) + }) + + it('waits for a tail with no size yet, then lands on it once it has one', async () => { + const { container, rerender } = render() + await settle() + grownRows.set('call:call-259', 0) + rerender() + await settle() + // A measurement with no size does not count as landing. + await growRow(container, 'call:call-259', 0) + // The tail now lays out: the follow that waited for it lands on the new end. + await growRow(container, 'call:call-259', 40) + const box = membersBox(container) + expect(box.scrollTop).toBe(box.scrollHeight - BOX_HEIGHT_PX) + const landed = box.scrollTop + await growRow(container, 'call:call-259', 300) + expect(box.scrollTop).toBe(landed) + }) + + it('returns to where the reader scrolled with Find open after the pane hides and shows', async () => { + const { container, rerender } = render() + await settle() + rerender() + await settle() + await scrollBox(container, 9_013) + const before = topRow(container) + expect(before.key).toBe('call:call-225') + + // A hidden box loses its offset; only the remembered row brings the reader back. + pane.hidden = true + rerender() + membersBox(container).scrollTop = 0 + pane.hidden = false + rerender() + await settle() + expect(topRow(container)).toEqual(before) + + // The landing finished without any size change: later growth elsewhere leaves the reader in place. + await growRow(container, 'call:call-900', 300) + expect(topRow(container)).toEqual(before) + }) + + it('keeps a reader who jumps past the drawn rows of a live run where they jumped', async () => { + layout.rowPx = 26 + const { container, rerender } = render() + await settle() + const box = membersBox(container) + const opened = container.querySelector('[data-native-chat-tool-run-row="call:call-28"] button') + if (!opened) { + throw new Error('row call-28 is not drawn') + } + await act(async () => { + fireEvent.click(opened) + }) + await growRow(container, 'call:call-28', 68) + for (let count = 30; count <= 62; count++) { + rerender() + await settle() + } + + // A scrollbar drag lands far from every drawn row; then work arrives. + await scrollBox(container, 683) + rerender() + await settle() + expect(box.scrollTop).toBe(683) + }) + + for (const mode of ['small run shown again', 'long run with Find'] as const) { + it(`lets a reader scroll away from a followed live run without a jump (${mode})`, async () => { + layout.rowPx = 26 + const find = mode === 'long run with Find' + const first = find ? 200 : 30 + const { container, rerender } = render() + await settle() + if (!find) { + pane.hidden = true + rerender() + pane.hidden = false + rerender() + await settle() + } + for (let count = first + 1; count <= first + 15; count++) { + rerender() + await settle() + } + const box = membersBox(container) + const away = box.scrollHeight - BOX_HEIGHT_PX - 120 + await scrollBox(container, away) + expect(box.scrollTop).toBe(away) + rerender() + await settle() + expect(box.scrollTop).toBe(away) + }) + } + + for (const hiddenAfterMs of [100, 160]) { + it(`keeps the reader's row through a hidden pane that reports a scroll of its own (hidden ${hiddenAfterMs}ms after a scroll)`, async () => { + const { container, rerender } = render() + await settle() + await scrollBox(container, 9_013) + await reportDrawnRowSizes(container) + + // Hidden shortly after one more scroll, before anything deferred from it could settle. + await act(async () => { + fireEvent.scroll(membersBox(container)) + vi.advanceTimersByTime(hiddenAfterMs) + }) + // The reader's place as the pane hides is the one that must come back. + const before = topRow(container) + pane.hidden = true + rerender() + // A hidden box loses its offset and may report that as a scroll; the transcript then unmounts the run. + await scrollBox(container, 0) + rerender() + pane.hidden = false + rerender() + await settle() + await reportLandingScroll(container) + await reportDrawnRowSizes(container) + expect(topRow(container)).toEqual(before) + }) + } + + it('keeps a parked reader on its row through Find, the window threshold and a selection', async () => { + const { container, rerender } = render() + await settle() + await scrollBox(container, 613) + const before = topRow(container) + + rerender() + await settle() + expect(topRow(container)).toEqual(before) + rerender() + await settle() + expect(topRow(container)).toEqual(before) + rerender() + await settle() + expect(topRow(container)).toEqual(before) + + const box = membersBox(container) + await act(async () => { + document.getSelection()?.selectAllChildren(box) + document.dispatchEvent(new Event('selectionchange')) + }) + await settle() + expect(topRow(container)).toEqual(before) + await act(async () => { + document.getSelection()?.removeAllRanges() + document.dispatchEvent(new Event('selectionchange')) + }) + await settle() + expect(topRow(container)).toEqual(before) + }) + + it("lands on the reader's row once a pane shown before its layout finally lays out", async () => { + const { container, rerender } = render() + await settle() + await scrollBox(container, 9_013) + const before = topRow(container) + + pane.hidden = true + rerender() + membersBox(container).scrollTop = 0 + // Shown, but its box has no geometry for longer than any landing waits on frames alone. + rerender() + await settle() + await settle() + // Layout arrives; the held row reports a size that changes nothing. + pane.hidden = false + await growRow(container, before.key ?? '', 40) + expect(topRow(container)).toEqual(before) + + await growRow(container, 'call:call-900', 300) + expect(topRow(container)).toEqual(before) + }) + + it('does not keep a selection span drawn after it ended while the pane was hidden', async () => { + const { container, rerender } = render() + await settle() + const box = membersBox(container) + await act(async () => { + document.getSelection()?.selectAllChildren(box) + document.dispatchEvent(new Event('selectionchange')) + }) + expect(container.querySelectorAll(ROW).length).toBe(1_000) + + pane.hidden = true + rerender() + await act(async () => { + document.getSelection()?.removeAllRanges() + document.dispatchEvent(new Event('selectionchange')) + }) + pane.hidden = false + rerender() + await settle() + expect(container.querySelectorAll(ROW).length).toBeLessThan(100) + }) +}) diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.tsx index 0b13e5a9e63..ad8d99a461f 100644 --- a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.tsx +++ b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.tsx @@ -1,5 +1,17 @@ -import { useCallback, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react' +import { + Fragment, + memo, + useCallback, + useContext, + useEffect, + useLayoutEffect, + useMemo, + useRef, + useState +} from 'react' import { cn } from '@/lib/class-names' +import { createProgrammaticScrollMarks } from '@/hooks/programmatic-scroll-marks' +import { VIRTUALIZED_LIST_MIN_ROWS } from '@/components/virtualized-list' import { distanceFromBottom, NATIVE_CHAT_FOLLOW_REARM_PX @@ -9,14 +21,18 @@ import { MAX_NATIVE_CHAT_DISCLOSURES, NativeChatDisclosureContext } from '../../messages/disclosure/native-chat-disclosure-store' - -function openDetailHeight(content: HTMLElement): number { - let height = 0 - for (const detail of content.querySelectorAll('[data-native-chat-member-detail]')) { - height += detail.offsetHeight - } - return height -} +import { NativeChatToolRunWindowContext } from './native-chat-tool-run-window-context' +import type { NativeChatToolRunRow } from './native-chat-tool-run-rows' +import { ROW_KEY_ATTRIBUTE, useNativeChatToolRunPins } from './use-native-chat-tool-run-pins' +import { + useNativeChatToolRunWindow, + type NativeChatToolRunDrawnRow +} from './use-native-chat-tool-run-window' +import { useNativeChatToolRunDetailRoom } from './use-native-chat-tool-run-detail-room' +import { + claimNativeChatToolRunReveal, + useNativeChatToolRunReaderPosition +} from './use-native-chat-tool-run-reader-position' /** Scroll inside the run, then return its box for transcript alignment; member offsets omit internal scroll. */ export function revealNativeChatToolRunMember(member: HTMLElement): HTMLElement { @@ -25,97 +41,326 @@ export function revealNativeChatToolRunMember(member: HTMLElement): HTMLElement return member } scroller.scrollTop += member.getBoundingClientRect().top - scroller.getBoundingClientRect().top + // The revealed card is now the reader's place: a pending landing or follow must not move it again. + claimNativeChatToolRunReveal(scroller, member) return scroller } -/** An opened run's members in a bounded scroll box, so live work fills it instead of lengthening the page. */ +/** A drawn row re-renders only when its own row or renderer changes, not on every window move. */ +const WindowRow = memo(function WindowRow({ + row, + renderRow +}: { + row: NativeChatToolRunRow + renderRow: (row: NativeChatToolRunRow) => React.ReactNode +}): React.ReactNode { + return renderRow(row) +}) + +/** The drawn rows; unchanged in draw-every-row mode while the reader scrolls, so that mode costs nothing per move. */ +const WindowRows = memo(function WindowRows({ + drawn, + rows, + renderRow, + measureRow +}: { + drawn: readonly NativeChatToolRunDrawnRow[] + rows: readonly NativeChatToolRunRow[] + renderRow: (row: NativeChatToolRunRow) => React.ReactNode + measureRow: (node: HTMLDivElement | null) => void +}): React.ReactNode { + const children: React.ReactNode[] = [] + let previousEnd = 0 + for (const { index, start, end } of drawn) { + const row = rows[index] + if (!row) { + continue + } + children.push( + + {start > previousEnd + 0.5 ? ( +
+ ) : null} +
+ +
+ + ) + previousEnd = Math.max(previousEnd, end) + } + return children +}) + +/** An opened run's rows in a bounded scroll box that draws only what the reader can reach, so a long run costs a window, not its length. */ export function NativeChatToolRunMembers({ + rows, + renderRow, followKey, startsAtEnd, memoryKey, - children + revealRowKey }: { + rows: readonly NativeChatToolRunRow[] + renderRow: (row: NativeChatToolRunRow) => React.ReactNode /** How much work the run holds: a reader at the end follows it growing. Opening a detail must not count. */ followKey: number /** A live run opens on what it is doing now; a settled one reads from its top. */ startsAtEnd: boolean /** The run's disclosure key. Windowing unmounts an off-screen row; its box comes back as the reader left it. */ memoryKey?: string - children: React.ReactNode + /** The edit row a diff reveal targets; its card does the scrolling once drawn. */ + revealRowKey?: string }): React.JSX.Element { const scrollerRef = useRef(null) - const contentRef = useRef(null) - const [fades, setFades] = useState({ top: false, bottom: false }) + const { isVisible, findActive } = useContext(NativeChatToolRunWindowContext) const runScroll = useContext(NativeChatDisclosureContext)?.runScroll const [remembered] = useState(() => memoryKey === undefined ? undefined : runScroll?.get(memoryKey) ) + const returningTo = + remembered && !remembered.followsEnd ? (remembered.position ?? undefined) : undefined const followsEndRef = useRef(remembered?.followsEnd ?? startsAtEnd) - + // Drawn while a landing is pending, so it lands on the real row rather than an estimate. + const [heldRowKey, setHeldRowKey] = useState(returningTo?.key) + const [marks] = useState(createProgrammaticScrollMarks) + const [fades, setFades] = useState({ top: false, bottom: false }) + // The row whose real measurement says new work has landed: the last one that draws something + // (a thought with no words draws nothing, so it can never be measured). + const followTailKey = useMemo( + () => rows.findLast((row) => row.kind !== 'aside' || row.focusable)?.key, + [rows] + ) + const isVisibleRef = useRef(isVisible) + const followTailKeyRef = useRef(followTailKey) useLayoutEffect(() => { - if (scrollerRef.current && remembered && !remembered.followsEnd) { - scrollerRef.current.scrollTop = remembered.top - } - }, [remembered]) + isVisibleRef.current = isVisible + followTailKeyRef.current = followTailKey + }) - // Before paint: follow only if the reader was at the end before the new work. - useLayoutEffect(() => { + const rowIndexByKey = useMemo(() => new Map(rows.map((row, index) => [row.key, index])), [rows]) + // Below the threshold, or while Find searches drawn text, every row is drawn; one keyed tree serves both modes. + const drawsEveryRow = rows.length < VIRTUALIZED_LIST_MIN_ROWS || findActive + const pins = useNativeChatToolRunPins({ + rows, + rowIndexByKey, + revealRowKey, + heldRowKey, + drawsEveryRow, + isVisible, + scrollerRef + }) + const detailRoom = useNativeChatToolRunDetailRoom({ + scrollerRef, + rowIndexByKey, + initial: remembered?.detailHeights + }) + // Follow transaction: armed by new work while the reader is at the end; done once the tail has really been measured. + const followRef = useRef<{ observedTail: boolean } | null>(null) + // A real tail measurement wakes the follow pass even when it changed no size. + const [tailObservations, setTailObservations] = useState(0) + // The reader-position owner exists after the window; a measured landing row reaches it through here. + const onRowMeasuredRef = useRef<(key: string) => void>(() => {}) + const onMeasure = useCallback( + (element: HTMLDivElement) => { + const key = element.getAttribute(ROW_KEY_ATTRIBUTE) + if (key === null || !isVisibleRef.current) { + return + } + detailRoom.record(key, element) + onRowMeasuredRef.current(key) + const follow = followRef.current + // Only a laid-out tail counts: a callback can carry a cached or zero size. + if ( + follow && + !follow.observedTail && + key === followTailKeyRef.current && + element.getBoundingClientRect().height > 0 + ) { + follow.observedTail = true + setTailObservations((count) => count + 1) + } + }, + [detailRoom] + ) + const { virtualizer, measureRow, totalSize, drawn } = useNativeChatToolRunWindow({ + rows, + scrollerRef, + isVisible, + drawsEveryRow, + rangeExtractor: pins.rangeExtractor, + marks, + onMeasure + }) + + const pinToEnd = useCallback(() => { const scroller = scrollerRef.current - if (scroller && followsEndRef.current) { + if (scroller && scroller.scrollTop < scroller.scrollHeight - scroller.clientHeight - 1) { + marks.mark(scroller.scrollHeight) scroller.scrollTop = scroller.scrollHeight } - }, [followKey]) + }, [marks]) + + const onRevealed = useCallback(() => { + followRef.current = null + followsEndRef.current = false + }, []) + const reader = useNativeChatToolRunReaderPosition({ + scrollerRef, + virtualizer, + rows, + rowIndexByKey, + isVisible, + followsEndRef, + marks, + totalSize, + initial: returningTo, + setHeldRowKey, + onRevealed + }) + + useLayoutEffect(() => { + onRowMeasuredRef.current = reader.onRowMeasured + }, [reader]) + + // A following reader whose pane shows again goes back to the end. + const wasVisibleRef = useRef(isVisible) + useLayoutEffect(() => { + if (wasVisibleRef.current !== isVisible) { + wasVisibleRef.current = isVisible + if (isVisible && followsEndRef.current) { + followRef.current = { observedTail: false } + } + } + }, [isVisible]) + + // Before paint: new work starts a follow only if the reader was at the end before it. Each visible + // pass re-pins; once the tail is really measured (now, on a later measurement, or when a hidden pane + // shows again), one frame without tail growth ends it. + const followKeyRef = useRef(null) + useLayoutEffect(() => { + if (followKeyRef.current !== followKey) { + followKeyRef.current = followKey + if (followsEndRef.current) { + followRef.current = { observedTail: false } + } + } + const follow = followRef.current + if (!follow || !isVisible) { + return + } + if (!follow.observedTail && followTailKeyRef.current === undefined) { + // Nothing in the run draws: there is no landing to wait for. + follow.observedTail = true + } + if (!follow.observedTail) { + const tail = followTailKeyRef.current + const tailElement = Array.from( + scrollerRef.current?.querySelectorAll(`[${ROW_KEY_ATTRIBUTE}]`) ?? [] + ).findLast((element) => element.getAttribute(ROW_KEY_ATTRIBUTE) === tail) + // The default measurement returns a cached size without a resize; read the tail itself. + const height = tailElement?.getBoundingClientRect().height ?? 0 + if (tailElement && height > 0) { + virtualizer.resizeItem(Number(tailElement.dataset.index), height) + follow.observedTail = true + } + } + pinToEnd() + if (!follow.observedTail) { + return + } + const frame = window.requestAnimationFrame(() => { + if (followRef.current === follow && virtualizer.getTotalSize() === totalSize) { + followRef.current = null + } + }) + return () => window.cancelAnimationFrame(frame) + }, [followKey, isVisible, pinToEnd, tailObservations, totalSize, virtualizer]) + + const remember = useCallback(() => { + const scroller = scrollerRef.current + if (!scroller || !runScroll || memoryKey === undefined) { + return + } + setWithLRU( + runScroll, + memoryKey, + { + position: reader.current(), + followsEnd: followsEndRef.current, + detailHeights: detailRoom.snapshot() + }, + MAX_NATIVE_CHAT_DISCLOSURES + ) + }, [detailRoom, memoryKey, reader, runScroll]) const readPosition = useCallback(() => { const scroller = scrollerRef.current - if (!scroller) { + // A hidden pane reads zero geometry, which is not where the reader is. + if (!scroller || !isVisibleRef.current) { return } const fromEnd = distanceFromBottom(scroller) - followsEndRef.current = fromEnd <= NATIVE_CHAT_FOLLOW_REARM_PX + followsEndRef.current = !reader.landingPending() && fromEnd <= NATIVE_CHAT_FOLLOW_REARM_PX const top = scroller.scrollTop > 1 const bottom = fromEnd > 1 setFades((current) => current.top === top && current.bottom === bottom ? current : { top, bottom } ) - if (runScroll && memoryKey !== undefined) { - setWithLRU( - runScroll, - memoryKey, - { top: scroller.scrollTop, followsEnd: followsEndRef.current }, - MAX_NATIVE_CHAT_DISCLOSURES - ) - } - }, [memoryKey, runScroll]) + remember() + }, [reader, remember]) + + const onScroll = useCallback( + (event: React.UIEvent) => { + const scroller = event.currentTarget + const maxScroll = Math.max(0, scroller.scrollHeight - scroller.clientHeight) + const byReader = !marks.consume(event.nativeEvent, scroller.scrollTop, maxScroll) + if (byReader && isVisibleRef.current) { + // The reader moved: any follow in flight yields to them. + followRef.current = null + } + // Registered on the box before the window's own listener, so this runs before the redraw it causes. + reader.onScrolled(scroller.scrollTop, byReader) + readPosition() + }, + [marks, readPosition, reader] + ) useEffect(() => { const scroller = scrollerRef.current - const content = contentRef.current - if (!scroller || !content || typeof ResizeObserver === 'undefined') { + if (!scroller || typeof ResizeObserver === 'undefined') { return } - let roomForDetails = 0 - const observer = new ResizeObserver(() => { - const opened = openDetailHeight(content) - if (opened !== roomForDetails) { - roomForDetails = opened - // A variable, not state: a detail animating open would re-render every frame. - scroller.style.setProperty('--native-chat-opened-detail', `${opened}px`) - } - readPosition() - }) - observer.observe(content) // The box's cap moves with an opened detail, which changes what lies past its edges. + const observer = new ResizeObserver(readPosition) observer.observe(scroller) + if (scroller.firstElementChild) { + observer.observe(scroller.firstElementChild) + } return () => observer.disconnect() }, [readPosition]) + // The owned place is saved as the run leaves; nothing is read from a box that may already be hidden. + useLayoutEffect(() => remember, [remember]) + + const { retainInteractedRow } = pins + const onReaderAct = useCallback( + (event: React.SyntheticEvent) => { + // Opening a detail is the reader acting, not new work: it ends any follow in flight. + followRef.current = null + retainInteractedRow(event) + }, + [retainInteractedRow] + ) + + // Drawn rows are in index order, so the last one ends the drawn extent. + const trailing = drawsEveryRow ? 0 : totalSize - (drawn.at(-1)?.end ?? 0) + return (
-
{children}
+
+ + {trailing > 0.5 ?
: null} +
) } diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.window.test.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.window.test.tsx new file mode 100644 index 00000000000..311eafb45a5 --- /dev/null +++ b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.window.test.tsx @@ -0,0 +1,146 @@ +// @vitest-environment happy-dom + +import '@testing-library/jest-dom/vitest' +import { useMemo } from 'react' +import { cleanup, fireEvent, render, screen } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { NativeChatBlock } from '../../../../../../shared/native-chat/native-chat-types' +import { NativeChatToolRun } from './NativeChatToolRun' +import { NativeChatToolRunWindowContext } from './native-chat-tool-run-window-context' +import { + NativeChatDisclosureContext, + useNativeChatDisclosures +} from '../../messages/disclosure/native-chat-disclosure-store' + +const BOX_HEIGHT_PX = 288 +const ROW_HEIGHT_PX = 26 +/** 12 rows fill the box, 10 overscan rows on each side, and the first/last rows stay drawn. */ +const MAX_DRAWN_ROWS = 12 + 2 * 10 + 2 + +function calls(count: number): NativeChatBlock[] { + return Array.from({ length: count }, (_, index) => ({ + type: 'tool-call' as const, + name: 'Read', + callId: `call-${index}`, + input: { file_path: `src/file-${index}.ts` } + })) +} + +/** Two task-list updates (the second shows its "Full task list" control), then Read calls up to `count` rows. */ +function withTaskList(count: number): NativeChatBlock[] { + const todo = (status: string): NativeChatBlock => ({ + type: 'tool-call', + name: 'TodoWrite', + input: { todos: [{ content: 'Audit the window', status }] } + }) + return [todo('pending'), todo('in_progress'), ...calls(count - 2)] +} + +function Run({ + count, + findActive = false, + blocks = calls(count) +}: { + count: number + findActive?: boolean + blocks?: NativeChatBlock[] +}) { + const disclosures = useNativeChatDisclosures() + const windowFacts = useMemo(() => ({ isVisible: true, findActive }), [findActive]) + return ( + + + + + + ) +} + +function drawnRows(container: HTMLElement): HTMLElement[] { + return Array.from(container.querySelectorAll('[data-native-chat-tool-run-row]')) +} + +function rowButton(container: HTMLElement, index: number): HTMLElement { + const button = container.querySelector( + `[data-native-chat-tool-run-row="call:call-${index}"] button` + ) + if (!button) { + throw new Error(`row ${index} is not drawn`) + } + return button +} + +describe('NativeChatToolRun opened-run window', () => { + beforeEach(() => { + // happy-dom lays nothing out: the box and each row get the sizes the test states. + vi.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockImplementation(function ( + this: HTMLElement + ) { + if (this.hasAttribute('data-native-chat-tool-run-members')) { + return BOX_HEIGHT_PX + } + return this.hasAttribute('data-native-chat-tool-run-row') ? ROW_HEIGHT_PX : 0 + }) + vi.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(400) + }) + afterEach(() => { + cleanup() + vi.restoreAllMocks() + }) + + it('draws the same bounded window of a long run at 2,000 and 20,000 calls', () => { + for (const count of [2_000, 20_000]) { + const { container, unmount } = render() + const drawn = drawnRows(container) + expect(drawn.length).toBeGreaterThan(0) + expect(drawn.length).toBeLessThanOrEqual(MAX_DRAWN_ROWS) + // A settled run reads from its top, and its last row stays drawn for the tail. + expect(rowButton(container, 0)).toBeInTheDocument() + expect(rowButton(container, count - 1)).toBeInTheDocument() + unmount() + } + }) + + it('draws every row while Find has a query, then returns to the window without remounting', () => { + const { container, rerender } = render() + const first = rowButton(container, 0) + expect(drawnRows(container).length).toBeLessThanOrEqual(MAX_DRAWN_ROWS) + + rerender() + expect(drawnRows(container)).toHaveLength(200) + expect(rowButton(container, 0)).toBe(first) + + rerender() + expect(drawnRows(container).length).toBeLessThanOrEqual(MAX_DRAWN_ROWS) + expect(rowButton(container, 0)).toBe(first) + }) + + it('keeps a focused row and an opened full task list through windowing and Find', () => { + const { container, rerender } = render() + fireEvent.click(screen.getByRole('button', { name: /Full task list/ })) + const fullList = screen.getByRole('button', { name: /Full task list/ }) + expect(fullList).toHaveAttribute('aria-expanded', 'true') + const focused = rowButton(container, 10) + focused.focus() + + rerender() + expect(focused).toBeInTheDocument() + expect(document.activeElement).toBe(focused) + expect(screen.getByRole('button', { name: /Full task list/ })).toHaveAttribute( + 'aria-expanded', + 'true' + ) + + rerender() + rerender() + expect(screen.getByRole('button', { name: /Full task list/ })).toHaveAttribute( + 'aria-expanded', + 'true' + ) + }) +}) diff --git a/src/renderer/src/components/native-chat/tools/runs/native-chat-tool-run-rows.ts b/src/renderer/src/components/native-chat/tools/runs/native-chat-tool-run-rows.ts new file mode 100644 index 00000000000..3a965b6e23f --- /dev/null +++ b/src/renderer/src/components/native-chat/tools/runs/native-chat-tool-run-rows.ts @@ -0,0 +1,123 @@ +import type { + NativeChatBlock, + NativeChatToolCallBlock, + NativeChatToolResultBlock +} from '../../../../../../shared/native-chat/native-chat-types' +import { pairNativeChatToolResults } from '../../../../../../shared/native-chat/tools/native-chat-tool-pairing' +import { nativeChatToolLineIdentity } from '../../../../../../shared/native-chat/tools/native-chat-tool-line-identity' +import { buildEditCards } from '../../../../../../shared/native-chat/tools/native-chat-edit-cards' +import type { NativeChatEditFile } from '../../../../../../shared/native-chat/tools/native-chat-edit-model' +import { + buildNativeChatTaskListRows, + type NativeChatTaskListRow +} from '../../../../../../shared/native-chat/input/native-chat-task-list-history' +import type { NativeChatToolRunAsides } from './NativeChatToolRun' + +/** One drawn row of an opened run. `focusable`: it renders a control, so keyboard order can land on it. */ +export type NativeChatToolRunRow = + | { kind: 'aside'; key: string; focusable: boolean; element: React.JSX.Element } + | { kind: 'tasks'; key: string; focusable: boolean; taskList: NativeChatTaskListRow } + | { + kind: 'edit' + key: string + focusable: true + editKey: string + files: NativeChatEditFile[] + } + | { + kind: 'line' + key: string + focusable: true + block: NativeChatBlock + result: NativeChatToolResultBlock | undefined + lineIdentity: string + } + +/** A thought with no words draws nothing, so keyboard order cannot land on it. */ +function asideDrawsControl(element: React.JSX.Element): boolean { + const props: unknown = element.props + return !( + typeof props === 'object' && + props !== null && + 'markdown' in props && + typeof props.markdown === 'string' && + props.markdown.trim() === '' + ) +} + +/** The run's rows in drawing order, with today's keys; pairing and grouping run once over the whole run. */ +export function buildNativeChatToolRunRows({ + blocks, + headerBlocks, + asides, + previousTodoWrite, + previousUpdatePlan +}: { + blocks: NativeChatBlock[] + headerBlocks: NativeChatBlock[] + asides?: NativeChatToolRunAsides + previousTodoWrite?: NativeChatToolCallBlock + previousUpdatePlan?: NativeChatToolCallBlock +}): NativeChatToolRunRow[] { + const taskLists = buildNativeChatTaskListRows(blocks, { + todowrite: previousTodoWrite, + update_plan: previousUpdatePlan + }) + const { editCards, consumedResults } = buildEditCards(blocks) + const { resultByCall, pairedResults } = pairNativeChatToolResults(headerBlocks) + const seen = new Map() + const rows: NativeChatToolRunRow[] = [] + const pushAsides = (elements: readonly React.JSX.Element[] | undefined): void => { + for (const element of elements ?? []) { + rows.push({ + kind: 'aside', + key: String(element.key), + focusable: asideDrawsControl(element), + element + }) + } + } + for (const [blockIndex, block] of headerBlocks.entries()) { + pushAsides(asides?.before.get(block)) + const taskList = taskLists.rows.get(block) + if (taskList) { + // Only an update shows its "Full task list" trigger; a first list has no control. + rows.push({ + kind: 'tasks', + key: `tasks:${blockIndex}`, + focusable: taskList.previous !== undefined, + taskList + }) + continue + } + if (taskLists.consumedResults.has(block)) { + continue + } + const edit = editCards.get(block) + if (edit) { + rows.push({ + kind: 'edit', + key: `edit:${edit.key}`, + focusable: true, + editKey: edit.key, + files: edit.files + }) + continue + } + // A result its call now owns is drawn by that call's line, not as a row of its own. + if (consumedResults.has(block) || pairedResults.has(block)) { + continue + } + const lineIdentity = nativeChatToolLineIdentity(block, seen) + rows.push({ + kind: 'line', + key: lineIdentity, + focusable: true, + block, + result: block.type === 'tool-call' ? resultByCall.get(block) : undefined, + lineIdentity + }) + } + pushAsides(asides?.after) + return rows +} diff --git a/src/renderer/src/components/native-chat/tools/runs/native-chat-tool-run-window-context.tsx b/src/renderer/src/components/native-chat/tools/runs/native-chat-tool-run-window-context.tsx new file mode 100644 index 00000000000..d2a383c3dc6 --- /dev/null +++ b/src/renderer/src/components/native-chat/tools/runs/native-chat-tool-run-window-context.tsx @@ -0,0 +1,33 @@ +import { createContext, useMemo } from 'react' +import type { NativeChatFind } from '../../viewport/find/use-native-chat-find' + +/** What an opened run's own window needs from the chat around it; standalone runs get the defaults. */ +export type NativeChatToolRunWindowFacts = { + /** A hidden pane lays nothing out, so its rows must keep their last measured sizes. */ + isVisible: boolean + /** Find searches drawn text, so every row stays drawn while it has a query. */ + findActive: boolean +} + +export const NativeChatToolRunWindowContext = createContext({ + isVisible: true, + findActive: false +}) + +export function NativeChatToolRunWindowProvider({ + isVisible, + find, + children +}: { + isVisible: boolean + find: Pick + children: React.ReactNode +}): React.JSX.Element { + const findActive = find.isOpen && find.query !== '' + const facts = useMemo(() => ({ isVisible, findActive }), [findActive, isVisible]) + return ( + + {children} + + ) +} diff --git a/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-detail-room.ts b/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-detail-room.ts new file mode 100644 index 00000000000..7406ff08c28 --- /dev/null +++ b/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-detail-room.ts @@ -0,0 +1,80 @@ +import { useEffect, useMemo, useState } from 'react' + +/** What this row's opened details add, the same quantity the box's cap has always grown by. */ +function openedDetailHeight(row: Element): number { + let height = 0 + for (const detail of row.querySelectorAll('[data-native-chat-member-detail]')) { + height += detail.offsetHeight + } + return height +} + +/** The room the run's box gives opened details, kept per row so details scrolled out of the + * window (or the whole run evicted and back) still count; the total moves by each row's change. */ +export function useNativeChatToolRunDetailRoom({ + scrollerRef, + rowIndexByKey, + initial +}: { + scrollerRef: React.RefObject + rowIndexByKey: ReadonlyMap + initial: ReadonlyMap | undefined +}) { + const [room] = useState(() => { + const heights = new Map(initial) + let total = 0 + for (const height of heights.values()) { + total += height + } + let written = -1 + let saved: ReadonlyMap = new Map(heights) + let savedIsCurrent = true + const write = (): void => { + const scroller = scrollerRef.current + if (scroller && total !== written) { + written = total + // A variable, not state: a detail animating open would re-render every frame. + scroller.style.setProperty('--native-chat-opened-detail', `${total}px`) + } + } + const set = (key: string, height: number): void => { + if ((heights.get(key) ?? 0) === height) { + return + } + savedIsCurrent = false + total += height - (heights.get(key) ?? 0) + if (height > 0) { + heights.set(key, height) + } else { + heights.delete(key) + } + } + return { + record: (key: string, row: Element): void => { + set(key, openedDetailHeight(row)) + write() + }, + retire: (present: ReadonlyMap): void => { + for (const key of heights.keys()) { + if (!present.has(key)) { + set(key, 0) + } + } + write() + }, + // Copied only after a height changed: the reader's scrolling saves the same map again. + snapshot: (): ReadonlyMap => { + if (!savedIsCurrent) { + saved = new Map(heights) + savedIsCurrent = true + } + return saved + } + } + }) + // Rows that left the run give back their room; the first pass also applies a returning run's room. + useEffect(() => { + room.retire(rowIndexByKey) + }, [room, rowIndexByKey]) + return useMemo(() => ({ record: room.record, snapshot: room.snapshot }), [room]) +} diff --git a/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-pins.ts b/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-pins.ts new file mode 100644 index 00000000000..3f8822d85a8 --- /dev/null +++ b/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-pins.ts @@ -0,0 +1,200 @@ +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' +import { VIRTUALIZED_LIST_MIN_ROWS } from '@/components/virtualized-list' +import { nativeChatTranscriptRange } from '../../viewport/virtualization/native-chat-pinned-rows' +import type { NativeChatToolRunRow } from './native-chat-tool-run-rows' + +export const ROW_KEY_ATTRIBUTE = 'data-native-chat-tool-run-row' +/** Clicked/focused rows stay drawn, so their own scroll and local state survive the reader scrolling away. */ +const MAX_INTERACTION_PINS = 24 + +type WindowRange = { startIndex: number; endIndex: number; overscan: number; count: number } + +function rowKeyOf(target: EventTarget | null): string | null { + const row = target instanceof Element ? target.closest(`[${ROW_KEY_ATTRIBUTE}]`) : null + return row?.getAttribute(ROW_KEY_ATTRIBUTE) ?? null +} + +/** Which rows an opened run draws beyond its window: bounded obligations, never the run's length, + * except a selection, which keeps every row between its ends so copying takes all of them. */ +export function useNativeChatToolRunPins({ + rows, + rowIndexByKey, + revealRowKey, + heldRowKey, + drawsEveryRow, + isVisible, + scrollerRef +}: { + rows: readonly NativeChatToolRunRow[] + rowIndexByKey: ReadonlyMap + revealRowKey: string | undefined + /** The row a returning reader is being restored to; drawn until they move. */ + heldRowKey: string | undefined + drawsEveryRow: boolean + isVisible: boolean + scrollerRef: React.RefObject +}) { + const [interactionKeys, setInteractionKeys] = useState([]) + const [focusedKey, setFocusedKey] = useState(null) + const [selectionSpan, setSelectionSpan] = useState(null) + const rowIndexByKeyRef = useRef(rowIndexByKey) + useLayoutEffect(() => { + rowIndexByKeyRef.current = rowIndexByKey + }, [rowIndexByKey]) + // Each row's previous and next focusable rows, built once per row list so a focus move is a lookup. + const focusNeighbors = useMemo(() => { + const previous = new Int32Array(rows.length).fill(-1) + const next = new Int32Array(rows.length).fill(-1) + let last = -1 + for (let index = 0; index < rows.length; index++) { + previous[index] = last + if (rows[index]?.focusable) { + last = index + } + } + last = -1 + for (let index = rows.length - 1; index >= 0; index--) { + next[index] = last + if (rows[index]?.focusable) { + last = index + } + } + const first = rows[0]?.focusable ? 0 : (next[0] ?? -1) + const final = rows.at(-1)?.focusable ? rows.length - 1 : (previous[rows.length - 1] ?? -1) + return { previous, next, first, final } + }, [rows]) + + // The ends (Tab's way in from either side, the tail), the reveal target, interacted rows and the focused row's next stops. + const pinned = useMemo(() => { + const indexes = new Set() + const add = (index: number | undefined): void => { + if (index !== undefined && index >= 0) { + indexes.add(index) + } + } + add(rows.length - 1) + add(focusNeighbors.first) + add(focusNeighbors.final) + for (const key of [revealRowKey, heldRowKey, ...interactionKeys]) { + add(key === undefined ? undefined : rowIndexByKey.get(key)) + } + const focused = focusedKey === null ? undefined : rowIndexByKey.get(focusedKey) + if (focused !== undefined) { + add(focusNeighbors.previous[focused]) + add(focusNeighbors.next[focused]) + } + return indexes + }, [ + focusNeighbors, + focusedKey, + heldRowKey, + interactionKeys, + revealRowKey, + rowIndexByKey, + rows.length + ]) + + // A hidden run cannot be copied from; a visible one tracks selection even while drawing every row, + // so the span is already known when it goes back to its window. + const tracksSelection = isVisible + // One array per row count: the virtualizer keeps its items while the same array comes back, so + // scrolling a run that draws every row rebuilds nothing. + const everyIndex = useMemo( + () => Array.from({ length: rows.length }, (_, index) => index), + [rows.length] + ) + // A selection's rows plus the pins, reused while the window stays inside the selection. + const span = tracksSelection && !drawsEveryRow ? selectionSpan : null + const spanned = useMemo(() => { + if (!span) { + return null + } + const indexes = new Set(pinned) + for (let index = span[0]; index <= Math.min(span[1], rows.length - 1); index++) { + indexes.add(index) + } + return Array.from(indexes).sort((a, b) => a - b) + }, [pinned, rows.length, span]) + const rangeExtractor = useCallback( + (range: WindowRange) => { + if (drawsEveryRow) { + return everyIndex + } + const first = Math.max(0, range.startIndex - range.overscan) + const last = Math.min(range.count - 1, range.endIndex + range.overscan) + if (spanned && span && span[0] <= first && last <= span[1]) { + return spanned + } + const indexes = nativeChatTranscriptRange(range, pinned) + if (!spanned) { + return indexes + } + return Array.from(new Set([...indexes, ...spanned])).sort((a, b) => a - b) + }, + [drawsEveryRow, everyIndex, pinned, span, spanned] + ) + + // An end outside the run (or on a spacer) reaches the run's matching edge. + useEffect(() => { + if (!tracksSelection) { + return + } + const onSelectionChange = (): void => { + const scroller = scrollerRef.current + const selection = document.getSelection() + const range = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null + if (!scroller || !range || !range.intersectsNode(scroller)) { + setSelectionSpan((current) => (current === null ? current : null)) + return + } + const indexAt = (node: Node, fallback: number): number => { + if (!scroller.contains(node)) { + return fallback + } + const key = rowKeyOf(node instanceof Element ? node : node.parentElement) + return (key === null ? undefined : rowIndexByKeyRef.current.get(key)) ?? fallback + } + const start = indexAt(range.startContainer, 0) + const end = indexAt(range.endContainer, rowIndexByKeyRef.current.size - 1) + setSelectionSpan((current) => + current && current[0] === start && current[1] === end ? current : [start, end] + ) + } + // Re-derive on (re)registration: the selection may have changed while this run was hidden. + onSelectionChange() + document.addEventListener('selectionchange', onSelectionChange) + return () => document.removeEventListener('selectionchange', onSelectionChange) + }, [scrollerRef, tracksSelection]) + + const retainInteractedRow = useCallback((event: React.SyntheticEvent) => { + const key = rowKeyOf(event.target) + if (key === null) { + return + } + setInteractionKeys((current) => + current.at(-1) === key + ? current + : [...current.filter((candidate) => candidate !== key), key].slice(-MAX_INTERACTION_PINS) + ) + }, []) + const onFocusCapture = useCallback( + (event: React.FocusEvent) => { + setFocusedKey(rowKeyOf(event.target)) + retainInteractedRow(event) + }, + [retainInteractedRow] + ) + const onBlurCapture = useCallback( + (event: React.FocusEvent) => { + const next = event.relatedTarget + if (!(next instanceof Node) || !scrollerRef.current?.contains(next)) { + setFocusedKey(null) + } + }, + [scrollerRef] + ) + + // A selection wider than a window keeps that many rows drawn while the reader scrolls. + const spansSelection = span !== null && span[1] - span[0] >= VIRTUALIZED_LIST_MIN_ROWS + return { rangeExtractor, spansSelection, retainInteractedRow, onFocusCapture, onBlurCapture } +} diff --git a/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-reader-position.test.tsx b/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-reader-position.test.tsx new file mode 100644 index 00000000000..6f3d95e753e --- /dev/null +++ b/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-reader-position.test.tsx @@ -0,0 +1,228 @@ +// @vitest-environment happy-dom + +import { afterEach, describe, expect, it, vi } from 'vitest' +import { cleanup, renderHook } from '@testing-library/react' +import { createProgrammaticScrollMarks } from '@/hooks/programmatic-scroll-marks' +import type { NativeChatToolRunRow } from './native-chat-tool-run-rows' +import { ROW_KEY_ATTRIBUTE } from './use-native-chat-tool-run-pins' +import { + claimNativeChatToolRunReveal, + useNativeChatToolRunReaderPosition +} from './use-native-chat-tool-run-reader-position' +import type { NativeChatToolRunVirtualizer } from './use-native-chat-tool-run-window' + +// The window's size model, deliberately stale: the edit row is still its cached 78 px while the page has it far +// taller. Rows after it follow the cached height. +const MODEL_ROWS = [ + 'call:before', + 'edit:patch', + 'call:after-1', + 'call:after-2', + 'call:after-3', + 'call:after-4', + 'call:after-5', + 'call:after-6', + 'call:after-7', + 'call:after-8', + 'call:after-9', + 'call:after-10' +] +const MODEL_STARTS = [ + 0, 1_000, 1_078, 1_118, 1_158, 1_198, 1_238, 1_278, 1_318, 1_358, 1_398, 1_438 +] + +// Rows that draw something; the owner reads only their keys and whether they draw. +const rows: NativeChatToolRunRow[] = MODEL_ROWS.map((key) => ({ + kind: 'aside', + key, + focusable: true, + element: +})) +const rowIndexByKey = new Map(MODEL_ROWS.map((key, index) => [key, index])) +const measurementsCache = MODEL_STARTS.map((start, index) => { + const size = (MODEL_STARTS[index + 1] ?? start + 40) - start + return { index, start, size, end: start + size, key: MODEL_ROWS[index]!, lane: 0 } +}) +// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the owner reads only measurementsCache, scrollOffset and getVirtualItemForOffset, all supplied here; a full TanStack instance is not needed to give it a fixed model. +const staleModel = { + measurementsCache, + scrollOffset: 0, + getVirtualItemForOffset: (offset: number) => + measurementsCache.findLast((item) => item.start <= offset) +} as unknown as NativeChatToolRunVirtualizer + +function setClientHeight(element: Element, height: number): void { + Object.defineProperty(element, 'clientHeight', { configurable: true, value: height }) +} + +function setRect(element: Element, top: number, height: number): void { + Object.defineProperty(element, 'getBoundingClientRect', { + configurable: true, + value: () => DOMRect.fromRect({ x: 0, y: top, width: 300, height }) + }) +} + +/** A run box whose drawn children sit at literal page positions relative to a 288 px box at the page top. */ +function runBox( + children: { key?: string; top: number; height: number }[], + boxHeight = 288 +): { + scroller: HTMLDivElement + rowsByKey: Map +} { + const scroller = document.createElement('div') + const content = document.createElement('div') + scroller.append(content) + document.body.append(scroller) + setRect(scroller, 0, boxHeight) + setClientHeight(scroller, boxHeight) + const rowsByKey = new Map() + for (const child of children) { + const element = document.createElement('div') + if (child.key) { + element.setAttribute(ROW_KEY_ATTRIBUTE, child.key) + rowsByKey.set(child.key, element) + } + setRect(element, child.top, child.height) + content.append(element) + } + return { scroller, rowsByKey } +} + +function moveChildren( + rowsByKey: Map, + positions: Record +): void { + for (const [key, [top, height]] of Object.entries(positions)) { + setRect(rowsByKey.get(key)!, top, height) + } +} + +function owner(scroller: HTMLDivElement) { + return renderHook( + ({ isVisible }: { isVisible: boolean }) => + useNativeChatToolRunReaderPosition({ + scrollerRef: { current: scroller }, + virtualizer: staleModel, + rows, + rowIndexByKey, + isVisible, + followsEndRef: { current: false }, + marks: createProgrammaticScrollMarks(), + totalSize: 1_478, + initial: undefined, + setHeldRowKey: vi.fn(), + onRevealed: vi.fn() + }), + { initialProps: { isVisible: true } } + ) +} + +/** A card inside a drawn row scrolls itself to the box top and claims the place. */ +function revealCardIn(scroller: HTMLDivElement, row: HTMLDivElement): void { + const card = document.createElement('div') + row.append(card) + claimNativeChatToolRunReveal(scroller, card) +} + +describe('tool-run reader position with a stale size model', () => { + afterEach(() => { + cleanup() + document.body.replaceChildren() + }) + + it("keeps a revealed file's place through its own scroll report and a hide", () => { + // Box at 1,400: the edit really starts at 1,000 and is 600 tall; the next row starts at 1,600. + const { scroller, rowsByKey } = runBox([ + { top: -1_400, height: 1_000 }, + { key: 'edit:patch', top: -400, height: 600 }, + { key: 'call:after-1', top: 200, height: 40 } + ]) + const { result, rerender } = owner(scroller) + revealCardIn(scroller, rowsByKey.get('edit:patch')!) + expect(result.current.current()).toEqual({ key: 'edit:patch', offset: 400 }) + + // The reveal's own native scroll report, before the edit's new height reaches the model. + result.current.onScrolled(1_400, true) + expect(result.current.current()).toEqual({ key: 'edit:patch', offset: 400 }) + + rerender({ isVisible: false }) + expect(result.current.current()).toEqual({ key: 'edit:patch', offset: 400 }) + }) + + it('follows the reader inside the tall row and across its edge before the model catches up', () => { + const { scroller, rowsByKey } = runBox([ + { top: -1_400, height: 1_000 }, + { key: 'edit:patch', top: -400, height: 600 }, + { key: 'call:after-1', top: 200, height: 40 } + ]) + const { result } = owner(scroller) + revealCardIn(scroller, rowsByKey.get('edit:patch')!) + + moveChildren(rowsByKey, { 'edit:patch': [-420, 600], 'call:after-1': [180, 40] }) + result.current.onScrolled(1_420, true) + expect(result.current.current()).toEqual({ key: 'edit:patch', offset: 420 }) + + moveChildren(rowsByKey, { 'edit:patch': [-610, 600], 'call:after-1': [-10, 40] }) + result.current.onScrolled(1_610, true) + expect(result.current.current()).toEqual({ key: 'call:after-1', offset: 10 }) + }) + + it('takes the next drawn row across a blank row and a card margin', () => { + // Edit bottom at 1,600, a blank thought at 1,600, the next call at 1,604 (the card's margin); box at 1,602. + const { scroller } = runBox([ + { top: -1_602, height: 1_000 }, + { key: 'edit:patch', top: -602, height: 600 }, + { key: 'call:before', top: -2, height: 0 }, + { key: 'call:after-1', top: 2, height: 40 } + ]) + const { result } = owner(scroller) + result.current.onScrolled(1_602, true) + expect(result.current.current()).toEqual({ key: 'call:after-1', offset: 0 }) + }) + + it('keeps the place when the box has no geometry, even where the model has an answer', () => { + const { scroller, rowsByKey } = runBox([ + { top: -1_400, height: 1_000 }, + { key: 'edit:patch', top: -400, height: 600 } + ]) + const { result } = owner(scroller) + revealCardIn(scroller, rowsByKey.get('edit:patch')!) + setRect(scroller, 0, 0) + setClientHeight(scroller, 0) + result.current.onScrolled(0, true) + expect(result.current.current()).toEqual({ key: 'edit:patch', offset: 400 }) + }) + + it('records the actual place after a clamped report and a compensation write', () => { + const { scroller, rowsByKey } = runBox([ + { top: -1_900, height: 1_000 }, + { key: 'edit:patch', top: -900, height: 1_200 } + ]) + const { result } = owner(scroller) + revealCardIn(scroller, rowsByKey.get('edit:patch')!) + expect(result.current.current()).toEqual({ key: 'edit:patch', offset: 900 }) + + // The box could only reach 1,880. + moveChildren(rowsByKey, { 'edit:patch': [-880, 1_200] }) + result.current.onScrolled(1_880, true) + expect(result.current.current()).toEqual({ key: 'edit:patch', offset: 880 }) + + // The window's own compensation write (not the reader) moved it to 1,912. + moveChildren(rowsByKey, { 'edit:patch': [-912, 1_200] }) + result.current.onScrolled(1_912, false) + expect(result.current.current()).toEqual({ key: 'edit:patch', offset: 912 }) + expect(scroller.scrollTop).toBe(0) + }) + + it('uses the model only where the box top lies in an undrawn gap', () => { + // A wrapper for a row no longer in the run, then a spacer: the top is undrawn, so the model answers. + const { scroller } = runBox([ + { key: 'call:gone', top: -10, height: 40 }, + { top: 30, height: 5_000 } + ]) + const { result } = owner(scroller) + result.current.onScrolled(1_130, true) + expect(result.current.current()).toEqual({ key: 'call:after-2', offset: 12 }) + }) +}) diff --git a/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-reader-position.ts b/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-reader-position.ts new file mode 100644 index 00000000000..8aef76a6a40 --- /dev/null +++ b/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-reader-position.ts @@ -0,0 +1,341 @@ +import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react' +import type { ProgrammaticScrollMarks } from '@/hooks/programmatic-scroll-marks' +import type { NativeChatRunPosition } from '../../messages/disclosure/native-chat-disclosure-store' +import type { NativeChatToolRunRow } from './native-chat-tool-run-rows' +import { ROW_KEY_ATTRIBUTE } from './use-native-chat-tool-run-pins' +import type { NativeChatToolRunVirtualizer } from './use-native-chat-tool-run-window' + +const REVEAL_EVENT = 'native-chat-tool-run-reveal' +// A card can reveal in the same commit that mounts its run, before the run listens for it. The place is read from +// the card's own row as it aligns, since the window's model may not yet know that row's new height. +const unclaimedReveals = new WeakMap() +// Confirmation frames a landing may spend per real change before it waits for the next one. +const MAX_LANDING_FRAMES = 12 + +/** A card has just scrolled itself to the top of its run: that is now the reader's place. */ +export function claimNativeChatToolRunReveal(scroller: HTMLElement, member: HTMLElement): void { + const row = member.closest(`[${ROW_KEY_ATTRIBUTE}]`) + const key = row?.getAttribute(ROW_KEY_ATTRIBUTE) + const offset = row + ? scroller.getBoundingClientRect().top - row.getBoundingClientRect().top + : undefined + unclaimedReveals.set( + scroller, + key && offset !== undefined ? { key, offset: Math.max(0, offset) } : null + ) + scroller.dispatchEvent(new Event(REVEAL_EVENT)) +} + +/** A thought with no words draws nothing, so it can never be anyone's place. */ +const drawsSomething = (row: NativeChatToolRunRow): boolean => row.kind !== 'aside' || row.focusable + +// `seen` is the size and row list the frame budget was granted for; a real change grants a new budget. +// Blank rows a capture may step over after the box top before it trusts the model instead. +const MAX_BLANK_ROWS_STEPPED = 8 + +/** Where the drawn rows put the box top: a row's own place, an undrawn gap, or no usable geometry. */ +type DrawnPlace = NativeChatRunPosition | 'gap' | 'unusable' + +/** The reader's place read from the rows actually drawn at the box top, which win over a model that may still + * hold an old height. A binary search over the content's in-flow children keeps this bounded in every mode. */ +function placeFromDrawnRows( + scroller: HTMLElement, + rowIndexByKey: ReadonlyMap +): DrawnPlace { + const box = scroller.getBoundingClientRect() + const content = scroller.firstElementChild + // A hidden or not-yet-laid-out box reports no client height: nothing there is the reader's place. + if (scroller.clientHeight <= 0 || !content?.isConnected) { + return 'unusable' + } + const children = content.children + let found = -1 + for (let low = 0, high = children.length - 1; low <= high;) { + const middle = (low + high) >> 1 + if (children[middle]!.getBoundingClientRect().top - box.top <= 0.5) { + found = middle + low = middle + 1 + } else { + high = middle - 1 + } + } + const read = (child: Element) => { + const key = child.getAttribute(ROW_KEY_ATTRIBUTE) + const rect = child.getBoundingClientRect() + const kind = + key === null ? 'spacer' : rowIndexByKey.has(key) && rect.height > 0 ? 'row' : 'blank' + return { kind, key, rect } + } + const atTop = found >= 0 ? read(children[found]!) : undefined + if (atTop && atTop.kind !== 'blank' && atTop.rect.bottom > box.top) { + return atTop.kind === 'row' && atTop.key !== null + ? { key: atTop.key, offset: box.top - atTop.rect.top } + : 'gap' + } + // A blank row or a margin between children: the place is the next child that draws, if it is a row. + for (let index = found + 1, blanks = 0; index < children.length; index++) { + const next = read(children[index]!) + if (next.kind === 'blank') { + if (++blanks > MAX_BLANK_ROWS_STEPPED) { + return 'gap' + } + continue + } + return next.kind === 'row' && next.key !== null + ? { key: next.key, offset: Math.max(0, box.top - next.rect.top) } + : 'gap' + } + return 'gap' +} + +type Landing = { + target: NativeChatRunPosition + frames: number + seen?: { totalSize: number; rowIndexByKey: ReadonlyMap } +} + +/** The run's one record of where its reader is, and the one landing that may move the box back there. */ +export function useNativeChatToolRunReaderPosition({ + scrollerRef, + virtualizer, + rows, + rowIndexByKey, + isVisible, + followsEndRef, + marks, + totalSize, + initial, + setHeldRowKey, + onRevealed +}: { + scrollerRef: React.RefObject + virtualizer: NativeChatToolRunVirtualizer + rows: readonly NativeChatToolRunRow[] + rowIndexByKey: ReadonlyMap + isVisible: boolean + followsEndRef: React.RefObject + marks: ProgrammaticScrollMarks + totalSize: number + /** A returning reader's place, landed once the run is visible. */ + initial: NativeChatRunPosition | undefined + /** Keeps the landing row drawn while a landing is pending. */ + setHeldRowKey: (key: string | undefined) => void + /** A reveal took the box: no follow may move it. */ + onRevealed: () => void +}) { + const positionRef = useRef(initial ?? null) + const landingRef = useRef(initial ? { target: initial, frames: 0 } : null) + const [landingWake, setLandingWake] = useState(0) + // Each row's first drawing row at or after it, so finding a place never walks the run. + const nextDrawing = useMemo(() => { + const next = new Int32Array(rows.length).fill(-1) + for (let index = rows.length - 1; index >= 0; index--) { + next[index] = drawsSomething(rows[index]!) ? index : (next[index + 1] ?? -1) + } + return next + }, [rows]) + const isVisibleRef = useRef(isVisible) + const latest = useRef({ virtualizer, rows, rowIndexByKey, nextDrawing, onRevealed }) + useLayoutEffect(() => { + isVisibleRef.current = isVisible + latest.current = { virtualizer, rows, rowIndexByKey, nextDrawing, onRevealed } + }) + + const retireLanding = useCallback(() => { + if (landingRef.current) { + landingRef.current = null + setHeldRowKey(undefined) + } + }, [setHeldRowKey]) + /** The reader's row: from the drawn rows when they cover the box top, else from the window's full model, + * which also covers rows not drawn. No usable geometry keeps the last place. */ + const capture = useCallback( + (scrollTop: number) => { + const { + virtualizer: model, + rows: current, + rowIndexByKey: keys, + nextDrawing: next + } = latest.current + const scroller = scrollerRef.current + const drawn = scroller ? placeFromDrawnRows(scroller, keys) : 'unusable' + if (drawn === 'unusable') { + return + } + if (drawn !== 'gap') { + positionRef.current = drawn + return + } + const found = model.getVirtualItemForOffset(scrollTop)?.index + const index = found === undefined ? -1 : (next[found] ?? -1) + const row = current[index] + const measured = model.measurementsCache[index] + // No drawing row there (or nothing measured yet) keeps the last place rather than inventing one. + if (row && measured) { + positionRef.current = { key: row.key, offset: Math.max(0, scrollTop - measured.start) } + } + }, + [scrollerRef] + ) + + /** Every scroll of the visible box: the reader's own retires any landing first, in every drawing mode. */ + const onScrolled = useCallback( + (scrollTop: number, byReader: boolean) => { + if (!isVisibleRef.current) { + return + } + if (byReader) { + retireLanding() + } else if (landingRef.current) { + return + } + capture(scrollTop) + }, + [capture, retireLanding] + ) + + // A reveal in this commit (from the live listener or a claim left before this run listened) owns the box. + const revealedRef = useRef(false) + // The revealed row's own place, read from the DOM as the card aligned; absent, the model decides below. + const revealedPlaceRef = useRef(null) + const takeReveal = useCallback(() => { + const scroller = scrollerRef.current + if (!scroller || !unclaimedReveals.has(scroller)) { + return + } + const place = unclaimedReveals.get(scroller) ?? null + unclaimedReveals.delete(scroller) + revealedRef.current = true + revealedPlaceRef.current = place + retireLanding() + latest.current.onRevealed() + if (place) { + positionRef.current = place + } else if (isVisibleRef.current) { + capture(scroller.scrollTop) + } + }, [capture, retireLanding, scrollerRef]) + useLayoutEffect(takeReveal) + + // A hidden box has no geometry: hiding keeps the place (a pending landing's target), showing lands on it + // again unless a reveal in the same commit took the box. + const wasVisibleRef = useRef(isVisible) + useLayoutEffect(() => { + if (wasVisibleRef.current === isVisible) { + return + } + wasVisibleRef.current = isVisible + const target = landingRef.current?.target ?? positionRef.current + if (!isVisible) { + positionRef.current = target + retireLanding() + } else if (target && !followsEndRef.current && !revealedRef.current) { + positionRef.current = target + landingRef.current = { target, frames: 0 } + setHeldRowKey(target.key) + setLandingWake((count) => count + 1) + } + }, [followsEndRef, isVisible, retireLanding, setHeldRowKey]) + + // The reveal's own place, read once visibility is current; the next commit may restore again. + useLayoutEffect(() => { + const scroller = scrollerRef.current + if (revealedRef.current && !revealedPlaceRef.current && scroller && isVisibleRef.current) { + capture(scroller.scrollTop) + } + revealedRef.current = false + revealedPlaceRef.current = null + }) + useLayoutEffect(() => { + const scroller = scrollerRef.current + scroller?.addEventListener(REVEAL_EVENT, takeReveal) + return () => scroller?.removeEventListener(REVEAL_EVENT, takeReveal) + }, [scrollerRef, takeReveal]) + + // Moves the box toward the landing row. A correction owns one confirmation frame; past the frame budget the + // landing keeps its row drawn and waits for a size, row-list, measurement or visibility change. + useLayoutEffect(() => { + const landing = landingRef.current + const scroller = scrollerRef.current + if (!landing || !scroller || !isVisible) { + return + } + if (landing.seen?.totalSize !== totalSize || landing.seen.rowIndexByKey !== rowIndexByKey) { + landing.seen = { totalSize, rowIndexByKey } + landing.frames = 0 + } + const found = rowIndexByKey.get(landing.target.key) + const index = found === undefined ? -1 : (nextDrawing[found] ?? -1) + const row = rows[index] + if (!row) { + retireLanding() + return + } + if (row.key !== landing.target.key) { + landing.target = { key: row.key, offset: 0 } + setHeldRowKey(row.key) + } + const element = scroller.querySelector(`[${ROW_KEY_ATTRIBUTE}="${CSS.escape(row.key)}"]`) + const rect = element?.getBoundingClientRect() + const laidOut = rect !== undefined && rect.height > 0 + const delta = laidOut + ? rect.top - scroller.getBoundingClientRect().top + landing.target.offset + : (virtualizer.measurementsCache[index]?.start ?? scroller.scrollTop) + + landing.target.offset - + scroller.scrollTop + const atEnd = scroller.scrollTop >= scroller.scrollHeight - scroller.clientHeight - 1 + if (laidOut && (Math.abs(delta) <= 1 || (delta > 0 && atEnd))) { + positionRef.current = landing.target + retireLanding() + return + } + if (Math.abs(delta) > 1) { + marks.mark(scroller.scrollTop + delta) + scroller.scrollTop += delta + } else if (laidOut) { + return + } + if (landing.frames >= MAX_LANDING_FRAMES) { + return + } + landing.frames++ + const frame = window.requestAnimationFrame(() => { + if (landingRef.current === landing && isVisibleRef.current) { + setLandingWake((count) => count + 1) + } + }) + return () => window.cancelAnimationFrame(frame) + }, [ + isVisible, + landingWake, + marks, + nextDrawing, + retireLanding, + rowIndexByKey, + rows, + scrollerRef, + setHeldRowKey, + totalSize, + virtualizer + ]) + + /** The held row reported a real size: a waiting landing tries again, with a fresh confirmation budget. */ + const onRowMeasured = useCallback((key: string) => { + const landing = landingRef.current + if (landing?.target.key === key) { + landing.frames = 0 + setLandingWake((count) => count + 1) + } + }, []) + /** A landing is still moving the box: its own writes are not the reader arriving at the end. */ + const landingPending = useCallback((): boolean => landingRef.current !== null, []) + /** What the run keeps when it leaves: a pending landing's target is still the reader's place. */ + const current = useCallback( + (): NativeChatRunPosition | null => landingRef.current?.target ?? positionRef.current, + [] + ) + return useMemo( + () => ({ onScrolled, onRowMeasured, current, landingPending }), + [current, landingPending, onRowMeasured, onScrolled] + ) +} diff --git a/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-window.ts b/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-window.ts new file mode 100644 index 00000000000..f54240e1488 --- /dev/null +++ b/src/renderer/src/components/native-chat/tools/runs/use-native-chat-tool-run-window.ts @@ -0,0 +1,106 @@ +import { useCallback, useLayoutEffect, useMemo, useRef } from 'react' +import { + elementScroll, + measureElement, + useVirtualizer, + type Virtualizer +} from '@tanstack/react-virtual' +import { VIRTUALIZED_LIST_OVERSCAN } from '@/components/virtualized-list' +import type { ProgrammaticScrollMarks } from '@/hooks/programmatic-scroll-marks' +import { useLatestHandler } from '@/lib/use-latest-handler' +import type { NativeChatToolRunRow } from './native-chat-tool-run-rows' + +/** A tool line's `min-h-6.5`; measured rows replace it. */ +const ROW_ESTIMATE_PX = 26 +const estimateRowSize = (): number => ROW_ESTIMATE_PX + +export type NativeChatToolRunVirtualizer = Virtualizer +export type NativeChatToolRunDrawnRow = { index: number; start: number; end: number } + +/** The run's virtualizer, kept in its own hook: it reads a mutable library instance every render, + * so every live read (window, sizes, positions) happens here and leaves as plain values. */ +export function useNativeChatToolRunWindow({ + rows, + scrollerRef, + isVisible, + drawsEveryRow, + rangeExtractor, + marks, + onMeasure +}: { + rows: readonly NativeChatToolRunRow[] + scrollerRef: React.RefObject + isVisible: boolean + drawsEveryRow: boolean + rangeExtractor: (range: { + startIndex: number + endIndex: number + overscan: number + count: number + }) => number[] + marks: ProgrammaticScrollMarks + /** Every real measurement, attach and resize alike, so the cap and follow see them all. */ + onMeasure: (element: HTMLDivElement) => void +}) { + const onMeasured = useLatestHandler(onMeasure) + // Stable per row list: a new key function makes the virtualizer rebuild every row's measurement. + const getItemKey = useCallback((index: number) => rows[index]?.key ?? index, [rows]) + const virtualizer = useVirtualizer({ + count: rows.length, + getScrollElement: () => scrollerRef.current, + estimateSize: estimateRowSize, + getItemKey, + overscan: VIRTUALIZED_LIST_OVERSCAN, + rangeExtractor, + // A hidden pane has no boxes to measure; keep the last visible sizes. + useCachedMeasurements: !isVisible, + measureElement: (element, entry, instance) => { + const size = measureElement(element, entry, instance) + onMeasured(element) + return size + }, + // Mark every write the virtualizer makes so the reader's own scrolling stays distinguishable. + scrollToFn: (offset, options, instance) => { + const element = instance.scrollElement + const previous = element?.scrollTop + elementScroll(offset, options, instance) + if (element && element.scrollTop !== previous) { + marks.mark(element.scrollTop) + } + } + }) + const totalSize = virtualizer.getTotalSize() + const virtualItems = virtualizer.getVirtualItems() + const measurements = virtualizer.measurementsCache + // Nothing measurable (a hidden pane): keep what was drawn rather than unmounting the window. + const lastDrawnRef = useRef([]) + // Every row drawn: the same array while only the scroll position moves, so the rows are not rebuilt. + const everyRow = useMemo( + () => + drawsEveryRow + ? Array.from({ length: rows.length }, (_, index) => ({ index, start: 0, end: 0 })) + : null, + [drawsEveryRow, rows.length] + ) + // The window's rows keep their identity while the virtualizer's items and sizes do, so an + // unchanged window (a large selection span, say) re-renders no rows. + const windowRows = useMemo( + () => + everyRow || virtualItems.length === 0 + ? null + : virtualItems.map((item) => ({ + index: item.index, + start: measurements[item.index]?.start ?? 0, + end: measurements[item.index]?.end ?? 0 + })), + [everyRow, measurements, virtualItems] + ) + useLayoutEffect(() => { + if (windowRows) { + lastDrawnRef.current = windowRows + } + }) + const drawn: readonly NativeChatToolRunDrawnRow[] = + everyRow ?? windowRows ?? lastDrawnRef.current.filter((row) => row.index < rows.length) + return { virtualizer, measureRow: virtualizer.measureElement, totalSize, drawn } +} diff --git a/src/renderer/src/components/native-chat/tools/tasks/NativeChatTaskList.tsx b/src/renderer/src/components/native-chat/tools/tasks/NativeChatTaskList.tsx index 7f2b127929b..9996106009a 100644 --- a/src/renderer/src/components/native-chat/tools/tasks/NativeChatTaskList.tsx +++ b/src/renderer/src/components/native-chat/tools/tasks/NativeChatTaskList.tsx @@ -3,6 +3,7 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/component import { cn } from '@/lib/class-names' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' +import { useNativeChatDisclosure } from '../../messages/disclosure/native-chat-disclosure-store' import { diffNativeChatTaskLists, nativeChatCompletedTaskCount, @@ -92,13 +93,17 @@ function taskProgressLabel(completed: number, total: number, t: Translate = tran export function NativeChatTaskList({ list, previous, - presentation = 'inline' + presentation = 'inline', + disclosureKey }: { list: TaskList previous?: TaskList presentation?: 'inline' | 'composer' + /** Identity the full list's open state is remembered under while its row is unmounted. */ + disclosureKey?: string }): React.JSX.Element { const t = useTranslate() + const fullList = useNativeChatDisclosure(disclosureKey, false) const completed = nativeChatCompletedTaskCount(list) if (presentation === 'composer') { return ( @@ -159,7 +164,7 @@ export function NativeChatTaskList({ {t('components.native-chat.taskList.unchanged', 'Tasks unchanged')}

)} - + +
+ +
+
{title}
+

+ {t( + 'auto.hooks.useMacosTccPromptNotice.subtitle', + 'Full Disk Access stops these file prompts.' + )} +

+
+
+ {/* Why: on a roomy card, indent by illustration width + gap so the actions start + under the title. Below ~22rem of card width (20rem content box) that indent would + push the button past the border, so the row drops it and stacks full width. */} +
+ {/* Why: Button is nowrap/shrink-0 by default; let the label wrap rather than overflow. */} + + +
+
+ ) +} diff --git a/src/renderer/src/hooks/useMacosTccPromptNotice.ts b/src/renderer/src/hooks/useMacosTccPromptNotice.tsx similarity index 57% rename from src/renderer/src/hooks/useMacosTccPromptNotice.ts rename to src/renderer/src/hooks/useMacosTccPromptNotice.tsx index 29f03a0da43..5c9cdbd241e 100644 --- a/src/renderer/src/hooks/useMacosTccPromptNotice.ts +++ b/src/renderer/src/hooks/useMacosTccPromptNotice.tsx @@ -7,8 +7,8 @@ import { usePluginLanguagePackStore, usePluginLanguagePacks } from '@/store/plugins/localization/plugin-language-packs' -import { translate } from '@/i18n/i18n' import { resolveUiLocale } from '@/i18n/supported-languages' +import { FullDiskAccessNudgeToast } from '@/components/macos-permissions/FullDiskAccessNudgeToast' import { FULL_DISK_ACCESS_SETTINGS_TARGET_ID } from '@/components/settings/navigation/settings-navigation-types' import { dismissMacosTccPromptNotice, @@ -41,39 +41,38 @@ export function useMacosTccPromptNotice(): void { if (!localeReady) { return } - return subscribeToMacosTccPromptNotice(window.api?.macosTccPrompts, (_, acknowledge) => { - toast.warning( - translate( - 'auto.hooks.useMacosTccPromptNotice.title', - 'Seeing “Orca would like to access…” prompts?' - ), - { - description: translate( - 'auto.hooks.useMacosTccPromptNotice.description', - 'Permission messages from macOS may appear when an agent or terminal tool running in Orca attempts to access protected files. Grant Full Disk Access in Settings to reduce these prompts.' + return subscribeToMacosTccPromptNotice( + window.api?.macosTccPrompts, + ({ promptCount }, acknowledge) => { + toast.custom( + (toastId) => ( + toast.dismiss(toastId)} + onEnable={() => { + acknowledge() + openSettingsPage() + openSettingsTarget({ + pane: 'developer-permissions', + repoId: null, + sectionId: FULL_DISK_ACCESS_SETTINGS_TARGET_ID + }) + toast.dismiss(toastId) + }} + onDontShowAgain={() => { + void dismissMacosTccPromptNotice(window.api?.macosTccPrompts) + toast.dismiss(toastId) + }} + /> ), - duration: Infinity, - onDismiss: acknowledge, - action: { - label: translate('auto.hooks.useMacosTccPromptNotice.openSettings', 'Open Settings'), - onClick: () => { - acknowledge() - openSettingsPage() - openSettingsTarget({ - pane: 'developer-permissions', - repoId: null, - sectionId: FULL_DISK_ACCESS_SETTINGS_TARGET_ID - }) - } - }, - cancel: { - label: translate('auto.hooks.useMacosTccPromptNotice.dismiss', "Don't show again"), - onClick: () => { - void dismissMacosTccPromptNotice(window.api?.macosTccPrompts) - } + { + duration: Infinity, + closeButton: false, + unstyled: true, + onDismiss: acknowledge } - } - ) - }) + ) + } + ) }, [localeReady, openSettingsPage, openSettingsTarget]) } diff --git a/src/renderer/src/i18n/locales/de.json b/src/renderer/src/i18n/locales/de.json index 062bab6b7c7..396f1cb7af8 100644 --- a/src/renderer/src/i18n/locales/de.json +++ b/src/renderer/src/i18n/locales/de.json @@ -2113,9 +2113,6 @@ "unreadableSkillSource": "Ein Skill-Ordner hat nicht geantwortet, daher ist dieser Status möglicherweise unvollständig." }, "useMacosTccPromptNotice": { - "title": "Werden Meldungen wie „Orca would like to access…“ angezeigt?", - "description": "Berechtigungsabfragen von macOS können erscheinen, wenn ein in Orca ausgeführter Agent oder ein Terminalwerkzeug auf geschützte Dateien zugreift. In den Einstellungen „Full Disk Access“ gewähren, um diese Abfragen zu reduzieren.", - "openSettings": "Einstellungen öffnen", "dismiss": "Nicht mehr anzeigen" }, "useMacTccAttributionSeveredNotice": { diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index a5efbd1c672..2d0c1501784 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -2652,9 +2652,11 @@ "unreadableSkillSource": "A skill folder did not respond, so this status may be incomplete." }, "useMacosTccPromptNotice": { - "title": "Seeing “Orca would like to access…” prompts?", - "description": "Permission messages from macOS may appear when an agent or terminal tool running in Orca attempts to access protected files. Grant Full Disk Access in Settings to reduce these prompts.", - "openSettings": "Open Settings", + "titleOne": "macOS just interrupted an agent", + "titleMany": "macOS interrupted your agents {{count}}×", + "subtitle": "Full Disk Access stops these file prompts.", + "enable": "Turn on Full Disk Access", + "close": "Close", "dismiss": "Don't show again" }, "useMacTccAttributionSeveredNotice": { diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json index c85e0fedcdd..123288c7a1c 100644 --- a/src/renderer/src/i18n/locales/es.json +++ b/src/renderer/src/i18n/locales/es.json @@ -1624,9 +1624,6 @@ "pasteTooLarge": "El contenido pegado es demasiado grande." }, "useMacosTccPromptNotice": { - "title": "¿Ves avisos de “Orca quiere acceder…”?", - "description": "Los mensajes de permisos de macOS pueden aparecer cuando un agente o una herramienta de terminal que se ejecuta en Orca intenta acceder a archivos protegidos. Concede acceso total al disco en Ajustes para reducir estos avisos.", - "openSettings": "Abrir ajustes", "dismiss": "No volver a mostrar" }, "useInstalledAgentSkills": { diff --git a/src/renderer/src/i18n/locales/fr.json b/src/renderer/src/i18n/locales/fr.json index 91e2377e60a..2e6d846e090 100644 --- a/src/renderer/src/i18n/locales/fr.json +++ b/src/renderer/src/i18n/locales/fr.json @@ -1894,9 +1894,6 @@ "unreadableSkillSource": "Un dossier de skill n'a pas répondu ; cet état peut donc être incomplet." }, "useMacosTccPromptNotice": { - "title": "Vous voyez des invites « Orca souhaite accéder à… » ?", - "description": "Des messages d'autorisation macOS peuvent apparaître quand un agent ou un outil de terminal exécuté dans Orca tente d'accéder à des fichiers protégés. Accordez l'Accès complet au disque dans les paramètres pour réduire ces invites.", - "openSettings": "Ouvrir les paramètres", "dismiss": "Ne plus afficher" }, "useMacTccAttributionSeveredNotice": { diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json index 511abc5dd2d..8118800757c 100644 --- a/src/renderer/src/i18n/locales/ja.json +++ b/src/renderer/src/i18n/locales/ja.json @@ -1614,9 +1614,6 @@ "pasteTooLarge": "貼り付け内容が大きすぎます。" }, "useMacosTccPromptNotice": { - "title": "「Orca がアクセスしようとしています…」という確認が表示されますか?", - "description": "Orca で実行中の Agent やターミナルツールが保護されたファイルにアクセスしようとすると、macOS の権限メッセージが表示されることがあります。これらの確認を減らすには、設定でフルディスクアクセスを許可してください。", - "openSettings": "設定を開く", "dismiss": "今後表示しない" }, "useInstalledAgentSkills": { diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json index e15a1ef2e38..3d5d02e435e 100644 --- a/src/renderer/src/i18n/locales/ko.json +++ b/src/renderer/src/i18n/locales/ko.json @@ -1614,9 +1614,6 @@ "pasteTooLarge": "붙여넣기 내용이 너무 큽니다." }, "useMacosTccPromptNotice": { - "title": "“Orca에서 접근하려고 합니다…” 권한 요청이 표시되나요?", - "description": "Orca에서 실행 중인 에이전트나 터미널 도구가 보호된 파일에 접근하려고 하면 macOS 권한 메시지가 표시될 수 있습니다. 이러한 요청을 줄이려면 설정에서 전체 디스크 접근 권한을 허용하세요.", - "openSettings": "설정 열기", "dismiss": "다시 표시하지 않기" }, "useInstalledAgentSkills": { diff --git a/src/renderer/src/i18n/locales/pt.json b/src/renderer/src/i18n/locales/pt.json index e6a95271fa6..d82a01fb373 100644 --- a/src/renderer/src/i18n/locales/pt.json +++ b/src/renderer/src/i18n/locales/pt.json @@ -2528,9 +2528,6 @@ "unreadableSkillSource": "Uma pasta de skill não respondeu, então este status pode estar incompleto." }, "useMacosTccPromptNotice": { - "title": "Está vendo avisos de “O Orca gostaria de acessar…”?", - "description": "Mensagens de permissão do macOS podem aparecer quando um agente ou ferramenta de terminal em execução no Orca tenta acessar arquivos protegidos. Conceda Acesso Total ao Disco em Configurações para reduzir esses avisos.", - "openSettings": "Abrir Configurações", "dismiss": "Não mostrar novamente" }, "useMacTccAttributionSeveredNotice": { diff --git a/src/renderer/src/i18n/locales/ru.json b/src/renderer/src/i18n/locales/ru.json index 2a19b39d005..acf5f1b7738 100644 --- a/src/renderer/src/i18n/locales/ru.json +++ b/src/renderer/src/i18n/locales/ru.json @@ -2154,9 +2154,6 @@ "unreadableSkillSource": "Папка одного из навыков не ответила, поэтому статус может быть неполным." }, "useMacosTccPromptNotice": { - "title": "Видите запросы «Orca хочет получить доступ…»?", - "description": "macOS может запрашивать разрешения, когда агент или инструмент терминала в Orca обращается к защищённым файлам. Предоставьте полный доступ к диску в настройках, чтобы таких запросов стало меньше.", - "openSettings": "Открыть настройки", "dismiss": "Больше не показывать" }, "useMacTccAttributionSeveredNotice": { diff --git a/src/renderer/src/i18n/locales/tr.json b/src/renderer/src/i18n/locales/tr.json index 84d6397c7f8..c8aac0ff0f6 100644 --- a/src/renderer/src/i18n/locales/tr.json +++ b/src/renderer/src/i18n/locales/tr.json @@ -2082,9 +2082,6 @@ "unreadableSkillSource": "Bir beceri klasörü yanıt vermediği için durum bilgisi eksik olabilir." }, "useMacosTccPromptNotice": { - "title": "“Orca şuna erişmek istiyor…” uyarıları mı görüyorsunuz?", - "description": "Orca'da çalışan bir ajan veya terminal aracı korumalı dosyalara erişmeye çalıştığında macOS'tan izin mesajları görünebilir. Bu uyarıları azaltmak için Ayarlar'dan Tam Disk Erişimi verin.", - "openSettings": "Ayarları Aç", "dismiss": "Bir daha gösterme" }, "useMacTccAttributionSeveredNotice": { diff --git a/src/renderer/src/i18n/locales/zh-Hant.json b/src/renderer/src/i18n/locales/zh-Hant.json index 9a250e4ea66..fdc4ceb3682 100644 --- a/src/renderer/src/i18n/locales/zh-Hant.json +++ b/src/renderer/src/i18n/locales/zh-Hant.json @@ -2061,9 +2061,6 @@ "unreadableSkillSource": "技能資料夾沒有回應,因此此狀態可能不完整。" }, "useMacosTccPromptNotice": { - "title": "看到「Orca 想要存取…」提示?", - "description": "當 Orca 中執行的代理程式或終端機工具嘗試存取受保護的檔案時,macOS 可能會顯示權限訊息。請在「設定」中授予「完整磁碟存取權」,以減少此類提示。", - "openSettings": "開啟設定", "dismiss": "不再顯示" }, "useMacTccAttributionSeveredNotice": { diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json index 2d291616a23..a2efd9b54c4 100644 --- a/src/renderer/src/i18n/locales/zh.json +++ b/src/renderer/src/i18n/locales/zh.json @@ -1800,9 +1800,6 @@ "pasteTooLarge": "粘贴内容过大" }, "useMacosTccPromptNotice": { - "title": "看到\"Orca 想要访问…\"提示?", - "description": "当 Orca 中运行的代理或终端工具尝试访问受保护的文件时,macOS 可能会显示权限信息,请在\"设置\"中授予\"完全磁盘访问权限\",以减少此类提示", - "openSettings": "打开设置", "dismiss": "不再显示" }, "useInstalledAgentSkills": { From a0fc70d291d8195dedbd418201f23d745b26e448 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 23:12:20 -0800 Subject: [PATCH 031/178] test: remove low-value repo notifier and schema cache suites (#41877) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 2 - .../ipc/repos-remote-client-events.test.ts | 81 ------------------- .../zod-schema-compilation.test.ts | 16 ---- 3 files changed, 99 deletions(-) delete mode 100644 src/main/ipc/repos-remote-client-events.test.ts delete mode 100644 src/main/schema-validation/zod-schema-compilation.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index d866a51759f..79048338914 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -2068,7 +2068,6 @@ "src/main/ipc/repos-execution-host-catalog.test.ts": 1426, "src/main/ipc/repos-nested-import.test.ts": 1299, "src/main/ipc/repos-nested-scan.test.ts": 1236, - "src/main/ipc/repos-remote-client-events.test.ts": 1914, "src/main/ipc/repos/folder-workspace-attachment-handlers.test.ts": 1932, "src/main/ipc/repos/local-repo-registration.test.ts": 469, "src/main/ipc/repos/project-host-setup-wsl-move-fence.test.ts": 2529, @@ -4112,7 +4111,6 @@ "src/main/zcode/zcode-plan-api-key-store.test.ts": 89, "src/main/zcode/zcode-v2-personal-overlay-raw-json.test.ts": 259, "src/main/zcode/zcode-v2-plan-credentials.test.ts": 934, - "src/main/schema-validation/zod-schema-compilation.test.ts": 87, "src/packages/process-host/src/bounded-output-sink.test.ts": 75, "src/packages/process-host/src/close-process-registry.test.ts": 39, "src/packages/process-host/src/growing-byte-buffer.test.ts": 102, diff --git a/src/main/ipc/repos-remote-client-events.test.ts b/src/main/ipc/repos-remote-client-events.test.ts deleted file mode 100644 index 68fc3acc186..00000000000 --- a/src/main/ipc/repos-remote-client-events.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** Paired clients refetch only on reposChanged; broadcast failures must not reject committed mutations. */ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type * as ReposChangedNotificationModule from './repos/repos-changed-notification' - -const { handleMock, mockStore } = vi.hoisted(() => ({ - handleMock: vi.fn(), - mockStore: { - getRepos: vi.fn().mockReturnValue([]), - getRepo: vi.fn(), - addRepo: vi.fn(), - updateRepo: vi.fn(), - removeProjectForHost: vi.fn(), - reorderRepos: vi.fn().mockReturnValue(true) - } -})) - -vi.mock('electron', () => ({ - dialog: { showOpenDialog: vi.fn() }, - ipcMain: { handle: handleMock, removeHandler: vi.fn() } -})) - -vi.mock('../git/repositories/discovery/repo-detection', () => ({ - isGitRepo: vi.fn().mockReturnValue(true) -})) -vi.mock('../git/repositories/repository-name', () => ({ - getRepoName: vi.fn().mockImplementation((path: string) => path.split('/').pop()) -})) -vi.mock('../git/refs/resolution/repo-default-base-ref', () => ({ - getDefaultBaseRefAsync: vi.fn().mockResolvedValue('origin/main') -})) -vi.mock('../git/refs/resolution/repo-base-ref-search', () => ({ - searchBaseRefs: vi.fn().mockResolvedValue([]) -})) - -vi.mock('./worktrees/removal/registered-worktree-roots-cache', () => ({ - invalidateAuthorizedRootsCache: vi.fn(), - invalidateAuthorizedRootsCacheForRepo: vi.fn() -})) - -type HandlerMap = Map unknown> - -const handlers: HandlerMap = new Map() -const mainWindow = { isDestroyed: () => false, webContents: { send: vi.fn() } } - -/** Fresh module instance per test so the module-scoped notifier holder starts unset. */ -async function registerHandlersWithoutNotifier(): Promise { - vi.resetModules() - const repos = await import('./repos') - repos.registerRepoHandlers(mainWindow as never, mockStore as never, {} as never) - return import('./repos/repos-changed-notification') -} - -async function registerHandlersWithNotifier( - notifyReposChangedForRemoteClients: () => void -): Promise { - const notification = await registerHandlersWithoutNotifier() - notification.setRepoRemoteClientNotifier({ notifyReposChangedForRemoteClients } as never) -} - -beforeEach(() => { - handlers.clear() - handleMock.mockReset() - handleMock.mockImplementation((channel: string, handler: (...a: unknown[]) => unknown) => { - handlers.set(channel, handler) - }) - mainWindow.webContents.send.mockReset() - mockStore.removeProjectForHost.mockReset() - mockStore.reorderRepos.mockReset().mockReturnValue(true) -}) - -describe('repo IPC mutations notify paired clients', () => { - it('does not reject the handler when the notifier throws', async () => { - await registerHandlersWithNotifier(() => { - throw new Error('client event stream exploded') - }) - - await expect( - handlers.get('repos:removeForHost')!(null, { repoId: 'repo-1', hostId: 'local' }) - ).resolves.toBeUndefined() - }) -}) diff --git a/src/main/schema-validation/zod-schema-compilation.test.ts b/src/main/schema-validation/zod-schema-compilation.test.ts deleted file mode 100644 index a683a1ded77..00000000000 --- a/src/main/schema-validation/zod-schema-compilation.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { z } from 'zod' -import { getCompiledSchema } from './zod-schema-compilation' - -describe('getCompiledSchema', () => { - it('compiles once per schema identity and leaves the original parser untouched', () => { - const schema = z.object({ value: z.string() }) - const compiled = getCompiledSchema(schema) - expect(compiled).not.toBe(schema) - expect(getCompiledSchema(schema)).toBe(compiled) - expect(getCompiledSchema(z.object({ value: z.string() }))).not.toBe(compiled) - expect(schema.safeParse({ value: 'ok', extra: true })).toEqual( - compiled.safeParse({ value: 'ok', extra: true }) - ) - }) -}) From 7582371078708ffabbe00ef519fcc1ee2aeba011 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:14:54 -0800 Subject: [PATCH 032/178] perf: resolve launch preference owner without building route (#41803) --- .../launch/routing/agent-launch-route-input.ts | 11 +++++++++-- .../use-host-agent-launch-preferences.ts | 6 ++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/lib/agent-session/launch/routing/agent-launch-route-input.ts b/src/renderer/src/lib/agent-session/launch/routing/agent-launch-route-input.ts index fc0fe438133..0fae5c20ccd 100644 --- a/src/renderer/src/lib/agent-session/launch/routing/agent-launch-route-input.ts +++ b/src/renderer/src/lib/agent-session/launch/routing/agent-launch-route-input.ts @@ -188,14 +188,21 @@ function hostStructuredAgentsInput( return agents ? { hostStructuredAgents: agents.map((row) => row.agent) } : {} } -export function resolveAgentLaunchHost( +export function resolveAgentLaunchOwner( store: AgentLaunchRouteStore, workspace: ProspectiveWorkspace ) { const owner = workspace.worktreeId ? resolveStructuredAgentSessionOwner(store, workspace.worktreeId) : undefined - const executionHostId = owner ?? resolveExecutionHostId(store, workspace) + return { owner, executionHostId: owner ?? resolveExecutionHostId(store, workspace) } +} + +export function resolveAgentLaunchHost( + store: AgentLaunchRouteStore, + workspace: ProspectiveWorkspace +) { + const { owner, executionHostId } = resolveAgentLaunchOwner(store, workspace) return { owner, executionHostId, diff --git a/src/renderer/src/runtime/agent-launch-settings/use-host-agent-launch-preferences.ts b/src/renderer/src/runtime/agent-launch-settings/use-host-agent-launch-preferences.ts index c5a5a1e4583..3a385d39b6d 100644 --- a/src/renderer/src/runtime/agent-launch-settings/use-host-agent-launch-preferences.ts +++ b/src/renderer/src/runtime/agent-launch-settings/use-host-agent-launch-preferences.ts @@ -6,7 +6,7 @@ import { parseExecutionHostId } from '../../../../shared/execution-host' import { lastVerifiedRuntimeStatus } from '../../../../shared/remote-runtime/host-status/runtime-host-status' import type { AppState } from '@/store/types' import { - buildAgentLaunchRouteInput, + resolveAgentLaunchOwner, type ProspectiveWorkspace } from '@/lib/agent-session/launch/routing/agent-launch-route-input' import { workspaceKindForWorktreeId } from '../../../../shared/agent-launch/contracts/workspace-launch-kind' @@ -32,9 +32,7 @@ function preferencesForWorkspace(state: AppState, workspace: ProspectiveWorkspac ? { agentLaunchChoices: state.settings.agentLaunchChoices } : {}) } - const host = parseExecutionHostId( - buildAgentLaunchRouteInput(state, { agent: 'claude', workspace }).executionHostId - ) + const host = parseExecutionHostId(resolveAgentLaunchOwner(state, workspace).executionHostId) const owner = host?.kind === 'runtime' ? { From 9c7800d140a946eca9d337b8d679aeab533a7c1c Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:21:03 -0800 Subject: [PATCH 033/178] refactor: organize feature wall by concrete owners (#41875) --- config/focus-setting-read-baseline.txt | 10 +- config/react-compiler-bailout-baseline.txt | 20 +- config/react-compiler-uncompiled-files.txt | 4 +- .../scripts/ci/sharding/ci-shard-timings.json | 4 +- .../ChatUiHereFeatureTipVisual.tsx | 2 +- .../feature-tips/CliFeatureTipVisual.tsx | 4 +- .../CmdJPaletteFeatureTipVisual.tsx | 2 +- .../SessionSearchFeatureTipVisual.tsx | 2 +- .../VoiceDictationFeatureTipVisual.tsx | 2 +- .../feature-wall/FeatureWallModal.tsx | 4 +- .../FeatureWallSetupWorkflowActions.test.tsx | 73 ------- .../feature-wall/FeatureWallTourSurface.tsx | 27 +-- .../AgentsOrchestrationVisual.tsx | 8 +- .../UsageAccountsCard.tsx | 2 +- .../BrowserAgentVerificationPane.tsx | 0 .../BrowserAnimatedVisual.tsx | 0 .../BrowserPricingStoryboard.tsx | 0 .../BrowserSignupStoryboard.tsx | 0 .../BrowserStoryboardWindow.tsx | 2 +- .../browser-animated-visual-phase.ts | 0 .../browser-animated-visual-sequence.ts | 0 .../use-browser-animated-visual-storyboard.ts | 0 .../AiCommitPrSettingsCard.discovery.test.tsx | 8 +- .../AiCommitPrSettingsCard.test.tsx | 8 +- .../AiCommitPrSettingsCard.tsx | 2 +- .../AiCommitPrSettingsFields.tsx | 18 +- .../AiCommitPrSettingsSwitch.tsx | 0 .../ai-commit-pr-picker.ts | 12 +- .../commit-message-settings-selection.ts} | 8 +- .../source-control-model-label.ts | 0 .../useAiCommitPrSettings.ts | 18 +- .../feature-wall-completion-persistence.ts | 8 +- .../feature-wall-completion-progress.ts | 8 +- .../feature-wall-usage-tracking.ts | 2 +- .../use-feature-wall-completion.ts | 14 +- .../use-feature-wall-session-depth.ts | 12 +- .../use-persisted-feature-wall-completion.ts | 8 +- .../EditorAnimatedVisual.tsx | 2 +- .../editor-animated-toolbar-icons.tsx | 0 .../editor-animated-visual-markup.ts | 0 .../use-editor-animated-visual-animation.ts | 0 .../ReviewAnimatedVisual.tsx | 4 +- .../ReviewNotesAnimatedVisual.tsx | 4 +- .../ReviewPRViewAnimatedVisual.tsx | 6 +- .../ReviewShipAnimatedVisual.tsx | 9 +- .../review-animated-visual-notes-styles.tsx | 0 .../review-animated-visual-pr-view-styles.tsx | 0 .../review-animated-visual-ship-styles.tsx | 0 .../review-diff-tokenizer.ts | 10 + .../review-migration-scenario.ts | 63 ++++++ .../review-notes-diff-rows.tsx | 3 +- .../review-notes-terminal-phase.ts | 2 +- .../review-pr-view-animation.ts | 0 .../{ => setup}/FeatureWallSetupChecklist.tsx | 28 +-- .../FeatureWallSetupStepVisuals.tsx | 0 .../FeatureWallSetupWorkflowActions.tsx | 16 +- .../FullDiskAccessSetupPrompt.test.ts | 2 +- .../{ => setup}/FullDiskAccessSetupPrompt.tsx | 4 +- .../AgentCapabilitiesSetupAction.tsx | 12 +- .../AgentCapabilityStatusBadges.tsx | 0 .../BrowserUseSkillSetupCard.tsx | 2 +- .../OrchestrationSetupCard.tsx | 2 +- .../agent-capability-setup-copy.ts | 0 .../agent-capability-setup-rows.tsx | 2 +- .../agent-capability-setup-status.ts | 4 +- .../browser}/FeatureWallBrowserAction.tsx | 16 +- ...ure-wall-setup-checklist-localized-copy.ts | 2 +- .../feature-wall-setup-progress.ts | 12 +- .../integrations}/ConnectIntegrationsList.tsx | 0 .../connect-integration-step.tsx | 2 - ...e-feature-wall-task-source-presentation.ts | 4 +- .../use-integration-connection-status.ts | 2 +- .../FeatureWallClickRing.tsx | 0 .../storyboard-glyphs.tsx} | 75 ------- .../TasksAnimatedVisual.tsx | 4 +- .../{ => tour}/FeatureWallBody.tsx | 36 ++-- .../{ => tour}/FeatureWallContinueButton.tsx | 0 .../{ => tour}/FeatureWallPreview.tsx | 4 +- .../{ => tour}/FeatureWallRail.tsx | 8 +- .../{ => tour}/FeatureWallTourPanel.tsx | 14 +- .../feature-wall/{ => tour}/KeepAwakeCard.tsx | 6 +- .../feature-wall-active-step-copy.ts | 6 +- .../{ => tour}/feature-wall-assets.ts | 0 .../feature-wall-open-source.ts} | 3 +- .../feature-wall-rail-navigation.ts | 0 .../feature-wall-tour-localized-copy.ts | 10 +- ...use-feature-wall-tour-keyboard-shortcut.ts | 0 .../use-feature-wall-tour-rail-keydown.ts | 2 +- .../use-feature-wall-tour-skills.ts | 0 .../use-feature-wall-tour-telemetry.ts | 10 +- .../WorkbenchAnimatedVisual.tsx | 2 +- .../WorkbenchTerminalPanes.tsx | 2 +- .../WorkbenchTerminalSplitMenu.tsx | 0 .../use-workbench-terminal-cursor.ts | 0 .../use-workbench-terminal-storyboard.ts | 0 .../workbench-terminal-storyboard-sequence.ts | 0 .../workbench-terminal-storyboard-state.ts | 0 .../WorkspacesAnimatedVisual.tsx | 2 +- .../ai/text-generation-dialog-form.tsx | 4 +- .../setup-guide/SettingsSetupGuidePane.tsx | 2 +- .../SetupGuideModal.mount-gating.test.tsx | 194 ------------------ .../setup-guide/SetupGuideModal.tsx | 4 +- .../setup-guide-browser-milestone-progress.ts | 4 +- .../setup-guide/use-setup-guide-progress.ts | 6 +- .../setup-guide/use-setup-guide-telemetry.ts | 2 +- .../sidebar/SetupGuideSidebarEntry.tsx | 2 +- .../wsl-move/wsl-move-storage-dispositions.ts | 4 +- 107 files changed, 320 insertions(+), 591 deletions(-) delete mode 100644 src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.test.tsx rename src/renderer/src/components/feature-wall/{ => agents-orchestration}/AgentsOrchestrationVisual.tsx (87%) rename src/renderer/src/components/feature-wall/{ => browser-storyboard}/BrowserAgentVerificationPane.tsx (100%) rename src/renderer/src/components/feature-wall/{ => browser-storyboard}/BrowserAnimatedVisual.tsx (100%) rename src/renderer/src/components/feature-wall/{ => browser-storyboard}/BrowserPricingStoryboard.tsx (100%) rename src/renderer/src/components/feature-wall/{ => browser-storyboard}/BrowserSignupStoryboard.tsx (100%) rename src/renderer/src/components/feature-wall/{ => browser-storyboard}/BrowserStoryboardWindow.tsx (99%) rename src/renderer/src/components/feature-wall/{ => browser-storyboard}/browser-animated-visual-phase.ts (100%) rename src/renderer/src/components/feature-wall/{ => browser-storyboard}/browser-animated-visual-sequence.ts (100%) rename src/renderer/src/components/feature-wall/{ => browser-storyboard}/use-browser-animated-visual-storyboard.ts (100%) rename src/renderer/src/components/feature-wall/{ => commit-pr-settings}/AiCommitPrSettingsCard.discovery.test.tsx (90%) rename src/renderer/src/components/feature-wall/{ => commit-pr-settings}/AiCommitPrSettingsCard.test.tsx (93%) rename src/renderer/src/components/feature-wall/{ => commit-pr-settings}/AiCommitPrSettingsCard.tsx (97%) rename src/renderer/src/components/feature-wall/{ => commit-pr-settings}/AiCommitPrSettingsFields.tsx (84%) rename src/renderer/src/components/feature-wall/{ => commit-pr-settings}/AiCommitPrSettingsSwitch.tsx (100%) rename src/renderer/src/components/feature-wall/{ => commit-pr-settings}/ai-commit-pr-picker.ts (82%) rename src/renderer/src/components/feature-wall/{ai-commit-pr-settings-helpers.ts => commit-pr-settings/commit-message-settings-selection.ts} (88%) rename src/renderer/src/components/feature-wall/{ => commit-pr-settings}/source-control-model-label.ts (100%) rename src/renderer/src/components/feature-wall/{ => commit-pr-settings}/useAiCommitPrSettings.ts (90%) rename src/renderer/src/components/feature-wall/{ => completion}/feature-wall-completion-persistence.ts (96%) rename src/renderer/src/components/feature-wall/{ => completion}/feature-wall-completion-progress.ts (93%) rename src/renderer/src/components/feature-wall/{ => completion}/feature-wall-usage-tracking.ts (96%) rename src/renderer/src/components/feature-wall/{ => completion}/use-feature-wall-completion.ts (94%) rename src/renderer/src/components/feature-wall/{ => completion}/use-feature-wall-session-depth.ts (90%) rename src/renderer/src/components/feature-wall/{ => completion}/use-persisted-feature-wall-completion.ts (93%) rename src/renderer/src/components/feature-wall/{ => editor-storyboard}/EditorAnimatedVisual.tsx (99%) rename src/renderer/src/components/feature-wall/{ => editor-storyboard}/editor-animated-toolbar-icons.tsx (100%) rename src/renderer/src/components/feature-wall/{ => editor-storyboard}/editor-animated-visual-markup.ts (100%) rename src/renderer/src/components/feature-wall/{ => editor-storyboard}/use-editor-animated-visual-animation.ts (100%) rename src/renderer/src/components/feature-wall/{ => review-storyboard}/ReviewAnimatedVisual.tsx (90%) rename src/renderer/src/components/feature-wall/{ => review-storyboard}/ReviewNotesAnimatedVisual.tsx (99%) rename src/renderer/src/components/feature-wall/{ => review-storyboard}/ReviewPRViewAnimatedVisual.tsx (98%) rename src/renderer/src/components/feature-wall/{ => review-storyboard}/ReviewShipAnimatedVisual.tsx (98%) rename src/renderer/src/components/feature-wall/{ => review-storyboard}/review-animated-visual-notes-styles.tsx (100%) rename src/renderer/src/components/feature-wall/{ => review-storyboard}/review-animated-visual-pr-view-styles.tsx (100%) rename src/renderer/src/components/feature-wall/{ => review-storyboard}/review-animated-visual-ship-styles.tsx (100%) create mode 100644 src/renderer/src/components/feature-wall/review-storyboard/review-diff-tokenizer.ts create mode 100644 src/renderer/src/components/feature-wall/review-storyboard/review-migration-scenario.ts rename src/renderer/src/components/feature-wall/{ => review-storyboard}/review-notes-diff-rows.tsx (96%) rename src/renderer/src/components/feature-wall/{ => review-storyboard}/review-notes-terminal-phase.ts (97%) rename src/renderer/src/components/feature-wall/{ => review-storyboard}/review-pr-view-animation.ts (100%) rename src/renderer/src/components/feature-wall/{ => setup}/FeatureWallSetupChecklist.tsx (92%) rename src/renderer/src/components/feature-wall/{ => setup}/FeatureWallSetupStepVisuals.tsx (100%) rename src/renderer/src/components/feature-wall/{ => setup}/FeatureWallSetupWorkflowActions.tsx (93%) rename src/renderer/src/components/feature-wall/{ => setup}/FullDiskAccessSetupPrompt.test.ts (98%) rename src/renderer/src/components/feature-wall/{ => setup}/FullDiskAccessSetupPrompt.tsx (97%) rename src/renderer/src/components/feature-wall/{ => setup/agent-capabilities}/AgentCapabilitiesSetupAction.tsx (96%) rename src/renderer/src/components/feature-wall/{ => setup/agent-capabilities}/AgentCapabilityStatusBadges.tsx (100%) rename src/renderer/src/components/feature-wall/{ => setup/agent-capabilities}/BrowserUseSkillSetupCard.tsx (97%) rename src/renderer/src/components/feature-wall/{ => setup/agent-capabilities}/OrchestrationSetupCard.tsx (95%) rename src/renderer/src/components/feature-wall/{ => setup/agent-capabilities}/agent-capability-setup-copy.ts (100%) rename src/renderer/src/components/feature-wall/{ => setup/agent-capabilities}/agent-capability-setup-rows.tsx (95%) rename src/renderer/src/components/feature-wall/{ => setup/agent-capabilities}/agent-capability-setup-status.ts (98%) rename src/renderer/src/components/feature-wall/{ => setup/browser}/FeatureWallBrowserAction.tsx (93%) rename src/renderer/src/components/feature-wall/{ => setup}/feature-wall-setup-checklist-localized-copy.ts (98%) rename src/renderer/src/components/feature-wall/{ => setup}/feature-wall-setup-progress.ts (89%) rename src/renderer/src/components/feature-wall/{ => setup/integrations}/ConnectIntegrationsList.tsx (100%) rename src/renderer/src/components/feature-wall/{ => setup/integrations}/connect-integration-step.tsx (98%) rename src/renderer/src/components/feature-wall/{ => setup/integrations}/use-feature-wall-task-source-presentation.ts (94%) rename src/renderer/src/components/feature-wall/{ => setup/integrations}/use-integration-connection-status.ts (98%) rename src/renderer/src/components/feature-wall/{ => storyboard-interaction}/FeatureWallClickRing.tsx (100%) rename src/renderer/src/components/feature-wall/{review-animated-visual-shared.tsx => storyboard-interaction/storyboard-glyphs.tsx} (72%) rename src/renderer/src/components/feature-wall/{ => task-storyboard}/TasksAnimatedVisual.tsx (98%) rename src/renderer/src/components/feature-wall/{ => tour}/FeatureWallBody.tsx (86%) rename src/renderer/src/components/feature-wall/{ => tour}/FeatureWallContinueButton.tsx (100%) rename src/renderer/src/components/feature-wall/{ => tour}/FeatureWallPreview.tsx (97%) rename src/renderer/src/components/feature-wall/{ => tour}/FeatureWallRail.tsx (97%) rename src/renderer/src/components/feature-wall/{ => tour}/FeatureWallTourPanel.tsx (93%) rename src/renderer/src/components/feature-wall/{ => tour}/KeepAwakeCard.tsx (89%) rename src/renderer/src/components/feature-wall/{ => tour}/feature-wall-active-step-copy.ts (81%) rename src/renderer/src/components/feature-wall/{ => tour}/feature-wall-assets.ts (100%) rename src/renderer/src/components/feature-wall/{feature-wall-modal-helpers.ts => tour/feature-wall-open-source.ts} (76%) rename src/renderer/src/components/feature-wall/{ => tour}/feature-wall-rail-navigation.ts (100%) rename src/renderer/src/components/feature-wall/{ => tour}/feature-wall-tour-localized-copy.ts (94%) rename src/renderer/src/components/feature-wall/{ => tour}/use-feature-wall-tour-keyboard-shortcut.ts (100%) rename src/renderer/src/components/feature-wall/{ => tour}/use-feature-wall-tour-rail-keydown.ts (95%) rename src/renderer/src/components/feature-wall/{ => tour}/use-feature-wall-tour-skills.ts (100%) rename src/renderer/src/components/feature-wall/{ => tour}/use-feature-wall-tour-telemetry.ts (91%) rename src/renderer/src/components/feature-wall/{ => workbench-storyboard}/WorkbenchAnimatedVisual.tsx (98%) rename src/renderer/src/components/feature-wall/{ => workbench-storyboard}/WorkbenchTerminalPanes.tsx (99%) rename src/renderer/src/components/feature-wall/{ => workbench-storyboard}/WorkbenchTerminalSplitMenu.tsx (100%) rename src/renderer/src/components/feature-wall/{ => workbench-storyboard}/use-workbench-terminal-cursor.ts (100%) rename src/renderer/src/components/feature-wall/{ => workbench-storyboard}/use-workbench-terminal-storyboard.ts (100%) rename src/renderer/src/components/feature-wall/{ => workbench-storyboard}/workbench-terminal-storyboard-sequence.ts (100%) rename src/renderer/src/components/feature-wall/{ => workbench-storyboard}/workbench-terminal-storyboard-state.ts (100%) rename src/renderer/src/components/feature-wall/{ => workspace-storyboard}/WorkspacesAnimatedVisual.tsx (99%) delete mode 100644 src/renderer/src/components/setup-guide/SetupGuideModal.mount-gating.test.tsx diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index 94adc8e7a24..00962b987d8 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -37,12 +37,12 @@ 1 src/renderer/src/components/editor/restored-editor-workspace-runtime-owner.ts 2 src/renderer/src/components/editor/useEditorPanelContentState.ts 1 src/renderer/src/components/editor/useEditorPanelFileContentLoader.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 +2 src/renderer/src/components/feature-wall/setup/integrations/ConnectIntegrationsList.tsx +1 src/renderer/src/components/feature-wall/setup/browser/FeatureWallBrowserAction.tsx +1 src/renderer/src/components/feature-wall/setup/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 +2 src/renderer/src/components/feature-wall/setup/integrations/use-feature-wall-task-source-presentation.ts +2 src/renderer/src/components/feature-wall/setup/integrations/use-integration-connection-status.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 1 src/renderer/src/components/integrations/source-control/bitbucket-credentials-dialog.tsx diff --git a/config/react-compiler-bailout-baseline.txt b/config/react-compiler-bailout-baseline.txt index f8205003fc2..ecf0b935257 100644 --- a/config/react-compiler-bailout-baseline.txt +++ b/config/react-compiler-bailout-baseline.txt @@ -150,19 +150,19 @@ 1 src/renderer/src/components/emulator-pane/use-emulator-video-stream.ts 1 src/renderer/src/components/emulator-pane/use-mobile-emulator-agent-setup-state.ts 1 src/renderer/src/components/feature-tips/FeatureTipsModal.tsx -1 src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.tsx -1 src/renderer/src/components/feature-wall/BrowserPricingStoryboard.tsx -1 src/renderer/src/components/feature-wall/BrowserStoryboardWindow.tsx -2 src/renderer/src/components/feature-wall/EditorAnimatedVisual.tsx -1 src/renderer/src/components/feature-wall/FeatureWallBrowserAction.tsx -2 src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx +1 src/renderer/src/components/feature-wall/setup/agent-capabilities/AgentCapabilitiesSetupAction.tsx +1 src/renderer/src/components/feature-wall/browser-storyboard/BrowserPricingStoryboard.tsx +1 src/renderer/src/components/feature-wall/browser-storyboard/BrowserStoryboardWindow.tsx +2 src/renderer/src/components/feature-wall/editor-storyboard/EditorAnimatedVisual.tsx +1 src/renderer/src/components/feature-wall/setup/browser/FeatureWallBrowserAction.tsx +2 src/renderer/src/components/feature-wall/setup/FeatureWallSetupChecklist.tsx 1 src/renderer/src/components/feature-wall/FeatureWallTourSurface.tsx -1 src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.tsx -1 src/renderer/src/components/feature-wall/WorkbenchTerminalSplitMenu.tsx +1 src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.tsx +1 src/renderer/src/components/feature-wall/workbench-storyboard/WorkbenchTerminalSplitMenu.tsx 1 src/renderer/src/components/feature-wall/agents-orchestration/OrchestrationPage.tsx 2 src/renderer/src/components/feature-wall/agents-orchestration/UsageAccountsCard.tsx -2 src/renderer/src/components/feature-wall/use-feature-wall-tour-telemetry.ts -3 src/renderer/src/components/feature-wall/useAiCommitPrSettings.ts +2 src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-telemetry.ts +3 src/renderer/src/components/feature-wall/commit-pr-settings/useAiCommitPrSettings.ts 1 src/renderer/src/components/floating-terminal/FloatingTerminalIconContextMenu.tsx 3 src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.tsx 1 src/renderer/src/components/floating-terminal/use-floating-terminal-panel-geometry.ts diff --git a/config/react-compiler-uncompiled-files.txt b/config/react-compiler-uncompiled-files.txt index 3eb96f759b9..f542bc6648a 100644 --- a/config/react-compiler-uncompiled-files.txt +++ b/config/react-compiler-uncompiled-files.txt @@ -3,8 +3,8 @@ # are skipped too. Remove a line once its file is safe to compile. # Render-time reads of providerRuntimeSessionGeneration (lib/provider-runtime-context.ts; hazard live-read-in-memo-block). -src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx -src/renderer/src/components/feature-wall/use-integration-connection-status.ts +src/renderer/src/components/feature-wall/setup/FeatureWallSetupChecklist.tsx +src/renderer/src/components/feature-wall/setup/integrations/use-integration-connection-status.ts src/renderer/src/components/integrations/task-trackers/jira-integration-card.tsx src/renderer/src/components/integrations/task-trackers/linear-integration-card.tsx src/renderer/src/components/new-workspace/use-jira-source-connection.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 79048338914..b728d8b7fe5 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -4547,8 +4547,7 @@ "src/renderer/src/components/emulator-pane/use-emulator-pane-session.test.tsx": 1968, "src/renderer/src/components/emulator-pane/use-mobile-emulator-tab-intro-actions.test.tsx": 1828, "src/renderer/src/components/feature-tips/ChatUiHereFeatureTipVisual.test.tsx": 1093, - "src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.test.tsx": 2028, - "src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.test.ts": 466, + "src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts": 466, "src/renderer/src/components/floating-terminal/floating-workspace-tab-reorder.test.ts": 2520, "src/renderer/src/components/github-checks-tab-state.test.ts": 52, "src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.assignee-identity.test.tsx": 461, @@ -4960,7 +4959,6 @@ "src/renderer/src/components/settings/runtime-environments/catalog/use-runtime-environment-catalog.test.tsx": 2252, "src/renderer/src/components/session-search/use-session-search-status.test.tsx": 477, "src/renderer/src/components/settings/accounts/usage/use-zcode-plan-credentials.test.tsx": 880, - "src/renderer/src/components/setup-guide/SetupGuideModal.mount-gating.test.tsx": 2348, "src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts": 1925, "src/renderer/src/components/shared/useDaemonActions.test.tsx": 332, "src/renderer/src/components/sidebar/host-admission/ssh/AddRemoteHostDialog.config-picker.test.tsx": 1178, diff --git a/src/renderer/src/components/feature-tips/ChatUiHereFeatureTipVisual.tsx b/src/renderer/src/components/feature-tips/ChatUiHereFeatureTipVisual.tsx index 56782a57355..c1755800862 100644 --- a/src/renderer/src/components/feature-tips/ChatUiHereFeatureTipVisual.tsx +++ b/src/renderer/src/components/feature-tips/ChatUiHereFeatureTipVisual.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState, type CSSProperties, type JSX, type ReactNode } from 'react' import { ArrowUp, ChevronDown, ChevronRight, Eye, Hand, Plus, Square, X } from 'lucide-react' import { LoadingSpinner } from '@/components/LoadingSpinner' -import { usePrefersReducedMotion } from '@/components/feature-wall/feature-wall-modal-helpers' +import { usePrefersReducedMotion } from '@/hooks/usePrefersReducedMotion' import { nativeChatPermissionModeLabel } from '@/components/native-chat/session/options/controls/native-chat-permission-mode-labels' import { nativeChatToolRowVerb } from '@/components/native-chat/tools/lines/native-chat-tool-line-label' import { useIntlLocale, useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-tips/CliFeatureTipVisual.tsx b/src/renderer/src/components/feature-tips/CliFeatureTipVisual.tsx index ab1dd5c599d..1a4daf7f747 100644 --- a/src/renderer/src/components/feature-tips/CliFeatureTipVisual.tsx +++ b/src/renderer/src/components/feature-tips/CliFeatureTipVisual.tsx @@ -1,10 +1,10 @@ import { useEffect, useState, type JSX } from 'react' -import { AgentsOrchestrationVisual } from '@/components/feature-wall/AgentsOrchestrationVisual' +import { AgentsOrchestrationVisual } from '@/components/feature-wall/agents-orchestration/AgentsOrchestrationVisual' import { ORCHESTRATION_CLI_COMMAND_LOOP_MS, ORCHESTRATION_CLI_COMMAND_TIMINGS_MS } from '@/components/feature-wall/agents-orchestration/orchestration-types' -import { usePrefersReducedMotion } from '@/components/feature-wall/feature-wall-modal-helpers' +import { usePrefersReducedMotion } from '@/hooks/usePrefersReducedMotion' import { useTranslate } from '@/i18n/use-translate' const CLI_AGENT_COMMANDS = [ diff --git a/src/renderer/src/components/feature-tips/CmdJPaletteFeatureTipVisual.tsx b/src/renderer/src/components/feature-tips/CmdJPaletteFeatureTipVisual.tsx index 3351fac3986..e5ea71008b2 100644 --- a/src/renderer/src/components/feature-tips/CmdJPaletteFeatureTipVisual.tsx +++ b/src/renderer/src/components/feature-tips/CmdJPaletteFeatureTipVisual.tsx @@ -1,6 +1,6 @@ import { Fragment, useEffect, useState, type JSX } from 'react' import { Plus, Search } from 'lucide-react' -import { usePrefersReducedMotion } from '@/components/feature-wall/feature-wall-modal-helpers' +import { usePrefersReducedMotion } from '@/hooks/usePrefersReducedMotion' import { formatShortcutKeyComboDetails, useShortcutKeyDetails } from '@/hooks/useShortcutLabel' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-tips/SessionSearchFeatureTipVisual.tsx b/src/renderer/src/components/feature-tips/SessionSearchFeatureTipVisual.tsx index 3eaf6d6db2c..e2d9f45f9a4 100644 --- a/src/renderer/src/components/feature-tips/SessionSearchFeatureTipVisual.tsx +++ b/src/renderer/src/components/feature-tips/SessionSearchFeatureTipVisual.tsx @@ -1,5 +1,5 @@ import { useEffect, useState, type JSX } from 'react' -import { usePrefersReducedMotion } from '@/components/feature-wall/feature-wall-modal-helpers' +import { usePrefersReducedMotion } from '@/hooks/usePrefersReducedMotion' import { DemoSessionPanel, DemoSessionRow } from './feature-tip-session-panel-demo' import { getSessionSearchDemoRecentRows, diff --git a/src/renderer/src/components/feature-tips/VoiceDictationFeatureTipVisual.tsx b/src/renderer/src/components/feature-tips/VoiceDictationFeatureTipVisual.tsx index 42d19c42f42..cc957805da3 100644 --- a/src/renderer/src/components/feature-tips/VoiceDictationFeatureTipVisual.tsx +++ b/src/renderer/src/components/feature-tips/VoiceDictationFeatureTipVisual.tsx @@ -1,7 +1,7 @@ import { useEffect, useState, type JSX } from 'react' import { Mic, Square } from 'lucide-react' import { ShortcutKeyCombo } from '@/components/ShortcutKeyCombo' -import { usePrefersReducedMotion } from '@/components/feature-wall/feature-wall-modal-helpers' +import { usePrefersReducedMotion } from '@/hooks/usePrefersReducedMotion' import { useShortcutKeyDetails } from '@/hooks/useShortcutLabel' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/FeatureWallModal.tsx b/src/renderer/src/components/feature-wall/FeatureWallModal.tsx index fb9f4e3426e..df85a01063b 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallModal.tsx +++ b/src/renderer/src/components/feature-wall/FeatureWallModal.tsx @@ -1,5 +1,5 @@ import type { JSX } from 'react' -import { getFeatureWallOpenSource } from './feature-wall-modal-helpers' +import { getFeatureWallOpenSource } from './tour/feature-wall-open-source' import { Dialog, DialogContent, @@ -7,7 +7,7 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog' -import { useAppStore } from '@/store' +import { useAppStore } from '@/store/index' import { FeatureWallTourSurface } from './FeatureWallTourSurface' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.test.tsx b/src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.test.tsx deleted file mode 100644 index b067087119f..00000000000 --- a/src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.test.tsx +++ /dev/null @@ -1,73 +0,0 @@ -// @vitest-environment happy-dom - -import { act } from 'react' -import type { Root } from 'react-dom/client' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { - cancelPendingSetupGuideTourRequest, - requestSetupGuideTourWhenReady -} from './FeatureWallSetupWorkflowActions' -import { useAppStore } from '@/store' - -vi.mock('sonner', () => ({ - toast: { - message: vi.fn(), - success: vi.fn(), - error: vi.fn() - } -})) - -const mountedRoots: Root[] = [] - -describe('setup guide workflow actions', () => { - afterEach(async () => { - await act(async () => { - for (const root of mountedRoots.splice(0)) { - root.unmount() - } - }) - document.body.innerHTML = '' - cancelPendingSetupGuideTourRequest() - vi.restoreAllMocks() - vi.unstubAllGlobals() - vi.useRealTimers() - useAppStore.setState({ - activeModal: 'none', - modalData: {}, - activeWorktreeId: null, - worktreesByRepo: {}, - tabsByWorktree: {}, - activeTabId: null, - activeGroupIdByWorktree: {}, - terminalLayoutsByTabId: {}, - repos: [], - activeRepoId: null, - activeContextualTourId: null - }) - }) - - it('gates setup-guide tour retries on the expected destination surface', () => { - vi.useFakeTimers() - const requestContextualTour = vi.fn() - useAppStore.setState({ - activeModal: 'none', - activeContextualTourId: null, - requestContextualTour: requestContextualTour as never - }) - - requestSetupGuideTourWhenReady({ - id: 'workspace-creation', - source: 'setup_guide_parallel_work', - shouldContinue: () => useAppStore.getState().activeModal === 'new-workspace-composer', - retryDelayMs: 10, - maxAttempts: 5 - }) - - vi.advanceTimersByTime(50) - - expect(requestContextualTour).not.toHaveBeenCalled() - - cancelPendingSetupGuideTourRequest() - vi.useRealTimers() - }) -}) diff --git a/src/renderer/src/components/feature-wall/FeatureWallTourSurface.tsx b/src/renderer/src/components/feature-wall/FeatureWallTourSurface.tsx index 3fe468361e1..28c48d78439 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallTourSurface.tsx +++ b/src/renderer/src/components/feature-wall/FeatureWallTourSurface.tsx @@ -11,23 +11,26 @@ import { import { getAgentsSteps, type AgentsStepId } from '../../../../shared/agents-orchestration-steps' import { getWorkbenchSteps, type WorkbenchStepId } from '../../../../shared/workbench-steps' import { getReviewSteps, type ReviewStepId } from '../../../../shared/review-steps' -import type { FeatureWallOpenSourceTelemetry } from '../../../../shared/telemetry-events' +import type { FeatureWallOpenSourceTelemetry } from '../../../../shared/telemetry-property-schemas' import type { FeatureWallTourDepthSummary } from '../../../../shared/feature-wall-tour-depth' import { track } from '@/diagnostics/telemetry/telemetry' import { useAppStore } from '@/store/index' -import { useFeatureWallTourSkills } from './use-feature-wall-tour-skills' +import { useFeatureWallTourSkills } from './tour/use-feature-wall-tour-skills' import { useActiveProjectSkillRuntime } from '@/hooks/skills/runtime/useActiveProjectSkillRuntime' -import { usePrefersReducedMotion } from './feature-wall-modal-helpers' -import { toFeatureWallAssetUrl, useFeatureWallAssetBaseUrl } from './feature-wall-assets' -import { useFeatureWallTaskSourcePresentation } from './use-feature-wall-task-source-presentation' -import { useFeatureWallCompletion } from './use-feature-wall-completion' -import { useFeatureWallTourTelemetry } from './use-feature-wall-tour-telemetry' -import { FeatureWallContinueButton } from './FeatureWallContinueButton' -import { FeatureWallTourPanel } from './FeatureWallTourPanel' -import { getFeatureWallActiveStepCopy, tourContinueLabel } from './feature-wall-active-step-copy' +import { usePrefersReducedMotion } from '../../hooks/usePrefersReducedMotion' +import { toFeatureWallAssetUrl, useFeatureWallAssetBaseUrl } from './tour/feature-wall-assets' +import { useFeatureWallTaskSourcePresentation } from './setup/integrations/use-feature-wall-task-source-presentation' +import { useFeatureWallCompletion } from './completion/use-feature-wall-completion' +import { useFeatureWallTourTelemetry } from './tour/use-feature-wall-tour-telemetry' +import { FeatureWallContinueButton } from './tour/FeatureWallContinueButton' +import { FeatureWallTourPanel } from './tour/FeatureWallTourPanel' +import { + getFeatureWallActiveStepCopy, + tourContinueLabel +} from './tour/feature-wall-active-step-copy' import { getScreenSubmitModifierLabel } from '@/lib/keyboard/screen-submit-shortcut' -import { useFeatureWallTourKeyboardShortcut } from './use-feature-wall-tour-keyboard-shortcut' -import { useFeatureWallTourRailKeydown } from './use-feature-wall-tour-rail-keydown' +import { useFeatureWallTourKeyboardShortcut } from './tour/use-feature-wall-tour-keyboard-shortcut' +import { useFeatureWallTourRailKeydown } from './tour/use-feature-wall-tour-rail-keydown' type FeatureWallTourSurfaceProps = { isOpen: boolean diff --git a/src/renderer/src/components/feature-wall/AgentsOrchestrationVisual.tsx b/src/renderer/src/components/feature-wall/agents-orchestration/AgentsOrchestrationVisual.tsx similarity index 87% rename from src/renderer/src/components/feature-wall/AgentsOrchestrationVisual.tsx rename to src/renderer/src/components/feature-wall/agents-orchestration/AgentsOrchestrationVisual.tsx index ce5d4cccde5..cc77a4b2256 100644 --- a/src/renderer/src/components/feature-wall/AgentsOrchestrationVisual.tsx +++ b/src/renderer/src/components/feature-wall/agents-orchestration/AgentsOrchestrationVisual.tsx @@ -1,9 +1,9 @@ import type { JSX } from 'react' import { cn } from '@/lib/class-names' -import type { AgentsStepId } from '../../../../shared/agents-orchestration-steps' -import { StatusesPage } from './agents-orchestration/StatusesPage' -import { UsagePage } from './agents-orchestration/UsagePage' -import { OrchestrationPage } from './agents-orchestration/OrchestrationPage' +import type { AgentsStepId } from '../../../../../shared/agents-orchestration-steps' +import { StatusesPage } from './StatusesPage' +import { UsagePage } from './UsagePage' +import { OrchestrationPage } from './OrchestrationPage' const PANEL_HEIGHT_PX = 392 const PANEL_WIDTH_PX = 520 diff --git a/src/renderer/src/components/feature-wall/agents-orchestration/UsageAccountsCard.tsx b/src/renderer/src/components/feature-wall/agents-orchestration/UsageAccountsCard.tsx index 81cf8977424..172233a8cf7 100644 --- a/src/renderer/src/components/feature-wall/agents-orchestration/UsageAccountsCard.tsx +++ b/src/renderer/src/components/feature-wall/agents-orchestration/UsageAccountsCard.tsx @@ -12,7 +12,7 @@ import type { ClaudeRateLimitAccountsState, CodexRateLimitAccountsState } from '../../../../../shared/managed-account-types' -import { getFeatureWallUsageProviderConnection } from '../feature-wall-usage-tracking' +import { getFeatureWallUsageProviderConnection } from '../completion/feature-wall-usage-tracking' import { useTranslate } from '@/i18n/use-translate' type ConnectAction = 'idle' | 'adding' diff --git a/src/renderer/src/components/feature-wall/BrowserAgentVerificationPane.tsx b/src/renderer/src/components/feature-wall/browser-storyboard/BrowserAgentVerificationPane.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/BrowserAgentVerificationPane.tsx rename to src/renderer/src/components/feature-wall/browser-storyboard/BrowserAgentVerificationPane.tsx diff --git a/src/renderer/src/components/feature-wall/BrowserAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/browser-storyboard/BrowserAnimatedVisual.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/BrowserAnimatedVisual.tsx rename to src/renderer/src/components/feature-wall/browser-storyboard/BrowserAnimatedVisual.tsx diff --git a/src/renderer/src/components/feature-wall/BrowserPricingStoryboard.tsx b/src/renderer/src/components/feature-wall/browser-storyboard/BrowserPricingStoryboard.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/BrowserPricingStoryboard.tsx rename to src/renderer/src/components/feature-wall/browser-storyboard/BrowserPricingStoryboard.tsx diff --git a/src/renderer/src/components/feature-wall/BrowserSignupStoryboard.tsx b/src/renderer/src/components/feature-wall/browser-storyboard/BrowserSignupStoryboard.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/BrowserSignupStoryboard.tsx rename to src/renderer/src/components/feature-wall/browser-storyboard/BrowserSignupStoryboard.tsx diff --git a/src/renderer/src/components/feature-wall/BrowserStoryboardWindow.tsx b/src/renderer/src/components/feature-wall/browser-storyboard/BrowserStoryboardWindow.tsx similarity index 99% rename from src/renderer/src/components/feature-wall/BrowserStoryboardWindow.tsx rename to src/renderer/src/components/feature-wall/browser-storyboard/BrowserStoryboardWindow.tsx index edb7ad40158..fa47370da49 100644 --- a/src/renderer/src/components/feature-wall/BrowserStoryboardWindow.tsx +++ b/src/renderer/src/components/feature-wall/browser-storyboard/BrowserStoryboardWindow.tsx @@ -2,7 +2,7 @@ import type { JSX, ReactNode } from 'react' import { ClaudeIcon } from '@/components/status-bar/icons' import { useTranslate } from '@/i18n/use-translate' import { cn } from '@/lib/class-names' -import { FeatureWallClickRing } from './FeatureWallClickRing' +import { FeatureWallClickRing } from '../storyboard-interaction/FeatureWallClickRing' import { BrowserPricingStoryboard } from './BrowserPricingStoryboard' import { BrowserSignupStoryboard } from './BrowserSignupStoryboard' import { diff --git a/src/renderer/src/components/feature-wall/browser-animated-visual-phase.ts b/src/renderer/src/components/feature-wall/browser-storyboard/browser-animated-visual-phase.ts similarity index 100% rename from src/renderer/src/components/feature-wall/browser-animated-visual-phase.ts rename to src/renderer/src/components/feature-wall/browser-storyboard/browser-animated-visual-phase.ts diff --git a/src/renderer/src/components/feature-wall/browser-animated-visual-sequence.ts b/src/renderer/src/components/feature-wall/browser-storyboard/browser-animated-visual-sequence.ts similarity index 100% rename from src/renderer/src/components/feature-wall/browser-animated-visual-sequence.ts rename to src/renderer/src/components/feature-wall/browser-storyboard/browser-animated-visual-sequence.ts diff --git a/src/renderer/src/components/feature-wall/use-browser-animated-visual-storyboard.ts b/src/renderer/src/components/feature-wall/browser-storyboard/use-browser-animated-visual-storyboard.ts similarity index 100% rename from src/renderer/src/components/feature-wall/use-browser-animated-visual-storyboard.ts rename to src/renderer/src/components/feature-wall/browser-storyboard/use-browser-animated-visual-storyboard.ts diff --git a/src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.discovery.test.tsx b/src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsCard.discovery.test.tsx similarity index 90% rename from src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.discovery.test.tsx rename to src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsCard.discovery.test.tsx index 961db140cea..ba93c6cae03 100644 --- a/src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.discovery.test.tsx +++ b/src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsCard.discovery.test.tsx @@ -1,12 +1,12 @@ // @vitest-environment happy-dom import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' import { expect, it } from 'vitest' -import { useAppStore } from '@/store' -import { TooltipProvider } from '../ui/tooltip' -import { persist } from '../settings/__fixtures__/settings-persistence-test-fixture' +import { useAppStore } from '@/store/index' +import { TooltipProvider } from '../../ui/tooltip' +import { persist } from '../../settings/__fixtures__/settings-persistence-test-fixture' import { AiCommitPrSettingsCard } from './AiCommitPrSettingsCard' -import * as models from '../../../../shared/source-control/ai/settings/source-control-ai-model-selection' +import * as models from '../../../../../shared/source-control/ai/settings/source-control-ai-model-selection' it.each(['omitted', 'efforts replaced'])( 'keeps accepted choices when discovery is %s', diff --git a/src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.test.tsx b/src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsCard.test.tsx similarity index 93% rename from src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.test.tsx rename to src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsCard.test.tsx index 09079e1480a..98ce621210f 100644 --- a/src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.test.tsx +++ b/src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsCard.test.tsx @@ -1,13 +1,13 @@ // @vitest-environment happy-dom import { act, fireEvent, render, renderHook, screen, waitFor } from '@testing-library/react' import { expect, it } from 'vitest' -import { useAppStore } from '@/store' -import { TooltipProvider } from '../ui/tooltip' -import { persist } from '../settings/__fixtures__/settings-persistence-test-fixture' +import { useAppStore } from '@/store/index' +import { TooltipProvider } from '../../ui/tooltip' +import { persist } from '../../settings/__fixtures__/settings-persistence-test-fixture' import { AiCommitPrSettingsCard } from './AiCommitPrSettingsCard' import { useAiCommitPrSettings } from './useAiCommitPrSettings' import { aiCommitPrPickerDraft } from './ai-commit-pr-picker' -import { applyAgentPickerEdit } from '../../../../shared/agent-launch/picker/agent-launch-picker-edits' +import { applyAgentPickerEdit } from '../../../../../shared/agent-launch/picker/agent-launch-picker-edits' it('uses the shared AI-author picker without saving on cancel and preserves Custom command', async () => { const settings = useAppStore.getState().settings! diff --git a/src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.tsx b/src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsCard.tsx similarity index 97% rename from src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.tsx rename to src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsCard.tsx index 5ca4bbf27d5..bd5be6dc91c 100644 --- a/src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.tsx +++ b/src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsCard.tsx @@ -1,5 +1,5 @@ import type { JSX } from 'react' -import { useAppStore } from '@/store' +import { useAppStore } from '@/store/index' import { useTranslate } from '@/i18n/use-translate' import { AiCommitPrSettingsFields } from './AiCommitPrSettingsFields' import { AiCommitPrSettingsSwitch } from './AiCommitPrSettingsSwitch' diff --git a/src/renderer/src/components/feature-wall/AiCommitPrSettingsFields.tsx b/src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsFields.tsx similarity index 84% rename from src/renderer/src/components/feature-wall/AiCommitPrSettingsFields.tsx rename to src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsFields.tsx index 890498af336..8c962c5f261 100644 --- a/src/renderer/src/components/feature-wall/AiCommitPrSettingsFields.tsx +++ b/src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsFields.tsx @@ -1,20 +1,20 @@ import { useMemo, type JSX } from 'react' import { Terminal } from 'lucide-react' -import { CUSTOM_PROMPT_PLACEHOLDER } from '../../../../shared/source-control/ai/generation/commit-message-prompt' +import { CUSTOM_PROMPT_PLACEHOLDER } from '../../../../../shared/source-control/ai/generation/commit-message-prompt' import { CUSTOM_AGENT_ID, type CommitMessageAgentCapability -} from '../../../../shared/source-control/ai/generation/commit-message-agent-spec' -import type { CommitMessageAiSettings } from '../../../../shared/source-control/ai/generation/commit-message-ai-types' -import { Input } from '../ui/input' -import { Label } from '../ui/label' -import { AgentLaunchPicker } from '../agent/AgentLaunchPicker' -import { DropdownMenuItem } from '../ui/dropdown-menu' +} from '../../../../../shared/source-control/ai/generation/commit-message-agent-spec' +import type { CommitMessageAiSettings } from '../../../../../shared/source-control/ai/generation/commit-message-ai-types' +import { Input } from '../../ui/input' +import { Label } from '../../ui/label' +import { AgentLaunchPicker } from '../../agent/AgentLaunchPicker' +import { DropdownMenuItem } from '../../ui/dropdown-menu' import type { AgentPickerDraft, AgentPickerEntry -} from '../../../../shared/agent-launch/picker/agent-launch-picker' -import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent' +} from '../../../../../shared/agent-launch/picker/agent-launch-picker' +import type { TuiAgent } from '../../../../../shared/agent-launch/catalog/tui-agent' import { aiCommitPrPickerDraft, commitMessagePickerModel } from './ai-commit-pr-picker' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/AiCommitPrSettingsSwitch.tsx b/src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsSwitch.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/AiCommitPrSettingsSwitch.tsx rename to src/renderer/src/components/feature-wall/commit-pr-settings/AiCommitPrSettingsSwitch.tsx diff --git a/src/renderer/src/components/feature-wall/ai-commit-pr-picker.ts b/src/renderer/src/components/feature-wall/commit-pr-settings/ai-commit-pr-picker.ts similarity index 82% rename from src/renderer/src/components/feature-wall/ai-commit-pr-picker.ts rename to src/renderer/src/components/feature-wall/commit-pr-settings/ai-commit-pr-picker.ts index 34b91593741..77f487ff014 100644 --- a/src/renderer/src/components/feature-wall/ai-commit-pr-picker.ts +++ b/src/renderer/src/components/feature-wall/commit-pr-settings/ai-commit-pr-picker.ts @@ -1,17 +1,17 @@ -import type { NewTabAgentMenuModel } from '../tab-bar/new-tab-agent-menu-model' +import type { NewTabAgentMenuModel } from '../../tab-bar/new-tab-agent-menu-model' import { sourceControlModelLabel, sourceControlThinkingLabel } from './source-control-model-label' -import type { CommitMessageAiSettings } from '../../../../shared/source-control/ai/generation/commit-message-ai-types' -import type { CommitMessageAgentCapability } from '../../../../shared/source-control/ai/generation/commit-message-agent-spec' +import type { CommitMessageAiSettings } from '../../../../../shared/source-control/ai/generation/commit-message-ai-types' +import type { CommitMessageAgentCapability } from '../../../../../shared/source-control/ai/generation/commit-message-agent-spec' import { agentPickerDraft, type AgentPickerEntry -} from '../../../../shared/agent-launch/picker/agent-launch-picker' +} from '../../../../../shared/agent-launch/picker/agent-launch-picker' import { commitMessageAgentLabel, resolveCommitMessageSelectedModel, resolveCommitMessageSelectedThinking -} from './ai-commit-pr-settings-helpers' -import type { Translate } from '../../i18n/use-translate' +} from './commit-message-settings-selection' +import type { Translate } from '../../../i18n/use-translate' export function aiCommitPrPickerEntry( capability: CommitMessageAgentCapability, diff --git a/src/renderer/src/components/feature-wall/ai-commit-pr-settings-helpers.ts b/src/renderer/src/components/feature-wall/commit-pr-settings/commit-message-settings-selection.ts similarity index 88% rename from src/renderer/src/components/feature-wall/ai-commit-pr-settings-helpers.ts rename to src/renderer/src/components/feature-wall/commit-pr-settings/commit-message-settings-selection.ts index 53e37a5f95d..f3aca307baf 100644 --- a/src/renderer/src/components/feature-wall/ai-commit-pr-settings-helpers.ts +++ b/src/renderer/src/components/feature-wall/commit-pr-settings/commit-message-settings-selection.ts @@ -1,13 +1,13 @@ -import type { CommitMessageAiSettings } from '../../../../shared/source-control/ai/generation/commit-message-ai-types' -import type { GlobalSettings } from '../../../../shared/global-settings-types' -import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent' +import type { CommitMessageAiSettings } from '../../../../../shared/source-control/ai/generation/commit-message-ai-types' +import type { GlobalSettings } from '../../../../../shared/global-settings-types' +import type { TuiAgent } from '../../../../../shared/agent-launch/catalog/tui-agent' import { getCommitMessageAgentCapability, isCustomAgentId, type CommitMessageAgentCapability, type CommitMessageAgentChoice, type CommitMessageModelCapability -} from '../../../../shared/source-control/ai/generation/commit-message-agent-spec' +} from '../../../../../shared/source-control/ai/generation/commit-message-agent-spec' import { getAgentCatalog } from '@/lib/agent-catalog/agent-catalog' export const EMPTY_COMMIT_MESSAGE_AI_SETTINGS: CommitMessageAiSettings = { diff --git a/src/renderer/src/components/feature-wall/source-control-model-label.ts b/src/renderer/src/components/feature-wall/commit-pr-settings/source-control-model-label.ts similarity index 100% rename from src/renderer/src/components/feature-wall/source-control-model-label.ts rename to src/renderer/src/components/feature-wall/commit-pr-settings/source-control-model-label.ts diff --git a/src/renderer/src/components/feature-wall/useAiCommitPrSettings.ts b/src/renderer/src/components/feature-wall/commit-pr-settings/useAiCommitPrSettings.ts similarity index 90% rename from src/renderer/src/components/feature-wall/useAiCommitPrSettings.ts rename to src/renderer/src/components/feature-wall/commit-pr-settings/useAiCommitPrSettings.ts index 719167987c6..e83c096d22c 100644 --- a/src/renderer/src/components/feature-wall/useAiCommitPrSettings.ts +++ b/src/renderer/src/components/feature-wall/commit-pr-settings/useAiCommitPrSettings.ts @@ -1,23 +1,23 @@ import { useCallback, useMemo, useState } from 'react' -import type { CommitMessageAiSettings } from '../../../../shared/source-control/ai/generation/commit-message-ai-types' +import type { CommitMessageAiSettings } from '../../../../../shared/source-control/ai/generation/commit-message-ai-types' import { CUSTOM_AGENT_ID, getCommitMessageAgentCapability, isCustomAgentId, listCommitMessageAgentCapabilities, resolveCommitMessageAgentChoice -} from '../../../../shared/source-control/ai/generation/commit-message-agent-spec' -import { LOCAL_COMMIT_MESSAGE_HOST_KEY } from '../../../../shared/source-control/ai/generation/commit-message-host-key' -import { getDefaultSourceControlAiSettings } from '../../../../shared/source-control/ai/settings/source-control-ai-settings' -import { getTextGenerationModels } from '../../../../shared/source-control/ai/settings/source-control-ai-model-selection' -import { isTuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent-config' +} from '../../../../../shared/source-control/ai/generation/commit-message-agent-spec' +import { LOCAL_COMMIT_MESSAGE_HOST_KEY } from '../../../../../shared/source-control/ai/generation/commit-message-host-key' +import { getDefaultSourceControlAiSettings } from '../../../../../shared/source-control/ai/settings/source-control-ai-settings' +import { getTextGenerationModels } from '../../../../../shared/source-control/ai/settings/source-control-ai-model-selection' +import { isTuiAgent } from '../../../../../shared/agent-launch/catalog/tui-agent-config' import { getAgentCatalog } from '@/lib/agent-catalog/agent-catalog' import { useAppStore } from '@/store/index' import type { AgentPickerDraft, AgentPickerEntry -} from '../../../../shared/agent-launch/picker/agent-launch-picker' -import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent' +} from '../../../../../shared/agent-launch/picker/agent-launch-picker' +import type { TuiAgent } from '../../../../../shared/agent-launch/catalog/tui-agent' import { aiCommitPrPickerEntry } from './ai-commit-pr-picker' import { EMPTY_COMMIT_MESSAGE_AI_SETTINGS, @@ -25,7 +25,7 @@ import { resolveCommitMessageSelectedModel, resolveCommitMessageSelectedThinking, seedCommitMessageAiEnablePatch -} from './ai-commit-pr-settings-helpers' +} from './commit-message-settings-selection' export type AiCommitPrSettingsViewModel = { config: CommitMessageAiSettings diff --git a/src/renderer/src/components/feature-wall/feature-wall-completion-persistence.ts b/src/renderer/src/components/feature-wall/completion/feature-wall-completion-persistence.ts similarity index 96% rename from src/renderer/src/components/feature-wall/feature-wall-completion-persistence.ts rename to src/renderer/src/components/feature-wall/completion/feature-wall-completion-persistence.ts index 95cd8be7683..5bda1b2dda2 100644 --- a/src/renderer/src/components/feature-wall/feature-wall-completion-persistence.ts +++ b/src/renderer/src/components/feature-wall/completion/feature-wall-completion-persistence.ts @@ -1,10 +1,10 @@ -import type { AgentsStepId } from '../../../../shared/agents-orchestration-steps' +import type { AgentsStepId } from '../../../../../shared/agents-orchestration-steps' import { FEATURE_WALL_WORKFLOW_IDS, type FeatureWallWorkflowId -} from '../../../../shared/feature-wall-workflows' -import type { ReviewStepId } from '../../../../shared/review-steps' -import type { WorkbenchStepId } from '../../../../shared/workbench-steps' +} from '../../../../../shared/feature-wall-workflows' +import type { ReviewStepId } from '../../../../../shared/review-steps' +import type { WorkbenchStepId } from '../../../../../shared/workbench-steps' const PERSISTED_WORKFLOW_IDS = new Set(FEATURE_WALL_WORKFLOW_IDS) const VISITED_WORKFLOWS_STORAGE_KEY = 'orca.featureWall.visitedWorkflows.v1' diff --git a/src/renderer/src/components/feature-wall/feature-wall-completion-progress.ts b/src/renderer/src/components/feature-wall/completion/feature-wall-completion-progress.ts similarity index 93% rename from src/renderer/src/components/feature-wall/feature-wall-completion-progress.ts rename to src/renderer/src/components/feature-wall/completion/feature-wall-completion-progress.ts index 579d08dc022..ac979b4d0c9 100644 --- a/src/renderer/src/components/feature-wall/feature-wall-completion-progress.ts +++ b/src/renderer/src/components/feature-wall/completion/feature-wall-completion-progress.ts @@ -1,7 +1,7 @@ -import type { AgentsStepId } from '../../../../shared/agents-orchestration-steps' -import type { FeatureWallWorkflowId } from '../../../../shared/feature-wall-workflows' -import type { ReviewStepId } from '../../../../shared/review-steps' -import type { WorkbenchStepId } from '../../../../shared/workbench-steps' +import type { AgentsStepId } from '../../../../../shared/agents-orchestration-steps' +import type { FeatureWallWorkflowId } from '../../../../../shared/feature-wall-workflows' +import type { ReviewStepId } from '../../../../../shared/review-steps' +import type { WorkbenchStepId } from '../../../../../shared/workbench-steps' export const FEATURE_WALL_AGENT_STEP_IDS: readonly AgentsStepId[] = [ 'statuses', diff --git a/src/renderer/src/components/feature-wall/feature-wall-usage-tracking.ts b/src/renderer/src/components/feature-wall/completion/feature-wall-usage-tracking.ts similarity index 96% rename from src/renderer/src/components/feature-wall/feature-wall-usage-tracking.ts rename to src/renderer/src/components/feature-wall/completion/feature-wall-usage-tracking.ts index 1ddfea6a43d..3e3a960f249 100644 --- a/src/renderer/src/components/feature-wall/feature-wall-usage-tracking.ts +++ b/src/renderer/src/components/feature-wall/completion/feature-wall-usage-tracking.ts @@ -1,4 +1,4 @@ -import type { ProviderRateLimits } from '../../../../shared/rate-limit-types' +import type { ProviderRateLimits } from '../../../../../shared/rate-limit-types' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/use-feature-wall-completion.ts b/src/renderer/src/components/feature-wall/completion/use-feature-wall-completion.ts similarity index 94% rename from src/renderer/src/components/feature-wall/use-feature-wall-completion.ts rename to src/renderer/src/components/feature-wall/completion/use-feature-wall-completion.ts index f09ab66eb8d..6fd881b5861 100644 --- a/src/renderer/src/components/feature-wall/use-feature-wall-completion.ts +++ b/src/renderer/src/components/feature-wall/completion/use-feature-wall-completion.ts @@ -1,15 +1,15 @@ import { useCallback, useEffect, useMemo, useState } from 'react' -import type { FeatureWallWorkflowId } from '../../../../shared/feature-wall-workflows' -import type { AgentsStepId } from '../../../../shared/agents-orchestration-steps' -import type { WorkbenchStepId } from '../../../../shared/workbench-steps' -import type { ReviewStepId } from '../../../../shared/review-steps' -import type { FeatureWallTourDepthSummary } from '../../../../shared/feature-wall-tour-depth' +import type { FeatureWallWorkflowId } from '../../../../../shared/feature-wall-workflows' +import type { AgentsStepId } from '../../../../../shared/agents-orchestration-steps' +import type { WorkbenchStepId } from '../../../../../shared/workbench-steps' +import type { ReviewStepId } from '../../../../../shared/review-steps' +import type { FeatureWallTourDepthSummary } from '../../../../../shared/feature-wall-tour-depth' import { getCommitMessageAgentCapability, isCustomAgentId, resolveCommitMessageAgentChoice -} from '../../../../shared/source-control/ai/generation/commit-message-agent-spec' -import { useAppStore } from '@/store' +} from '../../../../../shared/source-control/ai/generation/commit-message-agent-spec' +import { useAppStore } from '@/store/index' import { FEATURE_WALL_AGENT_STEP_IDS, FEATURE_WALL_REVIEW_STEP_IDS, diff --git a/src/renderer/src/components/feature-wall/use-feature-wall-session-depth.ts b/src/renderer/src/components/feature-wall/completion/use-feature-wall-session-depth.ts similarity index 90% rename from src/renderer/src/components/feature-wall/use-feature-wall-session-depth.ts rename to src/renderer/src/components/feature-wall/completion/use-feature-wall-session-depth.ts index fdab4862b8f..b210988d44a 100644 --- a/src/renderer/src/components/feature-wall/use-feature-wall-session-depth.ts +++ b/src/renderer/src/components/feature-wall/completion/use-feature-wall-session-depth.ts @@ -1,10 +1,10 @@ import { useCallback, useEffect, useRef } from 'react' -import type { AgentsStepId } from '../../../../shared/agents-orchestration-steps' -import type { FeatureWallWorkflowId } from '../../../../shared/feature-wall-workflows' -import type { FeatureWallTourDepthSummary } from '../../../../shared/feature-wall-tour-depth' -import { buildFeatureWallTourDepthSummary } from '../../../../shared/feature-wall-tour-depth' -import type { ReviewStepId } from '../../../../shared/review-steps' -import type { WorkbenchStepId } from '../../../../shared/workbench-steps' +import type { AgentsStepId } from '../../../../../shared/agents-orchestration-steps' +import type { FeatureWallWorkflowId } from '../../../../../shared/feature-wall-workflows' +import type { FeatureWallTourDepthSummary } from '../../../../../shared/feature-wall-tour-depth' +import { buildFeatureWallTourDepthSummary } from '../../../../../shared/feature-wall-tour-depth' +import type { ReviewStepId } from '../../../../../shared/review-steps' +import type { WorkbenchStepId } from '../../../../../shared/workbench-steps' import { getFeatureWallCompletionProgress } from './feature-wall-completion-progress' type FeatureWallSessionDepthInput = { diff --git a/src/renderer/src/components/feature-wall/use-persisted-feature-wall-completion.ts b/src/renderer/src/components/feature-wall/completion/use-persisted-feature-wall-completion.ts similarity index 93% rename from src/renderer/src/components/feature-wall/use-persisted-feature-wall-completion.ts rename to src/renderer/src/components/feature-wall/completion/use-persisted-feature-wall-completion.ts index f234aa4bda0..0892fc79117 100644 --- a/src/renderer/src/components/feature-wall/use-persisted-feature-wall-completion.ts +++ b/src/renderer/src/components/feature-wall/completion/use-persisted-feature-wall-completion.ts @@ -1,9 +1,9 @@ import { useCallback, useState } from 'react' import type { Dispatch, SetStateAction } from 'react' -import type { AgentsStepId } from '../../../../shared/agents-orchestration-steps' -import type { FeatureWallWorkflowId } from '../../../../shared/feature-wall-workflows' -import type { ReviewStepId } from '../../../../shared/review-steps' -import type { WorkbenchStepId } from '../../../../shared/workbench-steps' +import type { AgentsStepId } from '../../../../../shared/agents-orchestration-steps' +import type { FeatureWallWorkflowId } from '../../../../../shared/feature-wall-workflows' +import type { ReviewStepId } from '../../../../../shared/review-steps' +import type { WorkbenchStepId } from '../../../../../shared/workbench-steps' import { persistCompletedAgentStep, persistCompletedReviewStep, diff --git a/src/renderer/src/components/feature-wall/EditorAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/editor-storyboard/EditorAnimatedVisual.tsx similarity index 99% rename from src/renderer/src/components/feature-wall/EditorAnimatedVisual.tsx rename to src/renderer/src/components/feature-wall/editor-storyboard/EditorAnimatedVisual.tsx index fb07bab2664..151082f4d52 100644 --- a/src/renderer/src/components/feature-wall/EditorAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/editor-storyboard/EditorAnimatedVisual.tsx @@ -1,7 +1,7 @@ import { useRef } from 'react' import type { JSX, ReactNode } from 'react' import { cn } from '@/lib/class-names' -import { getShortcutPlatform } from '@/hooks/useShortcutLabel' +import { getShortcutPlatform } from '@/lib/keyboard/shortcut-platform' import { useTranslate } from '@/i18n/use-translate' import { TB_ICON, ToolbarBtn, ToolbarSep } from './editor-animated-toolbar-icons' import { activeLineClass, caretClass } from './editor-animated-visual-markup' diff --git a/src/renderer/src/components/feature-wall/editor-animated-toolbar-icons.tsx b/src/renderer/src/components/feature-wall/editor-storyboard/editor-animated-toolbar-icons.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/editor-animated-toolbar-icons.tsx rename to src/renderer/src/components/feature-wall/editor-storyboard/editor-animated-toolbar-icons.tsx diff --git a/src/renderer/src/components/feature-wall/editor-animated-visual-markup.ts b/src/renderer/src/components/feature-wall/editor-storyboard/editor-animated-visual-markup.ts similarity index 100% rename from src/renderer/src/components/feature-wall/editor-animated-visual-markup.ts rename to src/renderer/src/components/feature-wall/editor-storyboard/editor-animated-visual-markup.ts diff --git a/src/renderer/src/components/feature-wall/use-editor-animated-visual-animation.ts b/src/renderer/src/components/feature-wall/editor-storyboard/use-editor-animated-visual-animation.ts similarity index 100% rename from src/renderer/src/components/feature-wall/use-editor-animated-visual-animation.ts rename to src/renderer/src/components/feature-wall/editor-storyboard/use-editor-animated-visual-animation.ts diff --git a/src/renderer/src/components/feature-wall/ReviewAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/review-storyboard/ReviewAnimatedVisual.tsx similarity index 90% rename from src/renderer/src/components/feature-wall/ReviewAnimatedVisual.tsx rename to src/renderer/src/components/feature-wall/review-storyboard/ReviewAnimatedVisual.tsx index b9f70efbb06..02e4d8f7ad0 100644 --- a/src/renderer/src/components/feature-wall/ReviewAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/review-storyboard/ReviewAnimatedVisual.tsx @@ -1,6 +1,6 @@ import type { JSX } from 'react' -import type { ReviewStepId } from '../../../../shared/review-steps' -import { PANEL_HEIGHT, PANEL_WIDTH } from './review-animated-visual-shared' +import type { ReviewStepId } from '../../../../../shared/review-steps' +import { PANEL_HEIGHT, PANEL_WIDTH } from './review-migration-scenario' import { ReviewNotesAnimatedVisual } from './ReviewNotesAnimatedVisual' import { ReviewPRViewAnimatedVisual } from './ReviewPRViewAnimatedVisual' import { ReviewShipAnimatedVisual } from './ReviewShipAnimatedVisual' diff --git a/src/renderer/src/components/feature-wall/ReviewNotesAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/review-storyboard/ReviewNotesAnimatedVisual.tsx similarity index 99% rename from src/renderer/src/components/feature-wall/ReviewNotesAnimatedVisual.tsx rename to src/renderer/src/components/feature-wall/review-storyboard/ReviewNotesAnimatedVisual.tsx index dd6dbbe89c2..67222c5be45 100644 --- a/src/renderer/src/components/feature-wall/ReviewNotesAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/review-storyboard/ReviewNotesAnimatedVisual.tsx @@ -6,10 +6,10 @@ import { CornerEnterIcon, CursorIcon, MessageIcon, - NOTE_TARGETS, PlusIcon, SendIcon -} from './review-animated-visual-shared' +} from '../storyboard-interaction/storyboard-glyphs' +import { NOTE_TARGETS } from './review-migration-scenario' import { ReviewNotesVisualStyles } from './review-animated-visual-notes-styles' import { ReviewDiffRows } from './review-notes-diff-rows' import { resetTerminal, runTerminalPhase } from './review-notes-terminal-phase' diff --git a/src/renderer/src/components/feature-wall/ReviewPRViewAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/review-storyboard/ReviewPRViewAnimatedVisual.tsx similarity index 98% rename from src/renderer/src/components/feature-wall/ReviewPRViewAnimatedVisual.tsx rename to src/renderer/src/components/feature-wall/review-storyboard/ReviewPRViewAnimatedVisual.tsx index 128ba09ac3f..5cdabe57ea6 100644 --- a/src/renderer/src/components/feature-wall/ReviewPRViewAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/review-storyboard/ReviewPRViewAnimatedVisual.tsx @@ -4,7 +4,11 @@ import { Files, GitBranch, ListChecks, MessageSquare, Search } from 'lucide-reac import { useShortcutLabel } from '@/hooks/useShortcutLabel' import { isUnassignedShortcutLabel } from '@/i18n/unassigned-label' import { ReviewPRViewVisualStyles } from './review-animated-visual-pr-view-styles' -import { CheckTinyIcon, ChevDownIcon, CursorIcon } from './review-animated-visual-shared' +import { + CheckTinyIcon, + ChevDownIcon, + CursorIcon +} from '../storyboard-interaction/storyboard-glyphs' import { useReviewPrViewAnimation } from './review-pr-view-animation' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/ReviewShipAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/review-storyboard/ReviewShipAnimatedVisual.tsx similarity index 98% rename from src/renderer/src/components/feature-wall/ReviewShipAnimatedVisual.tsx rename to src/renderer/src/components/feature-wall/review-storyboard/ReviewShipAnimatedVisual.tsx index ea4a3e02dad..6833cb185ed 100644 --- a/src/renderer/src/components/feature-wall/ReviewShipAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/review-storyboard/ReviewShipAnimatedVisual.tsx @@ -5,13 +5,10 @@ import { ArrowUpIcon, CheckTinyIcon, ChevDownIcon, - COMMIT_MSG, CursorIcon, - FileIcon, - PR_BODY, - PR_TITLE, - SHIP_FILES -} from './review-animated-visual-shared' + FileIcon +} from '../storyboard-interaction/storyboard-glyphs' +import { COMMIT_MSG, PR_BODY, PR_TITLE, SHIP_FILES } from './review-migration-scenario' import { ReviewShipVisualStyles } from './review-animated-visual-ship-styles' import { useTranslate } from '@/i18n/use-translate' import { forceLayoutReflow } from '@/lib/overlay-interaction/force-layout-reflow' diff --git a/src/renderer/src/components/feature-wall/review-animated-visual-notes-styles.tsx b/src/renderer/src/components/feature-wall/review-storyboard/review-animated-visual-notes-styles.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/review-animated-visual-notes-styles.tsx rename to src/renderer/src/components/feature-wall/review-storyboard/review-animated-visual-notes-styles.tsx diff --git a/src/renderer/src/components/feature-wall/review-animated-visual-pr-view-styles.tsx b/src/renderer/src/components/feature-wall/review-storyboard/review-animated-visual-pr-view-styles.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/review-animated-visual-pr-view-styles.tsx rename to src/renderer/src/components/feature-wall/review-storyboard/review-animated-visual-pr-view-styles.tsx diff --git a/src/renderer/src/components/feature-wall/review-animated-visual-ship-styles.tsx b/src/renderer/src/components/feature-wall/review-storyboard/review-animated-visual-ship-styles.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/review-animated-visual-ship-styles.tsx rename to src/renderer/src/components/feature-wall/review-storyboard/review-animated-visual-ship-styles.tsx diff --git a/src/renderer/src/components/feature-wall/review-storyboard/review-diff-tokenizer.ts b/src/renderer/src/components/feature-wall/review-storyboard/review-diff-tokenizer.ts new file mode 100644 index 00000000000..a737ee3880a --- /dev/null +++ b/src/renderer/src/components/feature-wall/review-storyboard/review-diff-tokenizer.ts @@ -0,0 +1,10 @@ +export function tokenize(line: string): string { + const KW = + /\b(?:const|let|await|function|return|if|else|for|of|in|try|catch|throw|new|export|import|from)\b/g + const STR = /(`[^`]*`|"[^"]*"|'[^']*')/g + let html = line.replace(/&/g, '&').replace(//g, '>') + html = html.replace(STR, (m) => `${m}`) + html = html.replace(KW, (m) => `${m}`) + html = html.replace(/(\b[A-Za-z_]\w*\b)(?=\()/g, (m) => `${m}`) + return html +} diff --git a/src/renderer/src/components/feature-wall/review-storyboard/review-migration-scenario.ts b/src/renderer/src/components/feature-wall/review-storyboard/review-migration-scenario.ts new file mode 100644 index 00000000000..91088c46d40 --- /dev/null +++ b/src/renderer/src/components/feature-wall/review-storyboard/review-migration-scenario.ts @@ -0,0 +1,63 @@ +export type DiffLine = { kind: 'ctx' | 'add' | 'rem'; t: string } +export type Hunk = { + header: string + oldStart: number + newStart: number + lines: DiffLine[] +} + +export const HUNKS: readonly Hunk[] = [ + { + header: '@@ -42,5 +42,7 @@ export function applyMigration(db, version) {', + oldStart: 42, + newStart: 42, + lines: [ + { kind: 'ctx', t: ' const ctx = beginTx(db)' }, + { kind: 'rem', t: ' runStep(ctx, "schema", version)' }, + { kind: 'add', t: ' await runStep(ctx, "schema", version)' }, + { kind: 'add', t: ' await runStep(ctx, "backfill", version)' }, + { kind: 'ctx', t: ' commit(ctx)' } + ] + }, + { + header: '@@ -88,3 +90,4 @@ function backfillUsers(rows) {', + oldStart: 88, + newStart: 90, + lines: [ + { kind: 'ctx', t: ' for (const row of rows) {' }, + { kind: 'rem', t: ' db.exec(sql, row)' }, + { kind: 'add', t: ' if (!row.tier) continue' }, + { kind: 'add', t: ' db.exec(sql, [row.tier, row.id])' } + ] + } +] + +export const NOTE_TARGETS: readonly { + hunk: number + lineIdx: number + body: string + summary: string +}[] = [ + { + hunk: 0, + lineIdx: 2, + body: 'Backfill must run before commit if schema assumes new columns.', + summary: 'Sequence schema → backfill before commit' + }, + { + hunk: 1, + lineIdx: 2, + body: 'Silently skipping rows — log the count or surface it in the result.', + summary: 'Log skipped rows in result' + } +] + +export const COMMIT_MSG = 'Run schema + backfill steps in order during migration' +export const PR_TITLE = 'Run schema + backfill in order during migration' +export const PR_BODY = + 'Awaits schema before backfill so commits stay in order. Skips rows with no tier on reruns.' + +export const SHIP_FILES: readonly string[] = ['migrate.ts', 'backfill-users.ts', 'migrate.test.ts'] + +export const PANEL_WIDTH = 480 +export const PANEL_HEIGHT = 416 diff --git a/src/renderer/src/components/feature-wall/review-notes-diff-rows.tsx b/src/renderer/src/components/feature-wall/review-storyboard/review-notes-diff-rows.tsx similarity index 96% rename from src/renderer/src/components/feature-wall/review-notes-diff-rows.tsx rename to src/renderer/src/components/feature-wall/review-storyboard/review-notes-diff-rows.tsx index 64de795ee35..65be453e2d3 100644 --- a/src/renderer/src/components/feature-wall/review-notes-diff-rows.tsx +++ b/src/renderer/src/components/feature-wall/review-storyboard/review-notes-diff-rows.tsx @@ -1,6 +1,7 @@ import type { JSX } from 'react' import { cn } from '@/lib/class-names' -import { HUNKS, tokenize, type DiffLine } from './review-animated-visual-shared' +import { HUNKS, type DiffLine } from './review-migration-scenario' +import { tokenize } from './review-diff-tokenizer' import { useTranslate } from '@/i18n/use-translate' // Precompute row geometry so the animation mutates styles rather than DOM structure. diff --git a/src/renderer/src/components/feature-wall/review-notes-terminal-phase.ts b/src/renderer/src/components/feature-wall/review-storyboard/review-notes-terminal-phase.ts similarity index 97% rename from src/renderer/src/components/feature-wall/review-notes-terminal-phase.ts rename to src/renderer/src/components/feature-wall/review-storyboard/review-notes-terminal-phase.ts index f6a74daf966..6882e4f2243 100644 --- a/src/renderer/src/components/feature-wall/review-notes-terminal-phase.ts +++ b/src/renderer/src/components/feature-wall/review-storyboard/review-notes-terminal-phase.ts @@ -1,4 +1,4 @@ -import { NOTE_TARGETS } from './review-animated-visual-shared' +import { NOTE_TARGETS } from './review-migration-scenario' export type TerminalPhaseContext = { term: HTMLDivElement diff --git a/src/renderer/src/components/feature-wall/review-pr-view-animation.ts b/src/renderer/src/components/feature-wall/review-storyboard/review-pr-view-animation.ts similarity index 100% rename from src/renderer/src/components/feature-wall/review-pr-view-animation.ts rename to src/renderer/src/components/feature-wall/review-storyboard/review-pr-view-animation.ts diff --git a/src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx b/src/renderer/src/components/feature-wall/setup/FeatureWallSetupChecklist.tsx similarity index 92% rename from src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx rename to src/renderer/src/components/feature-wall/setup/FeatureWallSetupChecklist.tsx index 6608bd8d08b..bd0883fe45c 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallSetupChecklist.tsx +++ b/src/renderer/src/components/feature-wall/setup/FeatureWallSetupChecklist.tsx @@ -3,35 +3,35 @@ import { Check } from 'lucide-react' import type { FeatureWallSetupStep, FeatureWallSetupStepId -} from '../../../../shared/feature-wall-setup-steps' -import { getFeatureWallSetupStepsForSection } from '../../../../shared/feature-wall-setup-steps' +} from '../../../../../shared/feature-wall-setup-steps' +import { getFeatureWallSetupStepsForSection } from '../../../../../shared/feature-wall-setup-steps' import { cn } from '@/lib/class-names' import { useScrollEdgeFades } from '@/lib/list-interaction/use-scroll-edge-fades' import type { FeatureWallSetupProgress } from './feature-wall-setup-progress' import { FullDiskAccessSetupPrompt } from './FullDiskAccessSetupPrompt' -import { AgentCapabilitiesSetupAction } from './AgentCapabilitiesSetupAction' +import { AgentCapabilitiesSetupAction } from './agent-capabilities/AgentCapabilitiesSetupAction' import { AddReposAction, SetupScriptAction, WorkspacesAction } from './FeatureWallSetupWorkflowActions' -import { ConnectIntegrationsList } from './ConnectIntegrationsList' -import { BrowserAction } from './FeatureWallBrowserAction' +import { ConnectIntegrationsList } from './integrations/ConnectIntegrationsList' +import { BrowserAction } from './browser/FeatureWallBrowserAction' import { SetupBrowserVisual, SetupMultipleReposVisual, SetupWorkspacesVisual } from './FeatureWallSetupStepVisuals' -import { AgentStep } from '../onboarding/AgentStep' -import { useChatAgentDetection } from '../onboarding/use-onboarding-agent-surface' -import type { AgentSurface } from '../onboarding/use-onboarding-flow-types' -import { agentSurfaceSettingsUpdate } from '../onboarding/onboarding-flow-state' -import { onboardingChatPermissionSetting } from '../onboarding/use-onboarding-flow-persistence' -import { savedYoloPermissions } from '../onboarding/use-onboarding-yolo-permissions' -import { applyAllAgentsPermission } from '../../../../shared/agent-launch/permissions/tui-agent-permission-choice' -import { NotificationStep } from '../onboarding/NotificationStep' +import { AgentStep } from '../../onboarding/AgentStep' +import { useChatAgentDetection } from '../../onboarding/use-onboarding-agent-surface' +import type { AgentSurface } from '../../onboarding/use-onboarding-flow-types' +import { agentSurfaceSettingsUpdate } from '../../onboarding/onboarding-flow-state' +import { onboardingChatPermissionSetting } from '../../onboarding/use-onboarding-flow-persistence' +import { savedYoloPermissions } from '../../onboarding/use-onboarding-yolo-permissions' +import { applyAllAgentsPermission } from '../../../../../shared/agent-launch/permissions/tui-agent-permission-choice' +import { NotificationStep } from '../../onboarding/NotificationStep' import { useAppStore } from '@/store/index' -import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent' +import type { TuiAgent } from '../../../../../shared/agent-launch/catalog/tui-agent' import { getProviderRuntimeContextKey } from '@/runtime/hosts/observations/provider-runtime-context' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/FeatureWallSetupStepVisuals.tsx b/src/renderer/src/components/feature-wall/setup/FeatureWallSetupStepVisuals.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/FeatureWallSetupStepVisuals.tsx rename to src/renderer/src/components/feature-wall/setup/FeatureWallSetupStepVisuals.tsx diff --git a/src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.tsx b/src/renderer/src/components/feature-wall/setup/FeatureWallSetupWorkflowActions.tsx similarity index 93% rename from src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.tsx rename to src/renderer/src/components/feature-wall/setup/FeatureWallSetupWorkflowActions.tsx index 303d663d78f..9fcd1f3c314 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.tsx +++ b/src/renderer/src/components/feature-wall/setup/FeatureWallSetupWorkflowActions.tsx @@ -3,18 +3,18 @@ import { ArrowUpRight, Plus, Save, Settings } from 'lucide-react' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' -import { useAppStore } from '@/store' +import { useAppStore } from '@/store/index' import { useAllWorktrees } from '@/store/slices/worktrees/listing/use-worktree-catalog' -import { getDefaultRepoHookSettings } from '../../../../shared/constants' -import { isGitRepoKind } from '../../../../shared/repo-kind' -import type { RepoHookSettings } from '../../../../shared/orca-yaml-hook-types' -import type { Repo } from '../../../../shared/repo-types' -import type { Worktree } from '../../../../shared/worktree/types' -import { getRepositoryLocalCommandsSectionId } from '../settings/repository/repository-settings-targets' +import { getDefaultRepoHookSettings } from '../../../../../shared/constants' +import { isGitRepoKind } from '../../../../../shared/repo-kind' +import type { RepoHookSettings } from '../../../../../shared/orca-yaml-hook-types' +import type { Repo } from '../../../../../shared/repo-types' +import type { Worktree } from '../../../../../shared/worktree/types' +import { getRepositoryLocalCommandsSectionId } from '../../settings/repository/repository-settings-targets' import { requestContextualTourWhenReady, type RequestContextualTourWhenReadyArgs -} from '../contextual-tours/request-contextual-tour-when-ready' +} from '../../contextual-tours/request-contextual-tour-when-ready' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.test.ts b/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts similarity index 98% rename from src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.test.ts rename to src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts index 7bf6fb2d045..cf98f6efee3 100644 --- a/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.test.ts +++ b/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts @@ -7,7 +7,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import type { DeveloperPermissionRequestResult, DeveloperPermissionState -} from '../../../../shared/developer-permissions-types' +} from '../../../../../shared/developer-permissions-types' import { FullDiskAccessSetupPrompt } from './FullDiskAccessSetupPrompt' vi.mock('sonner', () => ({ diff --git a/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.tsx b/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.tsx similarity index 97% rename from src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.tsx rename to src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.tsx index 568aaaa1cc5..e2bc52f7f85 100644 --- a/src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.tsx +++ b/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.tsx @@ -5,11 +5,11 @@ import type { DeveloperPermissionId, DeveloperPermissionState, DeveloperPermissionStatus -} from '../../../../shared/developer-permissions-types' +} from '../../../../../shared/developer-permissions-types' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { useMountedRef } from '@/hooks/useMountedRef' -import { isMacUserAgent } from '../../app-shell/platform/user-agent-platform' +import { isMacUserAgent } from '../../../app-shell/platform/user-agent-platform' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.tsx b/src/renderer/src/components/feature-wall/setup/agent-capabilities/AgentCapabilitiesSetupAction.tsx similarity index 96% rename from src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.tsx rename to src/renderer/src/components/feature-wall/setup/agent-capabilities/AgentCapabilitiesSetupAction.tsx index 96386c505f8..a36cdf1f5a0 100644 --- a/src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.tsx +++ b/src/renderer/src/components/feature-wall/setup/agent-capabilities/AgentCapabilitiesSetupAction.tsx @@ -2,12 +2,12 @@ import { useBundledSkillInstallAction, type BundledSkillInstallActionState } from '@/hooks/skills/installation/use-bundled-skill-install-action' -import { bundledSkillSetupActionLabel } from '../skills/setup/bundled-skill-setup-copy' +import { bundledSkillSetupActionLabel } from '../../../skills/setup/bundled-skill-setup-copy' import { useOnboardingSkillRuntime } from '@/hooks/skills/runtime/use-onboarding-skill-runtime' -import { FeatureSetupInlineTerminal } from '../onboarding/FeatureSetupInlineTerminal' +import { FeatureSetupInlineTerminal } from '../../../onboarding/FeatureSetupInlineTerminal' import { buildAgentFeatureSkillInstallCommand } from '@/lib/skills/installation/agent-feature-install-commands' -import { buildSkillCommandForRuntime } from '../../lib/skills/runtime/skill-runtime-command' -import type { OnboardingFeatureSetupRuntimeContext } from '../onboarding/onboarding-feature-setup-runtime' +import { buildSkillCommandForRuntime } from '../../../../lib/skills/runtime/skill-runtime-command' +import type { OnboardingFeatureSetupRuntimeContext } from '../../../onboarding/onboarding-feature-setup-runtime' import { useCallback, useEffect, useRef, useState } from 'react' import { Check, Download, Loader2 } from 'lucide-react' import { toast } from 'sonner' @@ -20,7 +20,7 @@ import { useBundledSkillInstall, type BundledSkillInstall } from '@/hooks/skills/installation/use-bundled-skill-install' -import { BundledSkillInstallStatus } from '../skills/setup/BundledSkillInstallStatus' +import { BundledSkillInstallStatus } from '../../../skills/setup/BundledSkillInstallStatus' import { DEFAULT_ONBOARDING_FEATURE_SETUP_SELECTION, hasSelectedOnboardingFeatureSetup, @@ -29,7 +29,7 @@ import { runOnboardingFeatureSetup, type OnboardingFeatureSetupId, type OnboardingFeatureSetupSelection -} from '../onboarding/onboarding-feature-setup' +} from '../../../onboarding/onboarding-feature-setup' import { getDefaultAgentCapabilitySetupSelection, isAgentCapabilityReadinessChecking, diff --git a/src/renderer/src/components/feature-wall/AgentCapabilityStatusBadges.tsx b/src/renderer/src/components/feature-wall/setup/agent-capabilities/AgentCapabilityStatusBadges.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/AgentCapabilityStatusBadges.tsx rename to src/renderer/src/components/feature-wall/setup/agent-capabilities/AgentCapabilityStatusBadges.tsx diff --git a/src/renderer/src/components/feature-wall/BrowserUseSkillSetupCard.tsx b/src/renderer/src/components/feature-wall/setup/agent-capabilities/BrowserUseSkillSetupCard.tsx similarity index 97% rename from src/renderer/src/components/feature-wall/BrowserUseSkillSetupCard.tsx rename to src/renderer/src/components/feature-wall/setup/agent-capabilities/BrowserUseSkillSetupCard.tsx index c99a6b0ed1a..39c71064edf 100644 --- a/src/renderer/src/components/feature-wall/BrowserUseSkillSetupCard.tsx +++ b/src/renderer/src/components/feature-wall/setup/agent-capabilities/BrowserUseSkillSetupCard.tsx @@ -4,7 +4,7 @@ import { BROWSER_USE_ENABLED_STORAGE_KEY } from '@/lib/browser/browser-use-setup import type { InstalledAgentSkillState } from '@/hooks/skills/discovery/useInstalledAgentSkills' import { useActiveProjectSkillRuntime } from '@/hooks/skills/runtime/useActiveProjectSkillRuntime' import { AgentSkillSetupPanel } from '@/components/skills/setup/AgentSkillSetupPanel' -import { useAppStore } from '@/store' +import { useAppStore } from '@/store/index' import { useTranslate } from '@/i18n/use-translate' export function BrowserUseSkillSetupCard(props: { diff --git a/src/renderer/src/components/feature-wall/OrchestrationSetupCard.tsx b/src/renderer/src/components/feature-wall/setup/agent-capabilities/OrchestrationSetupCard.tsx similarity index 95% rename from src/renderer/src/components/feature-wall/OrchestrationSetupCard.tsx rename to src/renderer/src/components/feature-wall/setup/agent-capabilities/OrchestrationSetupCard.tsx index 6cb204bd76d..26ba6f56987 100644 --- a/src/renderer/src/components/feature-wall/OrchestrationSetupCard.tsx +++ b/src/renderer/src/components/feature-wall/setup/agent-capabilities/OrchestrationSetupCard.tsx @@ -2,7 +2,7 @@ import type { JSX } from 'react' import { ORCHESTRATION_SKILL_NAME } from '@/lib/skills/installation/agent-feature-skill-names' import type { InstalledAgentSkillState } from '@/hooks/skills/discovery/useInstalledAgentSkills' import { useActiveProjectSkillRuntime } from '@/hooks/skills/runtime/useActiveProjectSkillRuntime' -import { AgentSkillSetupPanel } from '../skills/setup/AgentSkillSetupPanel' +import { AgentSkillSetupPanel } from '../../../skills/setup/AgentSkillSetupPanel' import { useAppStore } from '@/store/index' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/agent-capability-setup-copy.ts b/src/renderer/src/components/feature-wall/setup/agent-capabilities/agent-capability-setup-copy.ts similarity index 100% rename from src/renderer/src/components/feature-wall/agent-capability-setup-copy.ts rename to src/renderer/src/components/feature-wall/setup/agent-capabilities/agent-capability-setup-copy.ts diff --git a/src/renderer/src/components/feature-wall/agent-capability-setup-rows.tsx b/src/renderer/src/components/feature-wall/setup/agent-capabilities/agent-capability-setup-rows.tsx similarity index 95% rename from src/renderer/src/components/feature-wall/agent-capability-setup-rows.tsx rename to src/renderer/src/components/feature-wall/setup/agent-capabilities/agent-capability-setup-rows.tsx index f05c87f089c..314506c0e69 100644 --- a/src/renderer/src/components/feature-wall/agent-capability-setup-rows.tsx +++ b/src/renderer/src/components/feature-wall/setup/agent-capabilities/agent-capability-setup-rows.tsx @@ -1,7 +1,7 @@ import type { ReactNode } from 'react' import { Globe2, MonitorCog, Workflow } from 'lucide-react' import { translate } from '@/i18n/i18n' -import type { OnboardingFeatureSetupId } from '../onboarding/onboarding-feature-setup' +import type { OnboardingFeatureSetupId } from '../../../onboarding/onboarding-feature-setup' type AgentCapabilitySetupRow = { id: OnboardingFeatureSetupId diff --git a/src/renderer/src/components/feature-wall/agent-capability-setup-status.ts b/src/renderer/src/components/feature-wall/setup/agent-capabilities/agent-capability-setup-status.ts similarity index 98% rename from src/renderer/src/components/feature-wall/agent-capability-setup-status.ts rename to src/renderer/src/components/feature-wall/setup/agent-capabilities/agent-capability-setup-status.ts index 56e1f593498..07fe4874599 100644 --- a/src/renderer/src/components/feature-wall/agent-capability-setup-status.ts +++ b/src/renderer/src/components/feature-wall/setup/agent-capabilities/agent-capability-setup-status.ts @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react' import type { OnboardingFeatureSetupId, OnboardingFeatureSetupSelection -} from '../onboarding/onboarding-feature-setup' +} from '../../../onboarding/onboarding-feature-setup' import { COMPUTER_USE_SKILL_NAME, ORCA_CLI_SKILL_NAME, @@ -15,7 +15,7 @@ import { import { useOnboardingSkillRuntime } from '@/hooks/skills/runtime/use-onboarding-skill-runtime' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import { isAgentCapabilitiesDone } from './feature-wall-setup-progress' +import { isAgentCapabilitiesDone } from '../feature-wall-setup-progress' export type AgentCapabilityInstallStatusTone = | 'ready' diff --git a/src/renderer/src/components/feature-wall/FeatureWallBrowserAction.tsx b/src/renderer/src/components/feature-wall/setup/browser/FeatureWallBrowserAction.tsx similarity index 93% rename from src/renderer/src/components/feature-wall/FeatureWallBrowserAction.tsx rename to src/renderer/src/components/feature-wall/setup/browser/FeatureWallBrowserAction.tsx index fe369f2c93e..a80724ce022 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallBrowserAction.tsx +++ b/src/renderer/src/components/feature-wall/setup/browser/FeatureWallBrowserAction.tsx @@ -1,10 +1,10 @@ import { useBundledSkillInstallAction } from '@/hooks/skills/installation/use-bundled-skill-install-action' -import { bundledSkillSetupActionLabel } from '../skills/setup/bundled-skill-setup-copy' +import { bundledSkillSetupActionLabel } from '../../../skills/setup/bundled-skill-setup-copy' import { ORCA_CLI_SKILL_NAME } from '@/lib/skills/installation/agent-feature-skill-names' import { useOnboardingSkillRuntime } from '@/hooks/skills/runtime/use-onboarding-skill-runtime' -import { FeatureSetupInlineTerminal } from '../onboarding/FeatureSetupInlineTerminal' +import { FeatureSetupInlineTerminal } from '../../../onboarding/FeatureSetupInlineTerminal' import { buildAgentFeatureSkillInstallCommand } from '@/lib/skills/installation/agent-feature-install-commands' -import { buildSkillCommandForRuntime } from '../../lib/skills/runtime/skill-runtime-command' +import { buildSkillCommandForRuntime } from '../../../../lib/skills/runtime/skill-runtime-command' import { useCallback, useState } from 'react' import { ArrowUpRight, Download, Loader2 } from 'lucide-react' import { toast } from 'sonner' @@ -18,18 +18,18 @@ import { bundledSkillInstallNeedsAttention, useBundledSkillInstall } from '@/hooks/skills/installation/use-bundled-skill-install' -import { BundledSkillInstallStatus } from '../skills/setup/BundledSkillInstallStatus' +import { BundledSkillInstallStatus } from '../../../skills/setup/BundledSkillInstallStatus' import { onboardingFeatureSetupRunTelemetry, runOnboardingFeatureSetup -} from '../onboarding/onboarding-feature-setup' -import type { OnboardingFeatureSetupSelection } from '../onboarding/onboarding-feature-setup' +} from '../../../onboarding/onboarding-feature-setup' +import type { OnboardingFeatureSetupSelection } from '../../../onboarding/onboarding-feature-setup' import { promptForSetupGuideProject, useSetupTargetWorktree -} from './FeatureWallSetupWorkflowActions' +} from '../FeatureWallSetupWorkflowActions' import { getClientCreationActionPolicy } from '@/lib/workspace/creation/surface-policy/client-creation-action-policy' -import type { OnboardingFeatureSetupRuntimeContext } from '../onboarding/onboarding-feature-setup-runtime' +import type { OnboardingFeatureSetupRuntimeContext } from '../../../onboarding/onboarding-feature-setup-runtime' export function BrowserAction(props: { done: boolean }): React.JSX.Element { const t = useTranslate() diff --git a/src/renderer/src/components/feature-wall/feature-wall-setup-checklist-localized-copy.ts b/src/renderer/src/components/feature-wall/setup/feature-wall-setup-checklist-localized-copy.ts similarity index 98% rename from src/renderer/src/components/feature-wall/feature-wall-setup-checklist-localized-copy.ts rename to src/renderer/src/components/feature-wall/setup/feature-wall-setup-checklist-localized-copy.ts index a6d6ed6d880..8986117373f 100644 --- a/src/renderer/src/components/feature-wall/feature-wall-setup-checklist-localized-copy.ts +++ b/src/renderer/src/components/feature-wall/setup/feature-wall-setup-checklist-localized-copy.ts @@ -1,7 +1,7 @@ import type { FeatureWallSetupStep, FeatureWallSetupStepId -} from '../../../../shared/feature-wall-setup-steps' +} from '../../../../../shared/feature-wall-setup-steps' import { translate } from '@/i18n/i18n' import { createLocalizedCatalog } from '@/i18n/localized-catalog' diff --git a/src/renderer/src/components/feature-wall/feature-wall-setup-progress.ts b/src/renderer/src/components/feature-wall/setup/feature-wall-setup-progress.ts similarity index 89% rename from src/renderer/src/components/feature-wall/feature-wall-setup-progress.ts rename to src/renderer/src/components/feature-wall/setup/feature-wall-setup-progress.ts index 79dc6ce23da..b8882e8849c 100644 --- a/src/renderer/src/components/feature-wall/feature-wall-setup-progress.ts +++ b/src/renderer/src/components/feature-wall/setup/feature-wall-setup-progress.ts @@ -1,12 +1,12 @@ -import type { FeatureInteractionState } from '../../../../shared/feature-interactions' -import { hasFeatureInteraction } from '../../../../shared/feature-interactions' +import type { FeatureInteractionState } from '../../../../../shared/feature-interactions' +import { hasFeatureInteraction } from '../../../../../shared/feature-interactions' import { FEATURE_WALL_SETUP_STEPS, type FeatureWallSetupStepId -} from '../../../../shared/feature-wall-setup-steps' -import type { GlobalSettings } from '../../../../shared/global-settings-types' -import type { Worktree } from '../../../../shared/worktree/types' -import { selectWorktreesByRepo } from '../../store/workspaces/catalog/workspace-record-fields' +} from '../../../../../shared/feature-wall-setup-steps' +import type { GlobalSettings } from '../../../../../shared/global-settings-types' +import type { Worktree } from '../../../../../shared/worktree/types' +import { selectWorktreesByRepo } from '../../../store/workspaces/catalog/workspace-record-fields' export type FeatureWallSetupProgressInput = { ready?: boolean diff --git a/src/renderer/src/components/feature-wall/ConnectIntegrationsList.tsx b/src/renderer/src/components/feature-wall/setup/integrations/ConnectIntegrationsList.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/ConnectIntegrationsList.tsx rename to src/renderer/src/components/feature-wall/setup/integrations/ConnectIntegrationsList.tsx diff --git a/src/renderer/src/components/feature-wall/connect-integration-step.tsx b/src/renderer/src/components/feature-wall/setup/integrations/connect-integration-step.tsx similarity index 98% rename from src/renderer/src/components/feature-wall/connect-integration-step.tsx rename to src/renderer/src/components/feature-wall/setup/integrations/connect-integration-step.tsx index 903cf2642d9..3171de0eae5 100644 --- a/src/renderer/src/components/feature-wall/connect-integration-step.tsx +++ b/src/renderer/src/components/feature-wall/setup/integrations/connect-integration-step.tsx @@ -3,8 +3,6 @@ import { cn } from '@/lib/class-names' import type { IntegrationStepState } from './use-integration-connection-status' import { useTranslate } from '@/i18n/use-translate' -export type { IntegrationStepState } - // Track expansion separately from connection state so completed steps can reopen without losing their status. export function IntegrationStep(props: { index: number diff --git a/src/renderer/src/components/feature-wall/use-feature-wall-task-source-presentation.ts b/src/renderer/src/components/feature-wall/setup/integrations/use-feature-wall-task-source-presentation.ts similarity index 94% rename from src/renderer/src/components/feature-wall/use-feature-wall-task-source-presentation.ts rename to src/renderer/src/components/feature-wall/setup/integrations/use-feature-wall-task-source-presentation.ts index 075ee1243b8..2619c68163b 100644 --- a/src/renderer/src/components/feature-wall/use-feature-wall-task-source-presentation.ts +++ b/src/renderer/src/components/feature-wall/setup/integrations/use-feature-wall-task-source-presentation.ts @@ -3,8 +3,8 @@ import { useAppStore } from '@/store/index' import { getLocalPreflightContext } from '@/runtime/hosts/preflight/local-preflight-context' import { getProviderRuntimeContextKey } from '@/runtime/hosts/observations/provider-runtime-context' import { deriveIntegrationConnectionStatus } from './use-integration-connection-status' -import type { FeatureWallWorkflow } from '../../../../shared/feature-wall-workflows' -import { localPreflightContextKey } from '../../runtime/hosts/preflight/local-preflight-context-key' +import type { FeatureWallWorkflow } from '../../../../../../shared/feature-wall-workflows' +import { localPreflightContextKey } from '../../../../runtime/hosts/preflight/local-preflight-context-key' export type FeatureWallTaskSourcePresentation = { workflow: FeatureWallWorkflow diff --git a/src/renderer/src/components/feature-wall/use-integration-connection-status.ts b/src/renderer/src/components/feature-wall/setup/integrations/use-integration-connection-status.ts similarity index 98% rename from src/renderer/src/components/feature-wall/use-integration-connection-status.ts rename to src/renderer/src/components/feature-wall/setup/integrations/use-integration-connection-status.ts index 8aaedf5efa1..debba71cecf 100644 --- a/src/renderer/src/components/feature-wall/use-integration-connection-status.ts +++ b/src/renderer/src/components/feature-wall/setup/integrations/use-integration-connection-status.ts @@ -1,7 +1,7 @@ import { useAppStore } from '@/store/index' import { getLocalPreflightContext } from '@/runtime/hosts/preflight/local-preflight-context' import { getProviderRuntimeContextKey } from '@/runtime/hosts/observations/provider-runtime-context' -import { localPreflightContextKey } from '../../runtime/hosts/preflight/local-preflight-context-key' +import { localPreflightContextKey } from '../../../../runtime/hosts/preflight/local-preflight-context-key' export type IntegrationStepState = 'active' | 'done' | 'upcoming' diff --git a/src/renderer/src/components/feature-wall/FeatureWallClickRing.tsx b/src/renderer/src/components/feature-wall/storyboard-interaction/FeatureWallClickRing.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/FeatureWallClickRing.tsx rename to src/renderer/src/components/feature-wall/storyboard-interaction/FeatureWallClickRing.tsx diff --git a/src/renderer/src/components/feature-wall/review-animated-visual-shared.tsx b/src/renderer/src/components/feature-wall/storyboard-interaction/storyboard-glyphs.tsx similarity index 72% rename from src/renderer/src/components/feature-wall/review-animated-visual-shared.tsx rename to src/renderer/src/components/feature-wall/storyboard-interaction/storyboard-glyphs.tsx index 74420fee307..ccc460c8fba 100644 --- a/src/renderer/src/components/feature-wall/review-animated-visual-shared.tsx +++ b/src/renderer/src/components/feature-wall/storyboard-interaction/storyboard-glyphs.tsx @@ -203,78 +203,3 @@ export function CodexLogo(): JSX.Element { ) } - -export type DiffLine = { kind: 'ctx' | 'add' | 'rem'; t: string } -export type Hunk = { - header: string - oldStart: number - newStart: number - lines: DiffLine[] -} - -export const HUNKS: readonly Hunk[] = [ - { - header: '@@ -42,5 +42,7 @@ export function applyMigration(db, version) {', - oldStart: 42, - newStart: 42, - lines: [ - { kind: 'ctx', t: ' const ctx = beginTx(db)' }, - { kind: 'rem', t: ' runStep(ctx, "schema", version)' }, - { kind: 'add', t: ' await runStep(ctx, "schema", version)' }, - { kind: 'add', t: ' await runStep(ctx, "backfill", version)' }, - { kind: 'ctx', t: ' commit(ctx)' } - ] - }, - { - header: '@@ -88,3 +90,4 @@ function backfillUsers(rows) {', - oldStart: 88, - newStart: 90, - lines: [ - { kind: 'ctx', t: ' for (const row of rows) {' }, - { kind: 'rem', t: ' db.exec(sql, row)' }, - { kind: 'add', t: ' if (!row.tier) continue' }, - { kind: 'add', t: ' db.exec(sql, [row.tier, row.id])' } - ] - } -] - -export const NOTE_TARGETS: readonly { - hunk: number - lineIdx: number - body: string - summary: string -}[] = [ - { - hunk: 0, - lineIdx: 2, - body: 'Backfill must run before commit if schema assumes new columns.', - summary: 'Sequence schema → backfill before commit' - }, - { - hunk: 1, - lineIdx: 2, - body: 'Silently skipping rows — log the count or surface it in the result.', - summary: 'Log skipped rows in result' - } -] - -export const COMMIT_MSG = 'Run schema + backfill steps in order during migration' -export const PR_TITLE = 'Run schema + backfill in order during migration' -export const PR_BODY = - 'Awaits schema before backfill so commits stay in order. Skips rows with no tier on reruns.' - -export const SHIP_FILES: readonly string[] = ['migrate.ts', 'backfill-users.ts', 'migrate.test.ts'] - -export function tokenize(line: string): string { - const KW = - /\b(?:const|let|await|function|return|if|else|for|of|in|try|catch|throw|new|export|import|from)\b/g - const STR = /(`[^`]*`|"[^"]*"|'[^']*')/g - let html = line.replace(/&/g, '&').replace(//g, '>') - html = html.replace(STR, (m) => `${m}`) - html = html.replace(KW, (m) => `${m}`) - html = html.replace(/(\b[A-Za-z_]\w*\b)(?=\()/g, (m) => `${m}`) - return html -} - -export const PANEL_WIDTH = 480 -export const PANEL_HEIGHT = 416 diff --git a/src/renderer/src/components/feature-wall/TasksAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/task-storyboard/TasksAnimatedVisual.tsx similarity index 98% rename from src/renderer/src/components/feature-wall/TasksAnimatedVisual.tsx rename to src/renderer/src/components/feature-wall/task-storyboard/TasksAnimatedVisual.tsx index 12f633ef9f7..1a6f03f833b 100644 --- a/src/renderer/src/components/feature-wall/TasksAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/task-storyboard/TasksAnimatedVisual.tsx @@ -2,8 +2,8 @@ import { useEffect, useLayoutEffect, useRef, useState } from 'react' import type { JSX } from 'react' import { ArrowRight, CircleDot } from 'lucide-react' import { AgentStateDot } from '@/components/AgentStateDot' -import { ClaudeIcon } from '../status-bar/icons' -import { FeatureWallClickRing } from './FeatureWallClickRing' +import { ClaudeIcon } from '../../status-bar/icons' +import { FeatureWallClickRing } from '../storyboard-interaction/FeatureWallClickRing' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/FeatureWallBody.tsx b/src/renderer/src/components/feature-wall/tour/FeatureWallBody.tsx similarity index 86% rename from src/renderer/src/components/feature-wall/FeatureWallBody.tsx rename to src/renderer/src/components/feature-wall/tour/FeatureWallBody.tsx index 125c0017f4f..3607aeee12f 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallBody.tsx +++ b/src/renderer/src/components/feature-wall/tour/FeatureWallBody.tsx @@ -1,26 +1,26 @@ import type { JSX } from 'react' -import type { FeatureWallWorkflow } from '../../../../shared/feature-wall-workflows' -import type { FeatureWallOpenSourceTelemetry } from '../../../../shared/telemetry-events' -import type { AgentsStep } from '../../../../shared/agents-orchestration-steps' -import type { WorkbenchStep } from '../../../../shared/workbench-steps' -import type { ReviewStep } from '../../../../shared/review-steps' -import type { GlobalSettings } from '../../../../shared/global-settings-types' +import type { FeatureWallWorkflow } from '../../../../../shared/feature-wall-workflows' +import type { FeatureWallOpenSourceTelemetry } from '../../../../../shared/telemetry-property-schemas' +import type { AgentsStep } from '../../../../../shared/agents-orchestration-steps' +import type { WorkbenchStep } from '../../../../../shared/workbench-steps' +import type { ReviewStep } from '../../../../../shared/review-steps' +import type { GlobalSettings } from '../../../../../shared/global-settings-types' import type { InstalledAgentSkillState } from '@/hooks/skills/discovery/useInstalledAgentSkills' import { cn } from '@/lib/class-names' import { PreviewMedia, RelatedFeatures } from './FeatureWallPreview' import { getLocalizedFeatureWallWorkflowCopy } from './feature-wall-tour-localized-copy' -import { TasksAnimatedVisual } from './TasksAnimatedVisual' -import { WorkspacesAnimatedVisual } from './WorkspacesAnimatedVisual' -import { WorkbenchAnimatedVisual } from './WorkbenchAnimatedVisual' -import { EditorAnimatedVisual } from './EditorAnimatedVisual' -import { BrowserAnimatedVisual } from './BrowserAnimatedVisual' -import { AgentsOrchestrationVisual } from './AgentsOrchestrationVisual' -import { ReviewAnimatedVisual } from './ReviewAnimatedVisual' -import { GitHubRow, LinearRow } from '../onboarding/IntegrationsStep' -import { OrchestrationSetupCard } from './OrchestrationSetupCard' -import { BrowserUseSkillSetupCard } from './BrowserUseSkillSetupCard' -import { UsageAccountsCard } from './agents-orchestration/UsageAccountsCard' -import { AiCommitPrSettingsCard } from './AiCommitPrSettingsCard' +import { TasksAnimatedVisual } from '../task-storyboard/TasksAnimatedVisual' +import { WorkspacesAnimatedVisual } from '../workspace-storyboard/WorkspacesAnimatedVisual' +import { WorkbenchAnimatedVisual } from '../workbench-storyboard/WorkbenchAnimatedVisual' +import { EditorAnimatedVisual } from '../editor-storyboard/EditorAnimatedVisual' +import { BrowserAnimatedVisual } from '../browser-storyboard/BrowserAnimatedVisual' +import { AgentsOrchestrationVisual } from '../agents-orchestration/AgentsOrchestrationVisual' +import { ReviewAnimatedVisual } from '../review-storyboard/ReviewAnimatedVisual' +import { GitHubRow, LinearRow } from '../../onboarding/IntegrationsStep' +import { OrchestrationSetupCard } from '../setup/agent-capabilities/OrchestrationSetupCard' +import { BrowserUseSkillSetupCard } from '../setup/agent-capabilities/BrowserUseSkillSetupCard' +import { UsageAccountsCard } from '../agents-orchestration/UsageAccountsCard' +import { AiCommitPrSettingsCard } from '../commit-pr-settings/AiCommitPrSettingsCard' import { KeepAwakeCard } from './KeepAwakeCard' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/FeatureWallContinueButton.tsx b/src/renderer/src/components/feature-wall/tour/FeatureWallContinueButton.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/FeatureWallContinueButton.tsx rename to src/renderer/src/components/feature-wall/tour/FeatureWallContinueButton.tsx diff --git a/src/renderer/src/components/feature-wall/FeatureWallPreview.tsx b/src/renderer/src/components/feature-wall/tour/FeatureWallPreview.tsx similarity index 97% rename from src/renderer/src/components/feature-wall/FeatureWallPreview.tsx rename to src/renderer/src/components/feature-wall/tour/FeatureWallPreview.tsx index d8909dd37e5..b80faf716fa 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallPreview.tsx +++ b/src/renderer/src/components/feature-wall/tour/FeatureWallPreview.tsx @@ -4,8 +4,8 @@ import { ChevronRight } from 'lucide-react' import { getFeatureWallMediaTile, type FeatureWallWorkflow -} from '../../../../shared/feature-wall-workflows' -import type { FeatureWallOpenSourceTelemetry } from '../../../../shared/telemetry-events' +} from '../../../../../shared/feature-wall-workflows' +import type { FeatureWallOpenSourceTelemetry } from '../../../../../shared/telemetry-property-schemas' import { track } from '@/diagnostics/telemetry/telemetry' import { useTranslate } from '@/i18n/use-translate' import { getLocalizedFeatureWallTileTitle } from './feature-wall-tour-localized-copy' diff --git a/src/renderer/src/components/feature-wall/FeatureWallRail.tsx b/src/renderer/src/components/feature-wall/tour/FeatureWallRail.tsx similarity index 97% rename from src/renderer/src/components/feature-wall/FeatureWallRail.tsx rename to src/renderer/src/components/feature-wall/tour/FeatureWallRail.tsx index d97b679555e..5d5cf164e45 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallRail.tsx +++ b/src/renderer/src/components/feature-wall/tour/FeatureWallRail.tsx @@ -4,10 +4,10 @@ import { FEATURE_WALL_WORKFLOWS, type FeatureWallWorkflow, type FeatureWallWorkflowId -} from '../../../../shared/feature-wall-workflows' -import type { AgentsStep, AgentsStepId } from '../../../../shared/agents-orchestration-steps' -import type { WorkbenchStep, WorkbenchStepId } from '../../../../shared/workbench-steps' -import type { ReviewStep, ReviewStepId } from '../../../../shared/review-steps' +} from '../../../../../shared/feature-wall-workflows' +import type { AgentsStep, AgentsStepId } from '../../../../../shared/agents-orchestration-steps' +import type { WorkbenchStep, WorkbenchStepId } from '../../../../../shared/workbench-steps' +import type { ReviewStep, ReviewStepId } from '../../../../../shared/review-steps' import { cn } from '@/lib/class-names' import { useTranslate } from '@/i18n/use-translate' import { diff --git a/src/renderer/src/components/feature-wall/FeatureWallTourPanel.tsx b/src/renderer/src/components/feature-wall/tour/FeatureWallTourPanel.tsx similarity index 93% rename from src/renderer/src/components/feature-wall/FeatureWallTourPanel.tsx rename to src/renderer/src/components/feature-wall/tour/FeatureWallTourPanel.tsx index a88de257bf9..ea207220aeb 100644 --- a/src/renderer/src/components/feature-wall/FeatureWallTourPanel.tsx +++ b/src/renderer/src/components/feature-wall/tour/FeatureWallTourPanel.tsx @@ -1,16 +1,16 @@ import type { JSX, KeyboardEvent, MutableRefObject, ReactNode } from 'react' -import type { AgentsStep, AgentsStepId } from '../../../../shared/agents-orchestration-steps' +import type { AgentsStep, AgentsStepId } from '../../../../../shared/agents-orchestration-steps' import type { FeatureWallWorkflow, FeatureWallWorkflowId -} from '../../../../shared/feature-wall-workflows' -import type { ReviewStep, ReviewStepId } from '../../../../shared/review-steps' -import type { FeatureWallOpenSourceTelemetry } from '../../../../shared/telemetry-events' -import type { GlobalSettings } from '../../../../shared/global-settings-types' -import type { WorkbenchStep, WorkbenchStepId } from '../../../../shared/workbench-steps' +} from '../../../../../shared/feature-wall-workflows' +import type { ReviewStep, ReviewStepId } from '../../../../../shared/review-steps' +import type { FeatureWallOpenSourceTelemetry } from '../../../../../shared/telemetry-property-schemas' +import type { GlobalSettings } from '../../../../../shared/global-settings-types' +import type { WorkbenchStep, WorkbenchStepId } from '../../../../../shared/workbench-steps' import type { InstalledAgentSkillState } from '@/hooks/skills/discovery/useInstalledAgentSkills' import { cn } from '@/lib/class-names' -import type { FeatureWallCompletionState } from './use-feature-wall-completion' +import type { FeatureWallCompletionState } from '../completion/use-feature-wall-completion' import { FeatureWallBody } from './FeatureWallBody' import { FeatureWallRail } from './FeatureWallRail' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/KeepAwakeCard.tsx b/src/renderer/src/components/feature-wall/tour/KeepAwakeCard.tsx similarity index 89% rename from src/renderer/src/components/feature-wall/KeepAwakeCard.tsx rename to src/renderer/src/components/feature-wall/tour/KeepAwakeCard.tsx index bb34e6d2248..9fcaf918d96 100644 --- a/src/renderer/src/components/feature-wall/KeepAwakeCard.tsx +++ b/src/renderer/src/components/feature-wall/tour/KeepAwakeCard.tsx @@ -3,13 +3,13 @@ import { Switch } from '@/components/ui/switch' import { getAgentAwakeDescription, getAgentAwakeTitle -} from '../settings/agents/behavior/agent-awake-copy' -import type { GlobalSettings } from '../../../../shared/global-settings-types' +} from '../../settings/agents/behavior/agent-awake-copy' +import type { GlobalSettings } from '../../../../../shared/global-settings-types' import { useTranslate } from '@/i18n/use-translate' import { computerAwakeSettingsForMode, normalizeComputerAwakeMode -} from '../../../../shared/computer-awake-mode' +} from '../../../../../shared/computer-awake-mode' export function KeepAwakeCard(props: { settings: GlobalSettings diff --git a/src/renderer/src/components/feature-wall/feature-wall-active-step-copy.ts b/src/renderer/src/components/feature-wall/tour/feature-wall-active-step-copy.ts similarity index 81% rename from src/renderer/src/components/feature-wall/feature-wall-active-step-copy.ts rename to src/renderer/src/components/feature-wall/tour/feature-wall-active-step-copy.ts index 441d5c546e3..1990c869afc 100644 --- a/src/renderer/src/components/feature-wall/feature-wall-active-step-copy.ts +++ b/src/renderer/src/components/feature-wall/tour/feature-wall-active-step-copy.ts @@ -1,6 +1,6 @@ -import type { AgentsStep } from '../../../../shared/agents-orchestration-steps' -import type { ReviewStep } from '../../../../shared/review-steps' -import type { WorkbenchStep } from '../../../../shared/workbench-steps' +import type { AgentsStep } from '../../../../../shared/agents-orchestration-steps' +import type { ReviewStep } from '../../../../../shared/review-steps' +import type { WorkbenchStep } from '../../../../../shared/workbench-steps' import type { FeatureWallActiveStepCopy } from './FeatureWallTourPanel' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/feature-wall/feature-wall-assets.ts b/src/renderer/src/components/feature-wall/tour/feature-wall-assets.ts similarity index 100% rename from src/renderer/src/components/feature-wall/feature-wall-assets.ts rename to src/renderer/src/components/feature-wall/tour/feature-wall-assets.ts diff --git a/src/renderer/src/components/feature-wall/feature-wall-modal-helpers.ts b/src/renderer/src/components/feature-wall/tour/feature-wall-open-source.ts similarity index 76% rename from src/renderer/src/components/feature-wall/feature-wall-modal-helpers.ts rename to src/renderer/src/components/feature-wall/tour/feature-wall-open-source.ts index afdb12316e5..ad43b39e122 100644 --- a/src/renderer/src/components/feature-wall/feature-wall-modal-helpers.ts +++ b/src/renderer/src/components/feature-wall/tour/feature-wall-open-source.ts @@ -1,5 +1,4 @@ -import type { FeatureWallOpenSourceTelemetry } from '../../../../shared/telemetry-events' -export { usePrefersReducedMotion } from '@/hooks/usePrefersReducedMotion' +import type { FeatureWallOpenSourceTelemetry } from '../../../../../shared/telemetry-property-schemas' export function getFeatureWallOpenSource( modalData: Record diff --git a/src/renderer/src/components/feature-wall/feature-wall-rail-navigation.ts b/src/renderer/src/components/feature-wall/tour/feature-wall-rail-navigation.ts similarity index 100% rename from src/renderer/src/components/feature-wall/feature-wall-rail-navigation.ts rename to src/renderer/src/components/feature-wall/tour/feature-wall-rail-navigation.ts diff --git a/src/renderer/src/components/feature-wall/feature-wall-tour-localized-copy.ts b/src/renderer/src/components/feature-wall/tour/feature-wall-tour-localized-copy.ts similarity index 94% rename from src/renderer/src/components/feature-wall/feature-wall-tour-localized-copy.ts rename to src/renderer/src/components/feature-wall/tour/feature-wall-tour-localized-copy.ts index 057c4d42b00..9fda148b368 100644 --- a/src/renderer/src/components/feature-wall/feature-wall-tour-localized-copy.ts +++ b/src/renderer/src/components/feature-wall/tour/feature-wall-tour-localized-copy.ts @@ -1,8 +1,8 @@ -import type { AgentsStepId } from '../../../../shared/agents-orchestration-steps' -import type { FeatureWallMediaTileId } from '../../../../shared/feature-wall-tiles' -import type { FeatureWallWorkflowId } from '../../../../shared/feature-wall-workflows' -import type { ReviewStepId } from '../../../../shared/review-steps' -import type { WorkbenchStepId } from '../../../../shared/workbench-steps' +import type { AgentsStepId } from '../../../../../shared/agents-orchestration-steps' +import type { FeatureWallMediaTileId } from '../../../../../shared/feature-wall-tiles' +import type { FeatureWallWorkflowId } from '../../../../../shared/feature-wall-workflows' +import type { ReviewStepId } from '../../../../../shared/review-steps' +import type { WorkbenchStepId } from '../../../../../shared/workbench-steps' import { translate } from '@/i18n/i18n' import { createLocalizedCatalog } from '@/i18n/localized-catalog' diff --git a/src/renderer/src/components/feature-wall/use-feature-wall-tour-keyboard-shortcut.ts b/src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-keyboard-shortcut.ts similarity index 100% rename from src/renderer/src/components/feature-wall/use-feature-wall-tour-keyboard-shortcut.ts rename to src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-keyboard-shortcut.ts diff --git a/src/renderer/src/components/feature-wall/use-feature-wall-tour-rail-keydown.ts b/src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-rail-keydown.ts similarity index 95% rename from src/renderer/src/components/feature-wall/use-feature-wall-tour-rail-keydown.ts rename to src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-rail-keydown.ts index 1c0347d15ca..1c642c3a386 100644 --- a/src/renderer/src/components/feature-wall/use-feature-wall-tour-rail-keydown.ts +++ b/src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-rail-keydown.ts @@ -3,7 +3,7 @@ import type { KeyboardEvent, RefObject } from 'react' import { FEATURE_WALL_WORKFLOWS, type FeatureWallWorkflow -} from '../../../../shared/feature-wall-workflows' +} from '../../../../../shared/feature-wall-workflows' import { getFeatureWallRailNavigationTarget, type FeatureWallRailNavigationKey diff --git a/src/renderer/src/components/feature-wall/use-feature-wall-tour-skills.ts b/src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-skills.ts similarity index 100% rename from src/renderer/src/components/feature-wall/use-feature-wall-tour-skills.ts rename to src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-skills.ts diff --git a/src/renderer/src/components/feature-wall/use-feature-wall-tour-telemetry.ts b/src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-telemetry.ts similarity index 91% rename from src/renderer/src/components/feature-wall/use-feature-wall-tour-telemetry.ts rename to src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-telemetry.ts index a17be2914d9..039138e7365 100644 --- a/src/renderer/src/components/feature-wall/use-feature-wall-tour-telemetry.ts +++ b/src/renderer/src/components/feature-wall/tour/use-feature-wall-tour-telemetry.ts @@ -1,13 +1,11 @@ import { useCallback, useEffect, useRef } from 'react' -import { FEATURE_WALL_MAX_DWELL_MS } from '../../../../shared/feature-wall-telemetry' +import { FEATURE_WALL_MAX_DWELL_MS } from '../../../../../shared/feature-wall-telemetry' import type { FeatureWallExitAction, FeatureWallTourDepthSummary -} from '../../../../shared/feature-wall-tour-depth' -import type { - EventProps, - FeatureWallOpenSourceTelemetry -} from '../../../../shared/telemetry-events' +} from '../../../../../shared/feature-wall-tour-depth' +import type { EventProps } from '../../../../../shared/telemetry-event-classification' +import type { FeatureWallOpenSourceTelemetry } from '../../../../../shared/telemetry-property-schemas' import { track } from '@/diagnostics/telemetry/telemetry' export type FeatureWallTourTelemetryState = { diff --git a/src/renderer/src/components/feature-wall/WorkbenchAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/workbench-storyboard/WorkbenchAnimatedVisual.tsx similarity index 98% rename from src/renderer/src/components/feature-wall/WorkbenchAnimatedVisual.tsx rename to src/renderer/src/components/feature-wall/workbench-storyboard/WorkbenchAnimatedVisual.tsx index e83ed862439..66051e6351a 100644 --- a/src/renderer/src/components/feature-wall/WorkbenchAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/workbench-storyboard/WorkbenchAnimatedVisual.tsx @@ -3,7 +3,7 @@ import type { JSX } from 'react' import { useShortcutLabel } from '@/hooks/useShortcutLabel' import { useTranslate } from '@/i18n/use-translate' import { cn } from '@/lib/class-names' -import { FeatureWallClickRing } from './FeatureWallClickRing' +import { FeatureWallClickRing } from '../storyboard-interaction/FeatureWallClickRing' import { WorkbenchAgentTerminalPane, WorkbenchSourceTerminalContent diff --git a/src/renderer/src/components/feature-wall/WorkbenchTerminalPanes.tsx b/src/renderer/src/components/feature-wall/workbench-storyboard/WorkbenchTerminalPanes.tsx similarity index 99% rename from src/renderer/src/components/feature-wall/WorkbenchTerminalPanes.tsx rename to src/renderer/src/components/feature-wall/workbench-storyboard/WorkbenchTerminalPanes.tsx index 23fde12880b..6de68e014e7 100644 --- a/src/renderer/src/components/feature-wall/WorkbenchTerminalPanes.tsx +++ b/src/renderer/src/components/feature-wall/workbench-storyboard/WorkbenchTerminalPanes.tsx @@ -1,7 +1,7 @@ import type { JSX, ReactNode } from 'react' import { useTranslate } from '@/i18n/use-translate' import { cn } from '@/lib/class-names' -import { ClaudeIcon, OpenAIIcon } from '../status-bar/icons' +import { ClaudeIcon, OpenAIIcon } from '../../status-bar/icons' import type { WORKBENCH_RUN_QUEUE, WorkbenchTerminalLine diff --git a/src/renderer/src/components/feature-wall/WorkbenchTerminalSplitMenu.tsx b/src/renderer/src/components/feature-wall/workbench-storyboard/WorkbenchTerminalSplitMenu.tsx similarity index 100% rename from src/renderer/src/components/feature-wall/WorkbenchTerminalSplitMenu.tsx rename to src/renderer/src/components/feature-wall/workbench-storyboard/WorkbenchTerminalSplitMenu.tsx diff --git a/src/renderer/src/components/feature-wall/use-workbench-terminal-cursor.ts b/src/renderer/src/components/feature-wall/workbench-storyboard/use-workbench-terminal-cursor.ts similarity index 100% rename from src/renderer/src/components/feature-wall/use-workbench-terminal-cursor.ts rename to src/renderer/src/components/feature-wall/workbench-storyboard/use-workbench-terminal-cursor.ts diff --git a/src/renderer/src/components/feature-wall/use-workbench-terminal-storyboard.ts b/src/renderer/src/components/feature-wall/workbench-storyboard/use-workbench-terminal-storyboard.ts similarity index 100% rename from src/renderer/src/components/feature-wall/use-workbench-terminal-storyboard.ts rename to src/renderer/src/components/feature-wall/workbench-storyboard/use-workbench-terminal-storyboard.ts diff --git a/src/renderer/src/components/feature-wall/workbench-terminal-storyboard-sequence.ts b/src/renderer/src/components/feature-wall/workbench-storyboard/workbench-terminal-storyboard-sequence.ts similarity index 100% rename from src/renderer/src/components/feature-wall/workbench-terminal-storyboard-sequence.ts rename to src/renderer/src/components/feature-wall/workbench-storyboard/workbench-terminal-storyboard-sequence.ts diff --git a/src/renderer/src/components/feature-wall/workbench-terminal-storyboard-state.ts b/src/renderer/src/components/feature-wall/workbench-storyboard/workbench-terminal-storyboard-state.ts similarity index 100% rename from src/renderer/src/components/feature-wall/workbench-terminal-storyboard-state.ts rename to src/renderer/src/components/feature-wall/workbench-storyboard/workbench-terminal-storyboard-state.ts diff --git a/src/renderer/src/components/feature-wall/WorkspacesAnimatedVisual.tsx b/src/renderer/src/components/feature-wall/workspace-storyboard/WorkspacesAnimatedVisual.tsx similarity index 99% rename from src/renderer/src/components/feature-wall/WorkspacesAnimatedVisual.tsx rename to src/renderer/src/components/feature-wall/workspace-storyboard/WorkspacesAnimatedVisual.tsx index 52bbb767f3a..f20c7fda96b 100644 --- a/src/renderer/src/components/feature-wall/WorkspacesAnimatedVisual.tsx +++ b/src/renderer/src/components/feature-wall/workspace-storyboard/WorkspacesAnimatedVisual.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react' import type { JSX } from 'react' import { AgentStateDot } from '@/components/AgentStateDot' import { installWindowVisibilityInterval } from '@/lib/window-activity/window-visibility-interval' -import { ClaudeIcon, OpenCodeGoIcon } from '../status-bar/icons' +import { ClaudeIcon, OpenCodeGoIcon } from '../../status-bar/icons' type AgentKind = 'claude' | 'codex' | 'opencode' diff --git a/src/renderer/src/components/right-sidebar/source-control/ai/text-generation-dialog-form.tsx b/src/renderer/src/components/right-sidebar/source-control/ai/text-generation-dialog-form.tsx index 2448d683637..5142d250c02 100644 --- a/src/renderer/src/components/right-sidebar/source-control/ai/text-generation-dialog-form.tsx +++ b/src/renderer/src/components/right-sidebar/source-control/ai/text-generation-dialog-form.tsx @@ -1,4 +1,4 @@ -import { useAppStore } from '@/store' +import { useAppStore } from '@/store/index' import React, { useCallback, useMemo, useState } from 'react' import { RefreshCw, Save, Sparkles, Terminal, TriangleAlert } from 'lucide-react' import { Button } from '@/components/ui/button' @@ -15,7 +15,7 @@ import { import { getAgentCatalog } from '@/lib/agent-catalog/agent-catalog' import { AgentLaunchPicker } from '@/components/agent/AgentLaunchPicker' import { DropdownMenuItem } from '@/components/ui/dropdown-menu' -import { commitMessagePickerModel } from '@/components/feature-wall/ai-commit-pr-picker' +import { commitMessagePickerModel } from '@/components/feature-wall/commit-pr-settings/ai-commit-pr-picker' import type { AgentPickerEntry } from '../../../../../../shared/agent-launch/picker/agent-launch-picker' import { planSourceControlTextGeneration } from '@/lib/source-control/ai/source-control-generation-plan' import { diff --git a/src/renderer/src/components/settings/setup-guide/SettingsSetupGuidePane.tsx b/src/renderer/src/components/settings/setup-guide/SettingsSetupGuidePane.tsx index 761bc286229..a5fa67b5bc9 100644 --- a/src/renderer/src/components/settings/setup-guide/SettingsSetupGuidePane.tsx +++ b/src/renderer/src/components/settings/setup-guide/SettingsSetupGuidePane.tsx @@ -4,7 +4,7 @@ import { getFirstIncompleteFeatureWallSetupStepId } from '../../../../../shared/feature-wall-setup-steps' import type { FeatureWallSetupStepId } from '../../../../../shared/feature-wall-setup-steps' -import { FeatureWallSetupChecklist } from '../../feature-wall/FeatureWallSetupChecklist' +import { FeatureWallSetupChecklist } from '../../feature-wall/setup/FeatureWallSetupChecklist' import { useSetupGuideProgress } from '../../setup-guide/use-setup-guide-progress' export function SettingsSetupGuidePane(): React.JSX.Element { diff --git a/src/renderer/src/components/setup-guide/SetupGuideModal.mount-gating.test.tsx b/src/renderer/src/components/setup-guide/SetupGuideModal.mount-gating.test.tsx deleted file mode 100644 index 52a08c49b9d..00000000000 --- a/src/renderer/src/components/setup-guide/SetupGuideModal.mount-gating.test.tsx +++ /dev/null @@ -1,194 +0,0 @@ -// @vitest-environment happy-dom - -import { act } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { FEATURE_WALL_SETUP_STEPS } from '../../../../shared/feature-wall-setup-steps' -import type { FeatureWallSetupProgress } from '../feature-wall/feature-wall-setup-progress' -import { useAppStore } from '@/store' -import SetupGuideModal from './SetupGuideModal' - -const contentProbe = vi.hoisted(() => ({ - renders: vi.fn(), - storeNotifications: vi.fn(), - subscriptions: vi.fn(), - unsubscriptions: vi.fn(), - telemetryOpenStates: vi.fn(), - refreshEnabledStates: vi.fn() -})) - -const progress: FeatureWallSetupProgress = { - ready: true, - stepDone: { - 'default-agent': false, - 'add-two-repos': false, - notifications: false, - 'two-worktrees': false, - browser: false, - 'task-sources': false, - 'agent-capabilities': false, - 'setup-script': false - }, - coreDoneCount: 0, - coreTotal: FEATURE_WALL_SETUP_STEPS.length -} - -vi.mock('./use-setup-guide-progress', async () => { - const React = await import('react') - return { - useSetupGuideProgress: (shouldRefreshCoreState: boolean) => { - contentProbe.renders() - contentProbe.refreshEnabledStates(shouldRefreshCoreState) - useAppStore((state) => state.activeWorktreeId) - React.useEffect(() => { - contentProbe.subscriptions() - const unsubscribe = useAppStore.subscribe(() => contentProbe.storeNotifications()) - return () => { - contentProbe.unsubscriptions() - unsubscribe() - } - }, []) - return progress - } - } -}) - -vi.mock('./use-setup-guide-telemetry', () => ({ - useSetupGuideOpenCloseTelemetry: ({ isOpen }: { isOpen: boolean }) => - contentProbe.telemetryOpenStates(isOpen) -})) - -vi.mock('../feature-wall/FeatureWallSetupChecklist', () => ({ - FeatureWallSetupChecklist: () =>
-})) - -vi.mock('./SetupGuideProgressRing', () => ({ - SetupGuideProgressRing: () => -})) - -vi.mock('@/components/ui/tooltip', () => ({ - Tooltip: ({ children }: { children: React.ReactNode }) => <>{children}, - TooltipTrigger: ({ children }: { children: React.ReactNode }) => <>{children}, - TooltipContent: ({ children }: { children: React.ReactNode }) => <>{children} -})) - -vi.mock('@/components/ui/dialog', () => ({ - Dialog: ({ children, open }: { children: React.ReactNode; open?: boolean }) => - open ?
{children}
: null, - DialogContent: ({ children }: { children: React.ReactNode }) =>
{children}
, - DialogDescription: ({ children }: { children: React.ReactNode }) =>

{children}

, - DialogHeader: ({ children }: { children: React.ReactNode }) =>
{children}
, - DialogTitle: ({ children }: { children: React.ReactNode }) =>

{children}

-})) - -const initialAppState = useAppStore.getInitialState() -let testContainer: HTMLDivElement -let testRoot: Root - -async function flushEffects(): Promise { - await act(async () => { - await Promise.resolve() - await Promise.resolve() - }) -} - -async function churnClosedStore(count: number): Promise { - await act(async () => { - for (let index = 0; index < count; index += 1) { - useAppStore.setState({ activeWorktreeId: `background-${index}` }) - } - }) -} - -function activeContentSubscriptions(): number { - return ( - contentProbe.subscriptions.mock.calls.length - contentProbe.unsubscriptions.mock.calls.length - ) -} - -describe('SetupGuideModal mount gating', () => { - beforeEach(() => { - globalThis.IS_REACT_ACT_ENVIRONMENT = true - vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] }) - contentProbe.renders.mockClear() - contentProbe.storeNotifications.mockClear() - contentProbe.subscriptions.mockClear() - contentProbe.unsubscriptions.mockClear() - contentProbe.telemetryOpenStates.mockClear() - contentProbe.refreshEnabledStates.mockClear() - useAppStore.setState(initialAppState, true) - useAppStore.setState({ activeModal: 'none', activeWorktreeId: null }) - testContainer = document.createElement('div') - document.body.appendChild(testContainer) - testRoot = createRoot(testContainer) - }) - - afterEach(async () => { - await act(async () => testRoot.unmount()) - document.body.replaceChildren() - useAppStore.setState(initialAppState, true) - vi.clearAllTimers() - vi.useRealTimers() - }) - - it('releases setup content after the close animation', async () => { - await act(async () => testRoot.render()) - await flushEffects() - - await churnClosedStore(1_000) - - expect(contentProbe.renders).not.toHaveBeenCalled() - expect(contentProbe.subscriptions).not.toHaveBeenCalled() - expect(contentProbe.storeNotifications).not.toHaveBeenCalled() - - await act(async () => useAppStore.getState().openModal('setup-guide')) - await flushEffects() - - expect(testContainer.querySelector('[data-setup-guide-dialog="true"]')).not.toBeNull() - expect(activeContentSubscriptions()).toBe(1) - - await act(async () => useAppStore.getState().closeModal()) - - expect(testContainer.querySelector('[data-setup-guide-dialog="true"]')).toBeNull() - expect(activeContentSubscriptions()).toBe(1) - expect(contentProbe.telemetryOpenStates).toHaveBeenLastCalledWith(false) - // Retain progress inputs mid-fade so completed rows cannot flash incomplete. - expect(contentProbe.refreshEnabledStates).toHaveBeenLastCalledWith(true) - - await act(async () => vi.advanceTimersByTimeAsync(299)) - expect(activeContentSubscriptions()).toBe(1) - expect(contentProbe.refreshEnabledStates).toHaveBeenLastCalledWith(true) - - await act(async () => vi.advanceTimersByTimeAsync(1)) - expect(activeContentSubscriptions()).toBe(0) - - const rendersAfterUnmount = contentProbe.renders.mock.calls.length - const notificationsAfterUnmount = contentProbe.storeNotifications.mock.calls.length - await churnClosedStore(1_000) - - expect(contentProbe.renders).toHaveBeenCalledTimes(rendersAfterUnmount) - expect(contentProbe.storeNotifications).toHaveBeenCalledTimes(notificationsAfterUnmount) - }) - - it('cancels the pending unmount when reopened during the linger', async () => { - await act(async () => testRoot.render()) - await act(async () => useAppStore.getState().openModal('setup-guide')) - await flushEffects() - - await act(async () => useAppStore.getState().closeModal()) - await act(async () => vi.advanceTimersByTimeAsync(299)) - await act(async () => useAppStore.getState().openModal('setup-guide')) - await act(async () => vi.advanceTimersByTimeAsync(1_000)) - - expect(testContainer.querySelector('[data-setup-guide-dialog="true"]')).not.toBeNull() - expect(activeContentSubscriptions()).toBe(1) - - // An uncanceled first-close timer would collapse this later close and report Interrupted instead of Dismissed. - await act(async () => useAppStore.getState().closeModal()) - expect(activeContentSubscriptions()).toBe(1) - await act(async () => vi.advanceTimersByTimeAsync(299)) - expect(activeContentSubscriptions()).toBe(1) - await act(async () => vi.advanceTimersByTimeAsync(1)) - expect(activeContentSubscriptions()).toBe(0) - }) -}) diff --git a/src/renderer/src/components/setup-guide/SetupGuideModal.tsx b/src/renderer/src/components/setup-guide/SetupGuideModal.tsx index 6641212e929..d4718181966 100644 --- a/src/renderer/src/components/setup-guide/SetupGuideModal.tsx +++ b/src/renderer/src/components/setup-guide/SetupGuideModal.tsx @@ -13,8 +13,8 @@ import { DialogTitle } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' -import { useAppStore } from '@/store' -import { FeatureWallSetupChecklist } from '../feature-wall/FeatureWallSetupChecklist' +import { useAppStore } from '@/store/index' +import { FeatureWallSetupChecklist } from '../feature-wall/setup/FeatureWallSetupChecklist' import { useSetupGuideProgress } from './use-setup-guide-progress' import { useSetupGuideOpenCloseTelemetry } from './use-setup-guide-telemetry' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/setup-guide/setup-guide-browser-milestone-progress.ts b/src/renderer/src/components/setup-guide/setup-guide-browser-milestone-progress.ts index c02f074da67..31f55698d6e 100644 --- a/src/renderer/src/components/setup-guide/setup-guide-browser-milestone-progress.ts +++ b/src/renderer/src/components/setup-guide/setup-guide-browser-milestone-progress.ts @@ -1,10 +1,10 @@ import { useEffect, useMemo } from 'react' -import { useAppStore } from '@/store' +import { useAppStore } from '@/store/index' import { FEATURE_WALL_SETUP_STEPS, type FeatureWallSetupStepId } from '../../../../shared/feature-wall-setup-steps' -import type { FeatureWallSetupProgress } from '../feature-wall/feature-wall-setup-progress' +import type { FeatureWallSetupProgress } from '../feature-wall/setup/feature-wall-setup-progress' export function useSetupGuideBrowserMilestoneProgress( rawProgress: FeatureWallSetupProgress, diff --git a/src/renderer/src/components/setup-guide/use-setup-guide-progress.ts b/src/renderer/src/components/setup-guide/use-setup-guide-progress.ts index 4d7fe6deb47..f8153451941 100644 --- a/src/renderer/src/components/setup-guide/use-setup-guide-progress.ts +++ b/src/renderer/src/components/setup-guide/use-setup-guide-progress.ts @@ -16,9 +16,9 @@ import { useInstalledAgentSkill } from '@/hooks/skills/discovery/useInstalledAgentSkills' import { useOnboardingSkillRuntime } from '@/hooks/skills/runtime/use-onboarding-skill-runtime' -import { getSetupGuideProgressSnapshot } from '../feature-wall/feature-wall-setup-progress' -import type { FeatureWallSetupProgress } from '../feature-wall/feature-wall-setup-progress' -import { deriveIntegrationConnectionStatus } from '../feature-wall/use-integration-connection-status' +import { getSetupGuideProgressSnapshot } from '../feature-wall/setup/feature-wall-setup-progress' +import type { FeatureWallSetupProgress } from '../feature-wall/setup/feature-wall-setup-progress' +import { deriveIntegrationConnectionStatus } from '../feature-wall/setup/integrations/use-integration-connection-status' import { useSetupGuideBrowserMilestoneProgress } from './setup-guide-browser-milestone-progress' import { getComputerUsePermissionSetupState, diff --git a/src/renderer/src/components/setup-guide/use-setup-guide-telemetry.ts b/src/renderer/src/components/setup-guide/use-setup-guide-telemetry.ts index 156c884104c..617fefaca74 100644 --- a/src/renderer/src/components/setup-guide/use-setup-guide-telemetry.ts +++ b/src/renderer/src/components/setup-guide/use-setup-guide-telemetry.ts @@ -6,7 +6,7 @@ import { type FeatureWallSetupStepId } from '../../../../shared/feature-wall-setup-steps' import type { SetupGuideSource } from '../../../../shared/feature-education-telemetry' -import type { FeatureWallSetupProgress } from '../feature-wall/feature-wall-setup-progress' +import type { FeatureWallSetupProgress } from '../feature-wall/setup/feature-wall-setup-progress' import { persistEmittedSetupGuideStepId, readEmittedSetupGuideStepIds, diff --git a/src/renderer/src/components/sidebar/SetupGuideSidebarEntry.tsx b/src/renderer/src/components/sidebar/SetupGuideSidebarEntry.tsx index 0ff2e2f5459..efe735fbaff 100644 --- a/src/renderer/src/components/sidebar/SetupGuideSidebarEntry.tsx +++ b/src/renderer/src/components/sidebar/SetupGuideSidebarEntry.tsx @@ -13,7 +13,7 @@ import { getFirstIncompleteFeatureWallSetupStepId, type FeatureWallSetupStepId } from '../../../../shared/feature-wall-setup-steps' -import type { FeatureWallSetupProgress } from '../feature-wall/feature-wall-setup-progress' +import type { FeatureWallSetupProgress } from '../feature-wall/setup/feature-wall-setup-progress' import { SetupGuideProgressRing } from '../setup-guide/SetupGuideProgressRing' import { useSetupGuideProgress } from '../setup-guide/use-setup-guide-progress' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/shared/wsl-move/wsl-move-storage-dispositions.ts b/src/shared/wsl-move/wsl-move-storage-dispositions.ts index 56f2de6e067..eb7263281f7 100644 --- a/src/shared/wsl-move/wsl-move-storage-dispositions.ts +++ b/src/shared/wsl-move/wsl-move-storage-dispositions.ts @@ -51,8 +51,8 @@ export const LOCAL_STORAGE_MODULE_DISPOSITIONS: Record Date: Thu, 12 Nov 2026 23:23:36 -0800 Subject: [PATCH 034/178] test: remove terminal close and import setter projection suites (#41883) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/reliability-gates.jsonc | 6 +- .../scripts/ci/sharding/ci-shard-timings.json | 2 - .../add-repo-skip-finalization.test.ts | 134 ------- .../terminal/terminal-tab-actions.test.ts | 343 ------------------ 4 files changed, 3 insertions(+), 482 deletions(-) delete mode 100644 src/renderer/src/components/sidebar/project-admission/completion/add-repo-skip-finalization.test.ts delete mode 100644 src/renderer/src/components/terminal/terminal-tab-actions.test.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 2077fe4a6fe..d7e19757b11 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -6396,7 +6396,7 @@ "invariant": "Reconnect, replay, or lifecycle observations from a viewer that negotiated explicit close intent must never kill a live PTY. A capable reasonless close must keep and republish; a legacy paired viewer must retain current-main behavior because its intentional close and cleanup echo are wire-identical. Lifecycle close requires the exact observed publication, terminal, environment, and authoritative liveness, never signals a process, and leaves renderer-owned or partial-split retirement to its owner. Legacy daemon hello and warm reattachment remain non-destructive.", "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3881 intentionally removes the entire legacy E2EE channel and mobile socket-wiring test suites and the v2 additive runtime-capability refusal callback, without equivalent replacement. Those deleted suites leave current selectors and that v2 assertion alias is retired. Retained v2 replay/queue-counter cases do not establish the removed capability refusal. Reduced test bytes/selectors have no fresh execution in this static packet; independent dated daemon E2E receipts retain their complete original scope. Historical prior account (preserved verbatim): Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3774 (independent peer expansion of3773): deliberately retire7authored/13potential expanded registrations across4whole suites: standalone truncated8-byte E2EE guard; open/empty readiness null fast-path; capability parser acceptance/atomic malformed-array bounds; phone/delegated-parent/desktop/in-process creator provenance projections. These are real guard/policy alarms accepted as low marginal value, not tautologies. No equivalent replacement, authenticated-refusal, observer delivery, persisted visibility, synchronous scheduling or fresh execution is claimed. Prior dated receipts retain their exact original scopes and do not validate the changed/reduced current selector. Independent daemon E2E receipts remain scoped to their own unchanged commands. Historical prior account (preserved verbatim): Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1983: Delete the reasonless-close scalar suite and optional-capability socket forwarding alias. Delete the mocked close-attribution topology field-inventory suite and its aliases/selectors; preserve independent dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1959 (sources 1957:11, 1957:35, 1957:36): The fixture-supplied repository roundtrip, isolated capability metadata readback, parser/default-timer/size-threshold/internal failure-counter examples and standalone handshake-only replay example are removed. Source11 retains real encrypted multi-client event ordering, reconnect snapshot and multiplex delivery under synthetic runtime state, and does not establish the broad handshake-to-reasonless-close refusal alias; retire that whole assertion object. Source36 capability metadata readback alias is retired. Source35 retains the actual v2 runtime-only metadata/downgrade refusal callback and captured mutating-trace fresh-nonce rejection; its mapped mobile-v2 refusal alias remains within modeled credential lookup scope. Both independent daemon E2E dated receipts remain byte-exact with their original commands and results. No historical passed receipt is narrowed or transferred to changed source bytes. No fresh execution, equivalent replacement, physical provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed by this metadata packet. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Retain actual parked exit/disposal/cancel/leaf progression using injected exit and close adapters. The retained last-watcher case checks exact lifecyclePtyId, but no retained callback establishes the whole existing replacement-or-sibling-incarnation alias. Retire that broad assertion object rather than transfer partial support. Capability admission, static fields and duplicate routing examples are intentionally lost; unchanged daemon E2E receipts remain scoped to their own commands. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Start isolated v25 and v26 daemon generations with one capable-viewer PTY and one legacy-viewer PTY per generation in four target worktrees plus an unaddressed control PTY in a fifth worktree. Route them through the production desktop scanner, runtime, RPC dispatcher, renderer-close relay, and daemon router. First issue sequential reasonless closes from an authenticated capable connection and require refusal, snapshot republish, zero shutdown calls, exact process survival, and post-close I/O. Then issue the byte-identical requests without the negotiated capability and require current-main behavior: two ordered immediate shutdowns, session-killed events, and exact root/descendant death, while capable and control PTYs survive. An observer lists all targets before, between, and after while issuing zero closes. Unit contracts also require in-process reasonless refusal, legacy runtime/mobile compatibility, explicit user closes, encrypted client-auth advertisement, and old-server lifecycle calls never to fall back.", "commands": [ - "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts src/shared/remote-runtime/remote-runtime-client.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/cli/runtime/websocket-transport.test.ts src/renderer/src/web/web-runtime-client.test.ts src/renderer/src/components/terminal/terminal-tab-actions.test.ts src/renderer/src/components/terminal/terminal-close-incarnation.test.ts src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts src/shared/remote-runtime/remote-runtime-client.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/cli/runtime/websocket-transport.test.ts src/renderer/src/web/web-runtime-client.test.ts src/renderer/src/components/terminal/terminal-close-incarnation.test.ts src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts", "pnpm exec playwright test tests/e2e/daemon-generation-reconnect-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1", "pnpm exec playwright test tests/e2e/daemon-generation-legacy-close-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1" ], @@ -6407,7 +6407,6 @@ "src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts", "src/cli/runtime/websocket-transport.test.ts", "src/renderer/src/web/web-runtime-client.test.ts", - "src/renderer/src/components/terminal/terminal-tab-actions.test.ts", "src/renderer/src/components/terminal/terminal-close-incarnation.test.ts", "src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts", "tests/e2e/daemon-generation-reconnect-safety.spec.ts", @@ -6534,7 +6533,8 @@ "Audit3774 historical original active selector/assertion arrays, preserved verbatim; no replacement coverage claim.\ncommands:\n[\n \"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts src/main/runtime/rpc/mobile/mobile-socket-wiring.test.ts src/main/runtime/rpc/callers/runtime-client-capabilities.test.ts src/shared/remote-runtime/remote-runtime-client.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/cli/runtime/websocket-transport.test.ts src/renderer/src/web/web-runtime-client.test.ts src/renderer/src/components/terminal/terminal-tab-actions.test.ts src/renderer/src/components/terminal/terminal-close-incarnation.test.ts src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts\",\n \"pnpm exec playwright test tests/e2e/daemon-generation-reconnect-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1\",\n \"pnpm exec playwright test tests/e2e/daemon-generation-legacy-close-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1\"\n ]\ntestFiles:\n[\n \"src/main/runtime/construction/orca-runtime.test.ts\",\n \"src/main/runtime/rpc/mobile/e2ee/e2ee-channel.test.ts\",\n \"src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts\",\n \"src/main/runtime/rpc/mobile/mobile-socket-wiring.test.ts\",\n \"src/main/runtime/rpc/callers/runtime-client-capabilities.test.ts\",\n \"src/shared/remote-runtime/remote-runtime-client.test.ts\",\n \"src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts\",\n \"src/cli/runtime/websocket-transport.test.ts\",\n \"src/renderer/src/web/web-runtime-client.test.ts\",\n \"src/renderer/src/components/terminal/terminal-tab-actions.test.ts\",\n \"src/renderer/src/components/terminal/terminal-close-incarnation.test.ts\",\n \"src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts\",\n \"tests/e2e/daemon-generation-reconnect-safety.spec.ts\",\n \"tests/e2e/daemon-generation-legacy-close-safety.spec.ts\"\n ]\nassertionRefs:\n[\n {\n \"file\": \"tests/e2e/daemon-generation-legacy-close-safety.spec.ts\",\n \"assertions\": [\n \"one identified capable viewer and one legacy viewer issue byte-identical sequential reasonless closes while a third viewer lists every target before, between, and after but issues zero closes\",\n \"capable v25/v26 PTY root/descendant incarnations in separate worktrees survive, answer post-close input, and produce zero shutdown calls and zero daemon session-killed events\",\n \"legacy v25/v26 PTY root/descendant incarnations die through ordered immediate shutdown calls with one daemon session-killed event each, matching current-main behavior\",\n \"an unaddressed fifth-worktree PTY root and descendant survive with zero kill events, excluding global fanout\",\n \"the JSON reconstruction records request order, negotiated capabilities, viewer connection, worktree/tab/PTY ids, daemon PID/protocol, call site, and exact before/after process liveness\"\n ]\n },\n {\n \"file\": \"tests/e2e/daemon-generation-reconnect-safety.spec.ts\",\n \"assertions\": [\n \"the production scanner discovers v21/v22/v23/v24/v25 from v26 and every generation accepts repeated client hellos while every exact daemon, PTY-root, and descendant incarnation remains alive\",\n \"desktop and two remote profiles repeat lifecycle closes before and after client relaunch with zero session-killed events\",\n \"shutdown-dispose-failed drops named-pipe authority within the deadline and exact fixture cleanup leaves no process tree\"\n ]\n },\n {\n \"file\": \"src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts\",\n \"assertions\": [\"mobile E2EE v2 continues to reject additive runtime capability metadata\"]\n },\n {\n \"file\": \"src/main/runtime/rpc/callers/runtime-client-capabilities.test.ts\",\n \"assertions\": [\n \"the authenticated capability parser accepts only bounded string arrays and rejects malformed or oversized input\"\n ]\n },\n {\n \"file\": \"src/shared/remote-runtime/remote-runtime-client.test.ts\",\n \"assertions\": [\n \"one-shot and subscription runtime clients remain compatible while sending encrypted client authentication\"\n ]\n }\n ]", "Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed.", "Audit3881 intentionally removes the entire legacy E2EE channel and mobile socket-wiring test suites and the v2 additive runtime-capability refusal callback, without equivalent replacement. Those deleted suites leave current selectors and that v2 assertion alias is retired. Retained v2 replay/queue-counter cases do not establish the removed capability refusal. Reduced test bytes/selectors have no fresh execution in this static packet; independent dated daemon E2E receipts retain their complete original scope.", - "Audit3881 historical original selector/assertion arrays, preserved verbatim; retired entries are not active execution evidence or replacement coverage.\ncommands:\n[\n \"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts src/main/runtime/rpc/mobile/mobile-socket-wiring.test.ts src/shared/remote-runtime/remote-runtime-client.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/cli/runtime/websocket-transport.test.ts src/renderer/src/web/web-runtime-client.test.ts src/renderer/src/components/terminal/terminal-tab-actions.test.ts src/renderer/src/components/terminal/terminal-close-incarnation.test.ts src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts\",\n \"pnpm exec playwright test tests/e2e/daemon-generation-reconnect-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1\",\n \"pnpm exec playwright test tests/e2e/daemon-generation-legacy-close-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1\"\n ]\ntestFiles:\n[\n \"src/main/runtime/construction/orca-runtime.test.ts\",\n \"src/main/runtime/rpc/mobile/e2ee/e2ee-channel.test.ts\",\n \"src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts\",\n \"src/main/runtime/rpc/mobile/mobile-socket-wiring.test.ts\",\n \"src/shared/remote-runtime/remote-runtime-client.test.ts\",\n \"src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts\",\n \"src/cli/runtime/websocket-transport.test.ts\",\n \"src/renderer/src/web/web-runtime-client.test.ts\",\n \"src/renderer/src/components/terminal/terminal-tab-actions.test.ts\",\n \"src/renderer/src/components/terminal/terminal-close-incarnation.test.ts\",\n \"src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts\",\n \"tests/e2e/daemon-generation-reconnect-safety.spec.ts\",\n \"tests/e2e/daemon-generation-legacy-close-safety.spec.ts\"\n ]\nassertionRefs:\n[\n {\n \"file\": \"tests/e2e/daemon-generation-legacy-close-safety.spec.ts\",\n \"assertions\": [\n \"one identified capable viewer and one legacy viewer issue byte-identical sequential reasonless closes while a third viewer lists every target before, between, and after but issues zero closes\",\n \"capable v25/v26 PTY root/descendant incarnations in separate worktrees survive, answer post-close input, and produce zero shutdown calls and zero daemon session-killed events\",\n \"legacy v25/v26 PTY root/descendant incarnations die through ordered immediate shutdown calls with one daemon session-killed event each, matching current-main behavior\",\n \"an unaddressed fifth-worktree PTY root and descendant survive with zero kill events, excluding global fanout\",\n \"the JSON reconstruction records request order, negotiated capabilities, viewer connection, worktree/tab/PTY ids, daemon PID/protocol, call site, and exact before/after process liveness\"\n ]\n },\n {\n \"file\": \"tests/e2e/daemon-generation-reconnect-safety.spec.ts\",\n \"assertions\": [\n \"the production scanner discovers v21/v22/v23/v24/v25 from v26 and every generation accepts repeated client hellos while every exact daemon, PTY-root, and descendant incarnation remains alive\",\n \"desktop and two remote profiles repeat lifecycle closes before and after client relaunch with zero session-killed events\",\n \"shutdown-dispose-failed drops named-pipe authority within the deadline and exact fixture cleanup leaves no process tree\"\n ]\n },\n {\n \"file\": \"src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts\",\n \"assertions\": [\"mobile E2EE v2 continues to reject additive runtime capability metadata\"]\n },\n {\n \"file\": \"src/shared/remote-runtime/remote-runtime-client.test.ts\",\n \"assertions\": [\n \"one-shot and subscription runtime clients remain compatible while sending encrypted client authentication\"\n ]\n }\n ]" + "Audit3881 historical original selector/assertion arrays, preserved verbatim; retired entries are not active execution evidence or replacement coverage.\ncommands:\n[\n \"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts src/main/runtime/rpc/mobile/mobile-socket-wiring.test.ts src/shared/remote-runtime/remote-runtime-client.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/cli/runtime/websocket-transport.test.ts src/renderer/src/web/web-runtime-client.test.ts src/renderer/src/components/terminal/terminal-tab-actions.test.ts src/renderer/src/components/terminal/terminal-close-incarnation.test.ts src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts\",\n \"pnpm exec playwright test tests/e2e/daemon-generation-reconnect-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1\",\n \"pnpm exec playwright test tests/e2e/daemon-generation-legacy-close-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1\"\n ]\ntestFiles:\n[\n \"src/main/runtime/construction/orca-runtime.test.ts\",\n \"src/main/runtime/rpc/mobile/e2ee/e2ee-channel.test.ts\",\n \"src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts\",\n \"src/main/runtime/rpc/mobile/mobile-socket-wiring.test.ts\",\n \"src/shared/remote-runtime/remote-runtime-client.test.ts\",\n \"src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts\",\n \"src/cli/runtime/websocket-transport.test.ts\",\n \"src/renderer/src/web/web-runtime-client.test.ts\",\n \"src/renderer/src/components/terminal/terminal-tab-actions.test.ts\",\n \"src/renderer/src/components/terminal/terminal-close-incarnation.test.ts\",\n \"src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts\",\n \"tests/e2e/daemon-generation-reconnect-safety.spec.ts\",\n \"tests/e2e/daemon-generation-legacy-close-safety.spec.ts\"\n ]\nassertionRefs:\n[\n {\n \"file\": \"tests/e2e/daemon-generation-legacy-close-safety.spec.ts\",\n \"assertions\": [\n \"one identified capable viewer and one legacy viewer issue byte-identical sequential reasonless closes while a third viewer lists every target before, between, and after but issues zero closes\",\n \"capable v25/v26 PTY root/descendant incarnations in separate worktrees survive, answer post-close input, and produce zero shutdown calls and zero daemon session-killed events\",\n \"legacy v25/v26 PTY root/descendant incarnations die through ordered immediate shutdown calls with one daemon session-killed event each, matching current-main behavior\",\n \"an unaddressed fifth-worktree PTY root and descendant survive with zero kill events, excluding global fanout\",\n \"the JSON reconstruction records request order, negotiated capabilities, viewer connection, worktree/tab/PTY ids, daemon PID/protocol, call site, and exact before/after process liveness\"\n ]\n },\n {\n \"file\": \"tests/e2e/daemon-generation-reconnect-safety.spec.ts\",\n \"assertions\": [\n \"the production scanner discovers v21/v22/v23/v24/v25 from v26 and every generation accepts repeated client hellos while every exact daemon, PTY-root, and descendant incarnation remains alive\",\n \"desktop and two remote profiles repeat lifecycle closes before and after client relaunch with zero session-killed events\",\n \"shutdown-dispose-failed drops named-pipe authority within the deadline and exact fixture cleanup leaves no process tree\"\n ]\n },\n {\n \"file\": \"src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts\",\n \"assertions\": [\"mobile E2EE v2 continues to reject additive runtime capability metadata\"]\n },\n {\n \"file\": \"src/shared/remote-runtime/remote-runtime-client.test.ts\",\n \"assertions\": [\n \"one-shot and subscription runtime clients remain compatible while sending encrypted client authentication\"\n ]\n }\n ]", + "Audit4389 retires the entire terminal-tab-actions mock-projection suite (8 authored/10 finite cases). Real local/host mutation, confirmation settlement and transport are mocked; routing/reason/pin/sibling spy alarms intentionally lost, with no replacement or equivalence claim. Historical entries remain archival only. Original active command: pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts src/shared/remote-runtime/remote-runtime-client.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/cli/runtime/websocket-transport.test.ts src/renderer/src/web/web-runtime-client.test.ts src/renderer/src/components/terminal/terminal-tab-actions.test.ts src/renderer/src/components/terminal/terminal-close-incarnation.test.ts src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts" ], "demotionRule": "Keep experimental or quarantine if reconnect emits session-killed for a live canary, an exact root/descendant dies, an old server receives fallback destructive close, cleanup leaks a fixture process/pipe, or the gate flakes without a proven harness defect." }, diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index b728d8b7fe5..97e7cb34c64 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -4990,7 +4990,6 @@ "src/renderer/src/components/sidebar/workspace-card/identity/WorktreeTitleInlineRename.editor-lifecycle.test.tsx": 407, "src/renderer/src/components/sidebar/workspace-visibility/dialog/WorktreeVisibilityDialog.test.tsx": 2602, "src/renderer/src/components/sidebar/host-admission/ssh/add-remote-host-ssh-actions.test.ts": 232, - "src/renderer/src/components/sidebar/project-admission/completion/add-repo-skip-finalization.test.ts": 59, "src/renderer/src/components/sidebar/chat-markdown-math-typeset.test.ts": 1336, "src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-always-force-preference.test.tsx": 2175, "src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-dirty-change-counts.test.ts": 258, @@ -5372,7 +5371,6 @@ "src/renderer/src/components/terminal/terminal-close-incarnation.test.ts": 42, "src/renderer/src/components/terminal/terminal-tab-actions-unified-close.test.ts": 1831, "src/renderer/src/components/terminal/terminal-tab-actions-unrouted-close.test.ts": 589, - "src/renderer/src/components/terminal/terminal-tab-actions.test.ts": 626, "src/renderer/src/components/terminal/terminal-tab-close-running-confirm.test.ts": 499, "src/renderer/src/components/terminal/use-terminal-provider-snapshot-capability.test.tsx": 573, "src/renderer/src/components/terminal/window-close-running-work.test.ts": 97, diff --git a/src/renderer/src/components/sidebar/project-admission/completion/add-repo-skip-finalization.test.ts b/src/renderer/src/components/sidebar/project-admission/completion/add-repo-skip-finalization.test.ts deleted file mode 100644 index e85a10ded0c..00000000000 --- a/src/renderer/src/components/sidebar/project-admission/completion/add-repo-skip-finalization.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import type { Worktree } from '../../../../../../shared/worktree/types' -import { - finalizeImportedRepoAfterSkip, - type AddRepoSkipFinalizationState -} from './add-repo-skip-finalization' - -function makeWorktree(overrides: Partial & { id: string; repoId: string }): Worktree { - return { - path: `/tmp/${overrides.id}`, - head: 'abc123', - branch: 'refs/heads/feature', - isBare: false, - isMainWorktree: false, - displayName: overrides.id, - comment: '', - linkedIssue: null, - linkedPR: null, - linkedLinearIssue: null, - linkedGitLabMR: null, - linkedGitLabIssue: null, - isArchived: false, - isUnread: false, - isPinned: false, - sortOrder: 0, - lastActivityAt: 0, - ...overrides - } -} - -function makeState(overrides: Partial): AddRepoSkipFinalizationState { - return { - activeRepoId: null, - filterRepoIds: [], - showActiveOnly: false, - hideDefaultBranchWorkspace: false, - showSleepingWorkspaces: true, - alwaysShowDefaultBranchWorkspace: true, - repos: [], - worktreesByRepo: {}, - setActiveRepo: vi.fn(), - setFilterRepoIds: vi.fn(), - setShowActiveOnly: vi.fn(), - setHideDefaultBranchWorkspace: vi.fn(), - setAlwaysShowDefaultBranchWorkspace: vi.fn(), - ...overrides - } -} - -describe('finalizeImportedRepoAfterSkip', () => { - it('preserves default-branch hiding when importing a folder project with only its root', () => { - const state = makeState({ - hideDefaultBranchWorkspace: true, - repos: [{ id: 'folder-new', kind: 'folder' }], - worktreesByRepo: { - 'folder-new': [ - makeWorktree({ - id: 'folder-new::/notes', - repoId: 'folder-new', - isMainWorktree: true, - head: '', - branch: '' - }) - ] - } - }) - - finalizeImportedRepoAfterSkip(state, 'folder-new') - - expect(state.setHideDefaultBranchWorkspace).not.toHaveBeenCalled() - }) - - it.each([false, true])( - 'ignores archived siblings when revealing a folder root after Skip (root archived: %s)', - (rootArchived) => { - const state = makeState({ - hideDefaultBranchWorkspace: true, - showSleepingWorkspaces: false, - alwaysShowDefaultBranchWorkspace: false, - repos: [{ id: 'folder-new', kind: 'folder' }], - worktreesByRepo: { - 'folder-new': [ - makeWorktree({ - id: 'folder-new::/notes', - repoId: 'folder-new', - isMainWorktree: true, - head: '', - branch: '', - isArchived: rootArchived - }), - makeWorktree({ - id: 'folder-new::/notes::workspace:archived', - repoId: 'folder-new', - head: '', - branch: '', - isArchived: true - }) - ] - } - }) - - finalizeImportedRepoAfterSkip(state, 'folder-new') - - if (rootArchived) { - expect(state.setHideDefaultBranchWorkspace).not.toHaveBeenCalled() - expect(state.setAlwaysShowDefaultBranchWorkspace).not.toHaveBeenCalled() - } else { - expect(state.setHideDefaultBranchWorkspace).not.toHaveBeenCalled() - expect(state.setAlwaysShowDefaultBranchWorkspace).toHaveBeenCalledWith(true) - } - } - ) - - it('re-enables the default-branch exemption when the import would land asleep and hidden', () => { - const state = makeState({ - showSleepingWorkspaces: false, - alwaysShowDefaultBranchWorkspace: false, - worktreesByRepo: { - 'repo-new': [ - makeWorktree({ - id: 'repo-new::/repo/main', - repoId: 'repo-new', - isMainWorktree: true, - branch: 'refs/heads/main' - }) - ] - } - }) - - finalizeImportedRepoAfterSkip(state, 'repo-new') - - expect(state.setAlwaysShowDefaultBranchWorkspace).toHaveBeenCalledWith(true) - }) -}) diff --git a/src/renderer/src/components/terminal/terminal-tab-actions.test.ts b/src/renderer/src/components/terminal/terminal-tab-actions.test.ts deleted file mode 100644 index 098c0bf391a..00000000000 --- a/src/renderer/src/components/terminal/terminal-tab-actions.test.ts +++ /dev/null @@ -1,343 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' - -const { - activateWebRuntimeSessionTabMock, - createBrowserUuidMock, - closeWebRuntimeSessionTabMock, - createWebRuntimeSessionTerminalMock, - getLatestWebSessionTabsPublicationEpochMock, - getStateMock, - isWebRuntimeSessionActiveMock, - isWebTerminalSurfaceTabIdMock, - resolveHostSessionTabIdForWebSessionTabMock, - toHostSessionTabIdMock -} = vi.hoisted(() => ({ - activateWebRuntimeSessionTabMock: vi.fn(), - createBrowserUuidMock: vi.fn(() => 'tab-1'), - closeWebRuntimeSessionTabMock: vi.fn(), - createWebRuntimeSessionTerminalMock: vi.fn(), - getLatestWebSessionTabsPublicationEpochMock: vi.fn(() => 'epoch-1'), - getStateMock: vi.fn(), - isWebRuntimeSessionActiveMock: vi.fn(), - isWebTerminalSurfaceTabIdMock: vi.fn(() => false), - resolveHostSessionTabIdForWebSessionTabMock: vi.fn<() => string | null>(() => null), - toHostSessionTabIdMock: vi.fn((tabId: string) => tabId) -})) - -vi.mock('@/store', () => ({ - useAppStore: { - getState: getStateMock - } -})) - -vi.mock('../../../../shared/non-secure-context-uuid', () => ({ - createNonSecureContextUuid: createBrowserUuidMock -})) - -vi.mock('@/runtime/web-session-tabs-sync/actions/web-runtime-session-tab-lifecycle', () => ({ - activateWebRuntimeSessionTab: activateWebRuntimeSessionTabMock, - closeWebRuntimeSessionTab: closeWebRuntimeSessionTabMock -})) -vi.mock('@/runtime/hosted-terminals/creation/web-runtime-terminal-creation', () => ({ - createWebRuntimeSessionTerminal: createWebRuntimeSessionTerminalMock -})) -vi.mock('@/runtime/web-session-tabs-sync/identity/web-runtime-session-environment', () => ({ - isWebRuntimeSessionActive: isWebRuntimeSessionActiveMock -})) -vi.mock('../../../../shared/workspace-layout/terminal-surface-id', () => ({ - isWebTerminalSurfaceTabId: isWebTerminalSurfaceTabIdMock, - toHostSessionTabId: toHostSessionTabIdMock -})) - -vi.mock('@/runtime/web-session-tabs-sync/tracking-lifecycle', () => ({ - getLatestWebSessionTabsPublicationEpoch: getLatestWebSessionTabsPublicationEpochMock -})) -vi.mock('@/runtime/web-session-tabs-sync/tracking-mappings', () => ({ - resolveHostSessionTabIdForWebSessionTab: resolveHostSessionTabIdForWebSessionTabMock -})) - -import { closeOtherTerminalTabs, closeTerminalTab } from './terminal-tab-actions' -import { terminalOwnerRows } from './terminal-tab-owner-rows.test-fixture' - -describe('closeTerminalTab', () => { - beforeEach(() => { - vi.clearAllMocks() - isWebRuntimeSessionActiveMock.mockReturnValue(false) - resolveHostSessionTabIdForWebSessionTabMock.mockReturnValue(null) - isWebTerminalSurfaceTabIdMock.mockReturnValue(false) - }) - - it('lets the HUB snapshot adjudicate a stream exit', () => { - const closeTab = vi.fn() - isWebRuntimeSessionActiveMock.mockReturnValue(true) - resolveHostSessionTabIdForWebSessionTabMock.mockReturnValue('host-tab-1') - getStateMock.mockReturnValue({ - ...terminalOwnerRows('web-runtime'), - tabsByWorktree: { - 'wt-1': [{ id: 'local-tab-1' }, { id: 'local-tab-2' }] - }, - activeWorktreeId: 'wt-1', - activeTabId: 'local-tab-1', - closeTab, - setActiveTab: vi.fn() - }) - - closeTerminalTab('local-tab-1', { - reason: 'pty-exit', - lifecyclePtyId: 'remote:web-runtime@@term-1' - }) - - expect(closeTab).not.toHaveBeenCalled() - expect(closeWebRuntimeSessionTabMock).not.toHaveBeenCalled() - }) - - it('sends hostCloseReason on the wire without tagging the local close reason', () => { - // Lifecycle close reason must reach host adjudication while local guards still use the original reason. - const closeTab = vi.fn() - isWebRuntimeSessionActiveMock.mockReturnValue(true) - resolveHostSessionTabIdForWebSessionTabMock.mockReturnValue('host-tab-1') - getStateMock.mockReturnValue({ - ...terminalOwnerRows('web-runtime'), - tabsByWorktree: { - 'wt-1': [{ id: 'local-tab-1' }, { id: 'local-tab-2' }] - }, - activeWorktreeId: 'wt-1', - activeTabId: 'local-tab-1', - closeTab, - setActiveTab: vi.fn() - }) - - closeTerminalTab('local-tab-1', { - hostCloseReason: 'pty-exit', - lifecyclePtyId: 'remote:web-runtime@@term-1' - }) - - expect(closeTab).toHaveBeenCalledWith('local-tab-1', { - reason: undefined, - remoteCloseOwnedByHost: true - }) - expect(closeWebRuntimeSessionTabMock).toHaveBeenCalledWith({ - worktreeId: 'wt-1', - tabId: 'host-tab-1', - environmentId: 'web-runtime', - reason: 'pty-exit', - publicationEpoch: 'epoch-1', - terminalHandle: 'term-1' - }) - }) - - it.each([ - ['user', 'user'], - ['requested', 'user'], - ['cleanup', 'cleanup'] - ] as const)( - 'sends a %s close to a paired host as %s, so only declared intent is destructive (#37245)', - (reason, wireReason) => { - isWebRuntimeSessionActiveMock.mockReturnValue(true) - resolveHostSessionTabIdForWebSessionTabMock.mockReturnValue('host-tab-1') - getStateMock.mockReturnValue({ - ...terminalOwnerRows('web-runtime'), - tabsByWorktree: { 'wt-1': [{ id: 'local-tab-1' }, { id: 'local-tab-2' }] }, - activeWorktreeId: 'wt-1', - activeTabId: 'local-tab-1', - closeTab: vi.fn(), - setActiveTab: vi.fn() - }) - - closeTerminalTab('local-tab-1', { reason, skipRunningProcessConfirm: true }) - - expect(closeWebRuntimeSessionTabMock).toHaveBeenCalledWith( - expect.objectContaining({ tabId: 'host-tab-1', reason: wireReason }) - ) - } - ) - - it('keeps the pinned confirmation guard for a hostCloseReason pty-exit close', () => { - const requestPinnedTabCloseConfirm = vi.fn() - const closeUnifiedTab = vi.fn() - getStateMock.mockReturnValue( - makePinnedTabState({ - confirmClosePinnedTab: true, - requestPinnedTabCloseConfirm, - closeUnifiedTab - }) - ) - - closeTerminalTab('pinned-entity-1', { hostCloseReason: 'pty-exit' }) - - expect(closeUnifiedTab).not.toHaveBeenCalled() - expect(requestPinnedTabCloseConfirm).toHaveBeenCalledTimes(1) - }) - - it('closes unified-only terminal tabs when tabsByWorktree is missing the row', () => { - const closeTab = vi.fn() - const closeUnifiedTab = vi.fn() - getStateMock.mockReturnValue({ - ...terminalOwnerRows(null), - tabsByWorktree: {}, - unifiedTabsByWorktree: { - 'wt-1': [ - { - id: 'unified-tab-1', - entityId: 'terminal-entity-1', - contentType: 'terminal', - groupId: 'group-1', - worktreeId: 'wt-1', - label: 'Claude', - customLabel: null, - color: null, - sortOrder: 0, - createdAt: 0, - isPreview: false, - isPinned: false - } - ] - }, - activeWorktreeId: 'wt-1', - activeTabId: 'terminal-entity-1', - openFiles: [], - browserTabsByWorktree: {}, - reconcileWorktreeTabModel: vi.fn(() => ({ renderableTabCount: 0 })), - closeTab, - closeUnifiedTab, - setActiveTab: vi.fn(), - setActiveWorktree: vi.fn() - }) - - closeTerminalTab('terminal-entity-1', { reason: 'user' }) - - expect(closeTab).toHaveBeenCalledWith('terminal-entity-1', { reason: 'user' }) - expect(closeUnifiedTab).not.toHaveBeenCalled() - }) - - it('routes closes on a remote worktree to the host even when the local→host map has no entry', () => { - // Decode unmapped plain-UUID host tabs too, or the next authoritative snapshot resurrects a locally closed tab. - const closeTab = vi.fn() - isWebRuntimeSessionActiveMock.mockReturnValue(true) - resolveHostSessionTabIdForWebSessionTabMock.mockReturnValue(null) - getStateMock.mockReturnValue({ - ...terminalOwnerRows('web-runtime'), - tabsByWorktree: { - 'wt-1': [{ id: 'plain-uuid-tab' }, { id: 'local-tab-2' }] - }, - activeWorktreeId: 'wt-1', - activeTabId: 'plain-uuid-tab', - openFiles: [], - closeTab, - setActiveTab: vi.fn() - }) - - closeTerminalTab('plain-uuid-tab', { reason: 'user' }) - - expect(closeTab).toHaveBeenCalledWith('plain-uuid-tab', { - reason: 'user', - remoteCloseOwnedByHost: true - }) - expect(closeWebRuntimeSessionTabMock).toHaveBeenCalledWith({ - worktreeId: 'wt-1', - tabId: 'plain-uuid-tab', - environmentId: 'web-runtime', - reason: 'user' - }) - }) - - function makePinnedTabState( - overrides: { confirmClosePinnedTab: boolean } & Record - ): Record { - const { confirmClosePinnedTab, ...rest } = overrides - return { - settings: { activeRuntimeEnvironmentId: null, confirmClosePinnedTab }, - ...terminalOwnerRows(null), - tabsByWorktree: {}, - unifiedTabsByWorktree: { - 'wt-1': [ - { - id: 'unified-pinned-1', - entityId: 'pinned-entity-1', - contentType: 'terminal', - groupId: 'group-1', - worktreeId: 'wt-1', - label: 'Server', - generatedLabel: null, - customLabel: null, - color: null, - sortOrder: 0, - createdAt: 0, - isPreview: false, - isPinned: true - } - ] - }, - activeWorktreeId: 'wt-1', - activeTabId: 'pinned-entity-1', - openFiles: [], - browserTabsByWorktree: {}, - reconcileWorktreeTabModel: vi.fn(() => ({ renderableTabCount: 0 })), - closeTab: vi.fn(), - closeUnifiedTab: vi.fn(), - setActiveTab: vi.fn(), - setActiveWorktree: vi.fn(), - requestPinnedTabCloseConfirm: vi.fn(), - ...rest - } - } - - it('guards a pinned tab closed by its unified id (workspace overlay path)', () => { - const requestPinnedTabCloseConfirm = vi.fn() - const closeUnifiedTab = vi.fn() - getStateMock.mockReturnValue( - makePinnedTabState({ - confirmClosePinnedTab: true, - requestPinnedTabCloseConfirm, - closeUnifiedTab - }) - ) - - // Overlay close supplies the unified ID, so pin detection must recognize it. - closeTerminalTab('unified-pinned-1', { reason: 'user' }) - - expect(closeUnifiedTab).not.toHaveBeenCalled() - expect(requestPinnedTabCloseConfirm).toHaveBeenCalledTimes(1) - }) -}) - -describe('closeOtherTerminalTabs', () => { - beforeEach(() => { - vi.clearAllMocks() - isWebRuntimeSessionActiveMock.mockReturnValue(false) - }) - - it('delegates other terminal closes to the host runtime in paired web clients', () => { - const setActiveTab = vi.fn() - const closeTab = vi.fn() - isWebRuntimeSessionActiveMock.mockReturnValue(true) - getStateMock.mockReturnValue({ - ...terminalOwnerRows('web-runtime'), - tabsByWorktree: { - 'wt-1': [{ id: 'keep' }, { id: 'close-a' }, { id: 'close-b' }] - }, - setActiveTab, - closeTab - }) - - closeOtherTerminalTabs('keep', 'wt-1') - - expect(setActiveTab).toHaveBeenCalledWith('keep') - expect(closeWebRuntimeSessionTabMock).toHaveBeenCalledTimes(2) - expect(closeWebRuntimeSessionTabMock).toHaveBeenCalledWith({ - worktreeId: 'wt-1', - tabId: 'close-a', - environmentId: 'web-runtime', - reason: 'user' - }) - expect(closeWebRuntimeSessionTabMock).toHaveBeenCalledWith({ - worktreeId: 'wt-1', - tabId: 'close-b', - environmentId: 'web-runtime', - reason: 'user' - }) - expect(closeTab).toHaveBeenCalledTimes(2) - expect(closeTab).toHaveBeenNthCalledWith(1, 'close-a', { remoteCloseOwnedByHost: true }) - expect(closeTab).toHaveBeenNthCalledWith(2, 'close-b', { remoteCloseOwnedByHost: true }) - }) -}) From 3beb5b58ae7627b33e60f0dc94a120e1541a73e7 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:24:38 -0800 Subject: [PATCH 035/178] perf(orchestration): a paired server's pull loop checks only its own rows each pull (STA-15102) (#41834) peerMailRelayWants ran every pull-loop iteration and rebuilt the global set of servers (SELECT DISTINCT over every remote_run_links row, closed history included, plus home conversations) to answer one boolean for one server. It now asks about that server only, each an index seek on existing indexes. --- .../db/federation/peer-mail-store.ts | 13 +++++++++++++ .../peer-mail/peer-mail-relay-environments.ts | 17 +++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/runtime/orchestration/db/federation/peer-mail-store.ts b/src/main/runtime/orchestration/db/federation/peer-mail-store.ts index 28948462d3b..4fc7015d837 100644 --- a/src/main/runtime/orchestration/db/federation/peer-mail-store.ts +++ b/src/main/runtime/orchestration/db/federation/peer-mail-store.ts @@ -75,6 +75,17 @@ export function listHomePeerMailEnvironments(this: OrchestrationDb): string[] { return rows.map((row) => row.environment_id) } +/** One server's check, for its pull loop: a seek on that server's home rows, not every server's. */ +export function hasHomePeerMailEnvironment(this: OrchestrationDb, environmentId: string): boolean { + return ( + this.db + .prepare( + `SELECT 1 FROM peer_mail_conversations WHERE role = 'home' AND environment_id = ? LIMIT 1` + ) + .get(environmentId) !== undefined + ) +} + /** Conversations a paired Orca opened here that hold replies it has not acknowledged yet. */ export function listPeerConversationIdsWithPendingReplies( this: OrchestrationDb, @@ -178,6 +189,7 @@ export type PeerMailStoreMethods = { openHomePeerMailConversation: typeof openHomePeerMailConversation getPeerMailConversation: typeof getPeerMailConversation listHomePeerMailEnvironments: typeof listHomePeerMailEnvironments + hasHomePeerMailEnvironment: typeof hasHomePeerMailEnvironment listPeerConversationIdsWithPendingReplies: typeof listPeerConversationIdsWithPendingReplies getPeerMailMessage: typeof getPeerMailMessage isPeerMailSenderLabel: typeof isPeerMailSenderLabel @@ -189,6 +201,7 @@ export function attachPeerMailStore(ctor: { prototype: object }): void { openHomePeerMailConversation, getPeerMailConversation, listHomePeerMailEnvironments, + hasHomePeerMailEnvironment, listPeerConversationIdsWithPendingReplies, getPeerMailMessage, isPeerMailSenderLabel, diff --git a/src/main/runtime/orchestration/federation/peer-mail/peer-mail-relay-environments.ts b/src/main/runtime/orchestration/federation/peer-mail/peer-mail-relay-environments.ts index 8998d0a362c..d3cc5c2ea91 100644 --- a/src/main/runtime/orchestration/federation/peer-mail/peer-mail-relay-environments.ts +++ b/src/main/runtime/orchestration/federation/peer-mail/peer-mail-relay-environments.ts @@ -6,11 +6,24 @@ export function peerMailRelayEnvironments(db: OrchestrationDb): Set { return new Set([...db.listHomePeerMailEnvironments(), ...listLiveRemoteRunLinkEnvironments(db)]) } -/** Whether a server's pull loop still has a reason to run; a store read error keeps it running. */ +/** Whether a server's pull loop still has a reason to run; a store read error keeps it running. Runs + * every pull, so it asks about this one server only: closed links' history never enters it. */ export function peerMailRelayWants(db: OrchestrationDb, environmentId: string): boolean { try { - return peerMailRelayEnvironments(db).has(environmentId) + return db.hasHomePeerMailEnvironment(environmentId) || hasLiveRemoteRunLink(db, environmentId) } catch { return true } } + +/** A seek on this server's own (environment_id, state) index rows, however many closed links it keeps. */ +function hasLiveRemoteRunLink(db: OrchestrationDb, environmentId: string): boolean { + return ( + db.db + .prepare( + `SELECT 1 FROM remote_run_links + WHERE environment_id = ? AND state IN ('open', 'cancel_owed') LIMIT 1` + ) + .get(environmentId) !== undefined + ) +} From 047e2c4114579548733b55efb5aaf45aacbfbdb2 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:31:09 -0800 Subject: [PATCH 036/178] fix(wsl): write the Claude account pointer through stdin, not an empty argument (#41869) wsl.exe --exec refuses an empty argument with Wsl/Service/E_INVALIDARG. When a managed WSL Claude account exists but the distro is on System default, the pointer content is the empty string, so every Claude launch in that distro failed before its terminal spawned. This hits both Windows-driven WSL panes and WSL hosts, whose account env is prepared by the desktop. Fixes #41160 --- .../wsl/claude-profile-wsl-router.test.ts | 18 +++++++++++++++++- .../profiles/wsl/claude-profile-wsl-router.ts | 7 ++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/main/claude-accounts/profiles/wsl/claude-profile-wsl-router.test.ts b/src/main/claude-accounts/profiles/wsl/claude-profile-wsl-router.test.ts index 863eaa8ff15..972f601bd11 100644 --- a/src/main/claude-accounts/profiles/wsl/claude-profile-wsl-router.test.ts +++ b/src/main/claude-accounts/profiles/wsl/claude-profile-wsl-router.test.ts @@ -45,8 +45,13 @@ vi.mock('../../../wsl/execution/wsl-runner', () => ({ if (guest.failure) { return { ...guest.failure, stderr: '', timedOut: false } } + // Real wsl.exe --exec refuses an empty argument before the guest runs. + if (spec.args?.some((arg) => arg === '')) { + return { code: 1, stdout: '', stderr: 'Wsl/Service/E_INVALIDARG', timedOut: false } + } const result = spawnSync('/bin/sh', ['-c', spec.script ?? '', 'sh', ...(spec.args ?? [])], { - encoding: 'utf8' + encoding: 'utf8', + input: spec.input }) return { code: result.status, stdout: result.stdout, stderr: result.stderr, timedOut: false } } @@ -141,6 +146,17 @@ describe.skipIf(!posixHost)('ClaudeWslProfileRouter', () => { expect(readFileSync(f.pointer, 'utf8')).toBe(homeB) }) + it('launches on System default while a managed WSL account still exists', async () => { + const f = fixture() + mkdirSync(join(f.pointer, '..'), { recursive: true }) + writeFileSync(f.pointer, f.profileHome) + f.wsl.Ubuntu = null + await expect(f.router.prepareLaunch('Ubuntu')).resolves.toMatchObject({ + provenance: 'wsl:Ubuntu:system' + }) + expect(readFileSync(f.pointer, 'utf8')).toBe('') + }) + it("runs an account with no login of its own on the guest's ~/.claude while that is signed in to it", async () => { const f = fixture() mkdirSync(f.profileHome, { recursive: true }) diff --git a/src/main/claude-accounts/profiles/wsl/claude-profile-wsl-router.ts b/src/main/claude-accounts/profiles/wsl/claude-profile-wsl-router.ts index 4964813369c..42fb95f7571 100644 --- a/src/main/claude-accounts/profiles/wsl/claude-profile-wsl-router.ts +++ b/src/main/claude-accounts/profiles/wsl/claude-profile-wsl-router.ts @@ -262,10 +262,11 @@ async function writePointer(distro: string, pointer: string, home: string): Prom if (current === home) { return } + // wsl.exe --exec rejects an empty argument (E_INVALIDARG), and System default writes ''; pass content on stdin. await runGuest(distro, { - script: - 'umask 077; mkdir -p -- "${1%/*}" && printf %s "$2" > "$1.tmp" && mv -f -- "$1.tmp" "$1"', - args: [pointer, home], + script: 'umask 077; mkdir -p -- "${1%/*}" && cat > "$1.tmp" && mv -f -- "$1.tmp" "$1"', + args: [pointer], + input: home, loginPath: 'none' }) } From b65607914245f838821dbec2b395251817d84e65 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:34:34 -0800 Subject: [PATCH 037/178] fix(wsl-hosts): skip the running-distro probe when no WSL distro is registered (#41868) Without WSL installed, Windows' wsl.exe stub opens the installer and a UAC prompt. The WSL host distro list read the registry and ran wsl.exe --list --running in parallel, so opening Add WSL host (and every window refocus while it was open) spawned wsl.exe even with nothing to annotate. Read the registry first and only probe running state when a user distro is registered. Refs #40771 Co-authored-by: Claude --- .../wsl-host-distro-list.test.ts | 51 +++++++++++++++++++ .../host-lifecycle/wsl-host-distro-list.ts | 15 +++--- 2 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 src/main/wsl/host-lifecycle/wsl-host-distro-list.test.ts diff --git a/src/main/wsl/host-lifecycle/wsl-host-distro-list.test.ts b/src/main/wsl/host-lifecycle/wsl-host-distro-list.test.ts new file mode 100644 index 00000000000..32528ce113a --- /dev/null +++ b/src/main/wsl/host-lifecycle/wsl-host-distro-list.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, it, vi } from 'vitest' +import { listWslHostDistros } from './wsl-host-distro-list' + +describe('listWslHostDistros', () => { + it('never reads the running list when the registry cannot be read (no WSL installed)', async () => { + const readRunning = vi.fn() + await expect( + listWslHostDistros({ readRegistrations: async () => null, readRunning }) + ).resolves.toEqual([]) + expect(readRunning).not.toHaveBeenCalled() + }) + + it('never reads the running list when only internal distros are registered', async () => { + const readRunning = vi.fn() + await expect( + listWslHostDistros({ + readRegistrations: async () => [{ id: 'a', distro: 'docker-desktop', version: 2 }], + readRunning + }) + ).resolves.toEqual([]) + expect(readRunning).not.toHaveBeenCalled() + }) + + it('marks user distros running, stopped, or unverifiable from the running list', async () => { + const readRegistrations = async () => [ + { id: 'b', distro: 'Ubuntu', version: 2 as const }, + { id: 'a', distro: 'Debian', version: 1 as const }, + { id: 'c', distro: 'docker-desktop', version: 2 as const } + ] + await expect( + listWslHostDistros({ + readRegistrations, + readRunning: async () => ({ status: 'confirmed', distros: ['ubuntu'] }) + }) + ).resolves.toEqual([ + { id: 'a', name: 'Debian', version: 1, state: 'stopped' }, + { id: 'b', name: 'Ubuntu', version: 2, state: 'running' } + ]) + await expect( + listWslHostDistros({ + readRegistrations, + readRunning: async () => { + throw new Error('wsl.exe failed') + } + }) + ).resolves.toEqual([ + { id: 'a', name: 'Debian', version: 1, state: 'unverifiable' }, + { id: 'b', name: 'Ubuntu', version: 2, state: 'unverifiable' } + ]) + }) +}) diff --git a/src/main/wsl/host-lifecycle/wsl-host-distro-list.ts b/src/main/wsl/host-lifecycle/wsl-host-distro-list.ts index 4a16ab8c785..5b5fa61ebca 100644 --- a/src/main/wsl/host-lifecycle/wsl-host-distro-list.ts +++ b/src/main/wsl/host-lifecycle/wsl-host-distro-list.ts @@ -20,20 +20,19 @@ export type WslHostDistroCandidate = Omit & { id export async function listWslHostDistros( dependencies: WslHostDistroListDependencies = {} ): Promise { - const [registered, running] = await Promise.all([ - (dependencies.readRegistrations ?? readWslDistroRegistrations)(), - (dependencies.readRunning ?? readConfirmedRunningWslDistros)().catch( - (): WslRunningDistrosRead => ({ status: 'unavailable' }) - ) - ]) - if (!registered) { + const registered = await (dependencies.readRegistrations ?? readWslDistroRegistrations)() + const userNames = new Set(filterUserWslDistros((registered ?? []).map(({ distro }) => distro))) + // Without WSL, Windows' wsl.exe stub opens the installer and a UAC prompt: never spawn it for nothing. + if (!registered || userNames.size === 0) { return [] } + const running = await (dependencies.readRunning ?? readConfirmedRunningWslDistros)().catch( + (): WslRunningDistrosRead => ({ status: 'unavailable' }) + ) const runningKeys = running.status === 'confirmed' ? new Set(running.distros.map((name) => name.trim().toLowerCase())) : null - const userNames = new Set(filterUserWslDistros(registered.map(({ distro }) => distro))) return registered .filter(({ distro }) => userNames.has(distro)) .map(({ id, distro, version }) => ({ From bd7abcb3612fc8e12fa79c5eb2126e3b4e8800f4 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 23:37:01 -0800 Subject: [PATCH 038/178] test: remove simulated tool-run layout suites (#41885) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../NativeChatToolRunMembers.reader.test.tsx | 667 ------------------ .../NativeChatToolRunMembers.window.test.tsx | 146 ---- 2 files changed, 813 deletions(-) delete mode 100644 src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.reader.test.tsx delete mode 100644 src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.window.test.tsx diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.reader.test.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.reader.test.tsx deleted file mode 100644 index f5146e6968c..00000000000 --- a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.reader.test.tsx +++ /dev/null @@ -1,667 +0,0 @@ -// @vitest-environment happy-dom - -import '@testing-library/jest-dom/vitest' -import { useMemo } from 'react' -import { act, cleanup, fireEvent, render } from '@testing-library/react' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { NativeChatBlock } from '../../../../../../shared/native-chat/native-chat-types' -import type { NativeChatDiffReveal } from '../../../../../../shared/native-chat/tools/native-chat-turn-diffs' -import { NativeChatToolRun } from './NativeChatToolRun' -import { NativeChatToolRunWindowContext } from './native-chat-tool-run-window-context' -import { - NativeChatDisclosureContext, - useNativeChatDisclosures -} from '../../messages/disclosure/native-chat-disclosure-store' - -const BOX_HEIGHT_PX = 288 -/** Measured rows are taller than the 26px estimate, so a pixel position alone lands on the wrong row. */ -const ROW_HEIGHT_PX = 40 -/** Every row's laid-out height unless a test says otherwise. */ -const layout = { rowPx: ROW_HEIGHT_PX } -const ROW = '[data-native-chat-tool-run-row]' -/** Rows that have grown in place, by row key. */ -const grownRows = new Map() -/** A hidden pane is display:none: nothing in it has a size. */ -const pane = { hidden: false } - -const resizeObservers = new Set() -class StubResizeObserver implements ResizeObserver { - readonly targets = new Set() - constructor(readonly callback: ResizeObserverCallback) { - resizeObservers.add(this) - } - observe(target: Element): void { - this.targets.add(target) - } - unobserve(target: Element): void { - this.targets.delete(target) - } - disconnect(): void { - this.targets.clear() - resizeObservers.delete(this) - } -} - -/** Deliver what a browser's ResizeObserver would after `element` changed size. */ -function resized(element: Element, height: number): void { - for (const observer of resizeObservers) { - if (observer.targets.has(element)) { - const size = { blockSize: height, inlineSize: 400 } - const entry = { - target: element, - contentRect: element.getBoundingClientRect(), - borderBoxSize: [size], - contentBoxSize: [size], - devicePixelContentBoxSize: [size] - } satisfies ResizeObserverEntry - observer.callback([entry], observer) - } - } -} - -function rowHeight(element: Element): number { - return grownRows.get(element.getAttribute('data-native-chat-tool-run-row') ?? '') ?? layout.rowPx -} - -/** happy-dom lays nothing out: stack the run box's children by their stated heights. */ -function layOutRunBox(): void { - const heightOf = (element: Element): number => { - if (pane.hidden) { - return 0 - } - if (element.matches(ROW)) { - return rowHeight(element) - } - return element instanceof HTMLElement ? Number.parseFloat(element.style.height) || 0 : 0 - } - const box = (element: Element): HTMLElement | null => - element.closest('[data-native-chat-tool-run-members]') - vi.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockImplementation(function ( - this: HTMLElement - ) { - if (pane.hidden) { - return 0 - } - if (this.hasAttribute('data-native-chat-tool-run-members')) { - return BOX_HEIGHT_PX - } - return this.matches(ROW) ? rowHeight(this) : 0 - }) - vi.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(400) - vi.spyOn(HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(function ( - this: HTMLElement - ) { - return this.hasAttribute('data-native-chat-tool-run-members') ? BOX_HEIGHT_PX : 0 - }) - vi.spyOn(HTMLElement.prototype, 'scrollHeight', 'get').mockImplementation(function ( - this: HTMLElement - ) { - const content = this.hasAttribute('data-native-chat-tool-run-members') - ? this.firstElementChild - : null - return content ? Array.from(content.children).reduce((sum, c) => sum + heightOf(c), 0) : 0 - }) - // A browser clamps scrollTop to what the box can reach; an unclamped box would hide a wrong scroll. - const scrollTops = new WeakMap() - vi.spyOn(Element.prototype, 'scrollTop', 'get').mockImplementation(function (this: Element) { - return scrollTops.get(this) ?? 0 - }) - vi.spyOn(Element.prototype, 'scrollTop', 'set').mockImplementation(function ( - this: Element, - value: number - ) { - const max = this instanceof HTMLElement ? Math.max(0, this.scrollHeight - this.clientHeight) : 0 - scrollTops.set(this, Math.min(Math.max(0, value), max)) - }) - vi.spyOn(Element.prototype, 'getBoundingClientRect').mockImplementation(function (this: Element) { - const scroller = box(this) - if (!scroller || pane.hidden) { - return DOMRect.fromRect({ x: 0, y: 0, width: 0, height: 0 }) - } - if (this === scroller) { - return DOMRect.fromRect({ x: 0, y: 0, width: 400, height: BOX_HEIGHT_PX }) - } - const row = this.closest(ROW) ?? this - let top = -scroller.scrollTop - for (const child of Array.from(scroller.firstElementChild?.children ?? [])) { - if (child === row) { - return DOMRect.fromRect({ x: 0, y: top, width: 400, height: heightOf(child) }) - } - top += heightOf(child) - } - return DOMRect.fromRect({ x: 0, y: 0, width: 0, height: 0 }) - }) -} - -function calls(count: number): NativeChatBlock[] { - return Array.from({ length: count }, (_, index) => ({ - type: 'tool-call' as const, - name: 'Read', - callId: `call-${index}`, - input: { file_path: `src/file-${index}.ts` } - })) -} - -const PATCH = - '*** Begin Patch\n*** Update File: first.ts\n ctx\n-was\n+now\n*** Update File: second.ts\n@@ -1,1 +1,1 @@\n-a\n+b\n*** End Patch' - -/** A long settled run with one two-file edit far from its top. */ -function editRun(count: number, editAt: number): NativeChatBlock[] { - const blocks = calls(count) - blocks.splice( - editAt, - 0, - { - type: 'tool-call', - name: 'apply_patch', - callId: 'edit-1', - input: { input: PATCH }, - state: 'completed' - }, - { type: 'tool-result', callId: 'edit-1', output: 'Success. Updated the following files' } - ) - return blocks -} - -// Stable, as a real transcript's callback is: a new one each render would make every card reveal again. -const ignoreRevealTarget = (): void => {} - -function Transcript({ - blocks, - mounted = true, - live = false, - visible = true, - find = false, - revealedDiff -}: { - blocks: NativeChatBlock[] - mounted?: boolean - live?: boolean - visible?: boolean - find?: boolean - revealedDiff?: NativeChatDiffReveal -}) { - const disclosures = useNativeChatDisclosures() - const windowFacts = useMemo(() => ({ isVisible: visible, findActive: find }), [visible, find]) - return ( - - - {mounted ? ( - - ) : null} - - - ) -} - -function membersBox(container: HTMLElement): HTMLElement { - const element = container.querySelector('[data-native-chat-tool-run-members]') - if (!element) { - throw new Error('members box did not render') - } - return element -} - -/** The row at the box's top edge and how far into it the box starts. */ -function topRow(container: HTMLElement): { key: string | null; offset: number } { - for (const row of Array.from(container.querySelectorAll(ROW))) { - const rect = row.getBoundingClientRect() - if (rect.bottom > 0) { - return { key: row.getAttribute('data-native-chat-tool-run-row'), offset: -rect.top } - } - } - return { key: null, offset: 0 } -} - -/** Deliver frames and the anchor's idle record on the fake clock: no wall-clock waits. */ -async function settle(): Promise { - for (let pass = 0; pass < 4; pass++) { - await act(async () => { - vi.advanceTimersByTime(250) - }) - } -} - -/** A drawn row's laid-out height changes, as a browser's ResizeObserver reports it. */ -async function growRow(container: HTMLElement, key: string, height: number): Promise { - const row = container.querySelector(`[data-native-chat-tool-run-row="${key}"]`) - if (!row) { - throw new Error(`row ${key} is not drawn`) - } - grownRows.set(key, height) - await act(async () => { - resized(row, height) - }) - await settle() -} - -async function scrollBox(container: HTMLElement, top: number): Promise { - const box = membersBox(container) - await act(async () => { - box.scrollTop = top - fireEvent.scroll(box) - }) - await settle() -} - -/** The browser's report of a landing's own scroll write, which this harness does not send: it may only - * let the window catch up to where the box already is, never move it. */ -async function reportLandingScroll(container: HTMLElement): Promise { - const box = membersBox(container) - const landed = box.scrollTop - await act(async () => { - fireEvent.scroll(box) - }) - await settle() - expect(box.scrollTop).toBe(landed) -} - -/** The browser's first size report for each drawn row, which it sends in the frame a row appears and this - * harness only sends when asked: rows the window drew while it believed the box was scrolling get theirs here. */ -async function reportDrawnRowSizes(container: HTMLElement): Promise { - await act(async () => { - for (const row of Array.from(container.querySelectorAll(ROW))) { - resized(row, rowHeight(row)) - } - }) - await settle() -} - -describe('NativeChatToolRun opened-run reader', () => { - beforeEach(() => { - vi.useFakeTimers({ - toFake: ['setTimeout', 'clearTimeout', 'requestAnimationFrame', 'cancelAnimationFrame'] - }) - grownRows.clear() - layout.rowPx = ROW_HEIGHT_PX - pane.hidden = false - vi.stubGlobal('ResizeObserver', StubResizeObserver) - layOutRunBox() - }) - afterEach(() => { - cleanup() - vi.useRealTimers() - vi.restoreAllMocks() - vi.unstubAllGlobals() - }) - - it('returns to the same row and offset after the transcript unmounts it and work arrives', async () => { - const { container, rerender } = render() - await settle() - await scrollBox(container, 9_013) - const before = topRow(container) - expect(before.key).not.toBeNull() - expect(before.offset).toBeGreaterThan(0) - - rerender() - rerender() - // Evicted again before that return lands: the place it was landing on is what it keeps. - rerender() - rerender() - await settle() - await reportLandingScroll(container) - - const after = topRow(container) - expect(after.key).toBe(before.key) - expect(Math.abs(after.offset - before.offset)).toBeLessThanOrEqual(1) - }) - - it('keeps a revealed edit at the top over the place the reader had scrolled to', async () => { - const blocks = editRun(600, 400) - const { container, rerender } = render() - await settle() - await scrollBox(container, 2_000) - const reveal = { messageId: 'message-1', editKey: 'apply_patch:400', fileIndex: 1 } - - rerender() - await settle() - expect(topRow(container).key).toBe('edit:apply_patch:400') - - await scrollBox(container, 2_000) - rerender() - await settle() - expect(topRow(container).key).toBe('edit:apply_patch:400') - }) - - it('forgets its place when the reader closes and reopens it', async () => { - const { container } = render() - await settle() - await scrollBox(container, 9_013) - const header = container.querySelector('[data-native-chat-tool-run-state]') - if (!header) { - throw new Error('run header did not render') - } - await act(async () => { - fireEvent.click(header) - }) - await settle() - await act(async () => { - fireEvent.click(header) - }) - await settle() - expect(membersBox(container).scrollTop).toBe(0) - expect(topRow(container).key).toBe('call:call-0') - }) - - it('follows new work at the end, but not a detail growing after it lands', async () => { - const { container, rerender } = render() - await settle() - const box = membersBox(container) - const atEnd = (): boolean => box.scrollTop >= box.scrollHeight - BOX_HEIGHT_PX - 1 - expect(atEnd()).toBe(true) - - // A burst larger than the window, then a result that adds no row. - rerender() - await settle() - expect(atEnd()).toBe(true) - const withResult = [ - ...calls(260), - { type: 'tool-result' as const, callId: 'call-259', output: 'done' } - ] - rerender() - await settle() - expect(atEnd()).toBe(true) - - // In-place growth that is not new work leaves the reader where they are. - const landed = box.scrollTop - const tail = container.querySelector('[data-native-chat-tool-run-row="call:call-259"]') - if (!tail) { - throw new Error('tail row is not drawn') - } - grownRows.set('call:call-259', 400) - await act(async () => { - resized(tail, 400) - }) - await settle() - expect(box.scrollTop).toBe(landed) - - // A reader scrolled away stays put when more work arrives. - await scrollBox(container, 1_000) - rerender() - await settle() - expect(box.scrollTop).toBe(1_000) - }) - - it('lets a reveal win over a restore that has not landed yet', async () => { - const blocks = editRun(600, 400) - const { container, rerender } = render() - await settle() - await scrollBox(container, 2_000) - rerender() - rerender() - // No settling: the returning restore is still pending when the reveal arrives. - const reveal = { messageId: 'message-1', editKey: 'apply_patch:400', fileIndex: 1 } - rerender() - await settle() - expect(topRow(container).key).toBe('edit:apply_patch:400') - - // A reveal made in the same render that shows the pane again wins over the place it kept while hidden, - // and is what the run keeps even before the browser reports the reveal's scroll: hidden again at once and - // shown under the same request (so no fresh reveal), it returns to the revealed edit. - await scrollBox(container, 2_000) - pane.hidden = true - rerender( - - ) - pane.hidden = false - rerender() - pane.hidden = true - rerender( - - ) - pane.hidden = false - rerender() - await settle() - // The revealed row stays drawn while its request stands, so no scroll report is needed to see it. - expect(topRow(container).key).toBe('edit:apply_patch:400') - }) - - it('stops following when the reader scrolls before new work has settled', async () => { - const { container, rerender } = render() - await settle() - const box = membersBox(container) - rerender() - await act(async () => { - box.scrollTop = 1_000 - fireEvent.scroll(box) - }) - await settle() - expect(box.scrollTop).toBe(1_000) - }) - - it('finishes a follow that started while the pane was hidden once it shows again', async () => { - const { container, rerender } = render() - await settle() - pane.hidden = true - rerender() - rerender() - await settle() - pane.hidden = false - rerender() - await settle() - const box = membersBox(container) - expect(box.scrollTop).toBe(box.scrollHeight - BOX_HEIGHT_PX) - - // Once landed, growth that is not new work leaves the reader where they are. - const landed = box.scrollTop - await growRow(container, 'call:call-229', 300) - expect(box.scrollTop).toBe(landed) - }) - - it('finishes a follow whose tail first measures exactly as estimated', async () => { - // 26px rows: a live run opens following before any row is drawn; the tail's first real - // measurement then changes no size, so only that measurement can end the follow. - layout.rowPx = 26 - const { container } = render() - await settle() - const box = membersBox(container) - expect(box.scrollTop).toBe(box.scrollHeight - BOX_HEIGHT_PX) - // The follow ended, so growth that is not new work leaves the reader in place. - const landed = box.scrollTop - await growRow(container, 'call:call-199', 300) - expect(box.scrollTop).toBe(landed) - }) - - it('waits for a tail with no size yet, then lands on it once it has one', async () => { - const { container, rerender } = render() - await settle() - grownRows.set('call:call-259', 0) - rerender() - await settle() - // A measurement with no size does not count as landing. - await growRow(container, 'call:call-259', 0) - // The tail now lays out: the follow that waited for it lands on the new end. - await growRow(container, 'call:call-259', 40) - const box = membersBox(container) - expect(box.scrollTop).toBe(box.scrollHeight - BOX_HEIGHT_PX) - const landed = box.scrollTop - await growRow(container, 'call:call-259', 300) - expect(box.scrollTop).toBe(landed) - }) - - it('returns to where the reader scrolled with Find open after the pane hides and shows', async () => { - const { container, rerender } = render() - await settle() - rerender() - await settle() - await scrollBox(container, 9_013) - const before = topRow(container) - expect(before.key).toBe('call:call-225') - - // A hidden box loses its offset; only the remembered row brings the reader back. - pane.hidden = true - rerender() - membersBox(container).scrollTop = 0 - pane.hidden = false - rerender() - await settle() - expect(topRow(container)).toEqual(before) - - // The landing finished without any size change: later growth elsewhere leaves the reader in place. - await growRow(container, 'call:call-900', 300) - expect(topRow(container)).toEqual(before) - }) - - it('keeps a reader who jumps past the drawn rows of a live run where they jumped', async () => { - layout.rowPx = 26 - const { container, rerender } = render() - await settle() - const box = membersBox(container) - const opened = container.querySelector('[data-native-chat-tool-run-row="call:call-28"] button') - if (!opened) { - throw new Error('row call-28 is not drawn') - } - await act(async () => { - fireEvent.click(opened) - }) - await growRow(container, 'call:call-28', 68) - for (let count = 30; count <= 62; count++) { - rerender() - await settle() - } - - // A scrollbar drag lands far from every drawn row; then work arrives. - await scrollBox(container, 683) - rerender() - await settle() - expect(box.scrollTop).toBe(683) - }) - - for (const mode of ['small run shown again', 'long run with Find'] as const) { - it(`lets a reader scroll away from a followed live run without a jump (${mode})`, async () => { - layout.rowPx = 26 - const find = mode === 'long run with Find' - const first = find ? 200 : 30 - const { container, rerender } = render() - await settle() - if (!find) { - pane.hidden = true - rerender() - pane.hidden = false - rerender() - await settle() - } - for (let count = first + 1; count <= first + 15; count++) { - rerender() - await settle() - } - const box = membersBox(container) - const away = box.scrollHeight - BOX_HEIGHT_PX - 120 - await scrollBox(container, away) - expect(box.scrollTop).toBe(away) - rerender() - await settle() - expect(box.scrollTop).toBe(away) - }) - } - - for (const hiddenAfterMs of [100, 160]) { - it(`keeps the reader's row through a hidden pane that reports a scroll of its own (hidden ${hiddenAfterMs}ms after a scroll)`, async () => { - const { container, rerender } = render() - await settle() - await scrollBox(container, 9_013) - await reportDrawnRowSizes(container) - - // Hidden shortly after one more scroll, before anything deferred from it could settle. - await act(async () => { - fireEvent.scroll(membersBox(container)) - vi.advanceTimersByTime(hiddenAfterMs) - }) - // The reader's place as the pane hides is the one that must come back. - const before = topRow(container) - pane.hidden = true - rerender() - // A hidden box loses its offset and may report that as a scroll; the transcript then unmounts the run. - await scrollBox(container, 0) - rerender() - pane.hidden = false - rerender() - await settle() - await reportLandingScroll(container) - await reportDrawnRowSizes(container) - expect(topRow(container)).toEqual(before) - }) - } - - it('keeps a parked reader on its row through Find, the window threshold and a selection', async () => { - const { container, rerender } = render() - await settle() - await scrollBox(container, 613) - const before = topRow(container) - - rerender() - await settle() - expect(topRow(container)).toEqual(before) - rerender() - await settle() - expect(topRow(container)).toEqual(before) - rerender() - await settle() - expect(topRow(container)).toEqual(before) - - const box = membersBox(container) - await act(async () => { - document.getSelection()?.selectAllChildren(box) - document.dispatchEvent(new Event('selectionchange')) - }) - await settle() - expect(topRow(container)).toEqual(before) - await act(async () => { - document.getSelection()?.removeAllRanges() - document.dispatchEvent(new Event('selectionchange')) - }) - await settle() - expect(topRow(container)).toEqual(before) - }) - - it("lands on the reader's row once a pane shown before its layout finally lays out", async () => { - const { container, rerender } = render() - await settle() - await scrollBox(container, 9_013) - const before = topRow(container) - - pane.hidden = true - rerender() - membersBox(container).scrollTop = 0 - // Shown, but its box has no geometry for longer than any landing waits on frames alone. - rerender() - await settle() - await settle() - // Layout arrives; the held row reports a size that changes nothing. - pane.hidden = false - await growRow(container, before.key ?? '', 40) - expect(topRow(container)).toEqual(before) - - await growRow(container, 'call:call-900', 300) - expect(topRow(container)).toEqual(before) - }) - - it('does not keep a selection span drawn after it ended while the pane was hidden', async () => { - const { container, rerender } = render() - await settle() - const box = membersBox(container) - await act(async () => { - document.getSelection()?.selectAllChildren(box) - document.dispatchEvent(new Event('selectionchange')) - }) - expect(container.querySelectorAll(ROW).length).toBe(1_000) - - pane.hidden = true - rerender() - await act(async () => { - document.getSelection()?.removeAllRanges() - document.dispatchEvent(new Event('selectionchange')) - }) - pane.hidden = false - rerender() - await settle() - expect(container.querySelectorAll(ROW).length).toBeLessThan(100) - }) -}) diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.window.test.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.window.test.tsx deleted file mode 100644 index 311eafb45a5..00000000000 --- a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRunMembers.window.test.tsx +++ /dev/null @@ -1,146 +0,0 @@ -// @vitest-environment happy-dom - -import '@testing-library/jest-dom/vitest' -import { useMemo } from 'react' -import { cleanup, fireEvent, render, screen } from '@testing-library/react' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { NativeChatBlock } from '../../../../../../shared/native-chat/native-chat-types' -import { NativeChatToolRun } from './NativeChatToolRun' -import { NativeChatToolRunWindowContext } from './native-chat-tool-run-window-context' -import { - NativeChatDisclosureContext, - useNativeChatDisclosures -} from '../../messages/disclosure/native-chat-disclosure-store' - -const BOX_HEIGHT_PX = 288 -const ROW_HEIGHT_PX = 26 -/** 12 rows fill the box, 10 overscan rows on each side, and the first/last rows stay drawn. */ -const MAX_DRAWN_ROWS = 12 + 2 * 10 + 2 - -function calls(count: number): NativeChatBlock[] { - return Array.from({ length: count }, (_, index) => ({ - type: 'tool-call' as const, - name: 'Read', - callId: `call-${index}`, - input: { file_path: `src/file-${index}.ts` } - })) -} - -/** Two task-list updates (the second shows its "Full task list" control), then Read calls up to `count` rows. */ -function withTaskList(count: number): NativeChatBlock[] { - const todo = (status: string): NativeChatBlock => ({ - type: 'tool-call', - name: 'TodoWrite', - input: { todos: [{ content: 'Audit the window', status }] } - }) - return [todo('pending'), todo('in_progress'), ...calls(count - 2)] -} - -function Run({ - count, - findActive = false, - blocks = calls(count) -}: { - count: number - findActive?: boolean - blocks?: NativeChatBlock[] -}) { - const disclosures = useNativeChatDisclosures() - const windowFacts = useMemo(() => ({ isVisible: true, findActive }), [findActive]) - return ( - - - - - - ) -} - -function drawnRows(container: HTMLElement): HTMLElement[] { - return Array.from(container.querySelectorAll('[data-native-chat-tool-run-row]')) -} - -function rowButton(container: HTMLElement, index: number): HTMLElement { - const button = container.querySelector( - `[data-native-chat-tool-run-row="call:call-${index}"] button` - ) - if (!button) { - throw new Error(`row ${index} is not drawn`) - } - return button -} - -describe('NativeChatToolRun opened-run window', () => { - beforeEach(() => { - // happy-dom lays nothing out: the box and each row get the sizes the test states. - vi.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockImplementation(function ( - this: HTMLElement - ) { - if (this.hasAttribute('data-native-chat-tool-run-members')) { - return BOX_HEIGHT_PX - } - return this.hasAttribute('data-native-chat-tool-run-row') ? ROW_HEIGHT_PX : 0 - }) - vi.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(400) - }) - afterEach(() => { - cleanup() - vi.restoreAllMocks() - }) - - it('draws the same bounded window of a long run at 2,000 and 20,000 calls', () => { - for (const count of [2_000, 20_000]) { - const { container, unmount } = render() - const drawn = drawnRows(container) - expect(drawn.length).toBeGreaterThan(0) - expect(drawn.length).toBeLessThanOrEqual(MAX_DRAWN_ROWS) - // A settled run reads from its top, and its last row stays drawn for the tail. - expect(rowButton(container, 0)).toBeInTheDocument() - expect(rowButton(container, count - 1)).toBeInTheDocument() - unmount() - } - }) - - it('draws every row while Find has a query, then returns to the window without remounting', () => { - const { container, rerender } = render() - const first = rowButton(container, 0) - expect(drawnRows(container).length).toBeLessThanOrEqual(MAX_DRAWN_ROWS) - - rerender() - expect(drawnRows(container)).toHaveLength(200) - expect(rowButton(container, 0)).toBe(first) - - rerender() - expect(drawnRows(container).length).toBeLessThanOrEqual(MAX_DRAWN_ROWS) - expect(rowButton(container, 0)).toBe(first) - }) - - it('keeps a focused row and an opened full task list through windowing and Find', () => { - const { container, rerender } = render() - fireEvent.click(screen.getByRole('button', { name: /Full task list/ })) - const fullList = screen.getByRole('button', { name: /Full task list/ }) - expect(fullList).toHaveAttribute('aria-expanded', 'true') - const focused = rowButton(container, 10) - focused.focus() - - rerender() - expect(focused).toBeInTheDocument() - expect(document.activeElement).toBe(focused) - expect(screen.getByRole('button', { name: /Full task list/ })).toHaveAttribute( - 'aria-expanded', - 'true' - ) - - rerender() - rerender() - expect(screen.getByRole('button', { name: /Full task list/ })).toHaveAttribute( - 'aria-expanded', - 'true' - ) - }) -}) From 2d9ec3224cecd4ce4989e1bb9cc5ead61e1b8570 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 23:37:51 -0800 Subject: [PATCH 039/178] fix: keep delete dialog status stable during catalog refreshes (#41864) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/react-compiler-bailout-baseline.txt | 1 - ...-delete-worktree-status-hydration.test.tsx | 118 ++++++++++++++++++ .../use-delete-worktree-status-hydration.ts | 71 +++++++---- 3 files changed, 164 insertions(+), 26 deletions(-) create mode 100644 src/renderer/src/components/sidebar/workspace-actions/deletion/use-delete-worktree-status-hydration.test.tsx diff --git a/config/react-compiler-bailout-baseline.txt b/config/react-compiler-bailout-baseline.txt index ecf0b935257..43049616dfa 100644 --- a/config/react-compiler-bailout-baseline.txt +++ b/config/react-compiler-bailout-baseline.txt @@ -501,7 +501,6 @@ 1 src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteNestedWorktreesDialog.tsx 1 src/renderer/src/components/sidebar/workspace-actions/deletion/RemovalUnsavedFilesNotice.tsx 1 src/renderer/src/components/sidebar/workspace-actions/deletion/WorktreeForceDeleteButton.tsx -1 src/renderer/src/components/sidebar/workspace-actions/deletion/use-delete-worktree-status-hydration.ts 1 src/renderer/src/components/sidebar/workspace-actions/parenting/WorktreeParentPickerPopover.tsx 3 src/renderer/src/components/sidebar/workspace-actions/parenting/use-worktree-parent-picker-transition.ts 7 src/renderer/src/components/sidebar/workspace-board/drag/use-workspace-kanban-card-pointer-drag.ts diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/use-delete-worktree-status-hydration.test.tsx b/src/renderer/src/components/sidebar/workspace-actions/deletion/use-delete-worktree-status-hydration.test.tsx new file mode 100644 index 00000000000..6b1812a4519 --- /dev/null +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/use-delete-worktree-status-hydration.test.tsx @@ -0,0 +1,118 @@ +// @vitest-environment happy-dom + +import { act, cleanup, renderHook, waitFor } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { makeWorktree } from '@/store/slices/worktrees/worktree.test-fixture' +import type { Repo } from '../../../../../../shared/repo-types' +import type { GitStatusResult } from '../../../../../../shared/git/status/git-status-types' +import { getRuntimeGitStatus } from '@/runtime/source-control/git/runtime-git-status-client' +import { useDeleteWorktreeStatusHydration } from './use-delete-worktree-status-hydration' + +const state = vi.hoisted(() => { + const repos: Repo[] = [] + return { + repos, + activeWorktreeId: null, + activeWorkspaceExecutionHostId: null, + gitStatusByWorktree: {} + } +}) + +vi.mock('@/store', () => ({ + useAppStore: Object.assign((selector: (value: typeof state) => unknown) => selector(state), { + getState: () => state + }) +})) +vi.mock('@/runtime/source-control/git/runtime-git-status-client', () => ({ + getRuntimeGitStatus: vi.fn() +})) + +afterEach(cleanup) +beforeEach(() => { + vi.resetAllMocks() + state.repos = [] +}) + +describe('delete status snapshot continuity', () => { + it('keeps loaded paths when catalog objects refresh for the same workspace', async () => { + vi.mocked(getRuntimeGitStatus) + .mockResolvedValueOnce({ + entries: [{ path: 'keep-visible.ts', status: 'modified', area: 'unstaged' }], + conflictOperation: 'unknown' + }) + .mockImplementation(() => new Promise(() => {})) + const target = makeWorktree({ id: 'feature', repoId: 'repo', hostId: 'local' }) + const targets = [target] + const view = renderHook(useDeleteWorktreeStatusHydration, { + initialProps: { + isOpen: true, + deleteTargets: targets, + visibleTargets: targets, + repoMap: new Map() + } + }) + await waitFor(() => + expect(view.result.current.get('local|feature')).toEqual([ + { path: 'keep-visible.ts', status: 'modified', area: 'unstaged' } + ]) + ) + + state.repos = [...state.repos] + view.rerender({ + isOpen: true, + deleteTargets: [{ ...target, lastActivityAt: 42 }], + visibleTargets: [{ ...target }], + repoMap: new Map() + }) + + expect(view.result.current.get('local|feature')).toEqual([ + { path: 'keep-visible.ts', status: 'modified', area: 'unstaged' } + ]) + }) + + it('replaces the snapshot when the same host changes its SSH connection', async () => { + const repo: Repo = { + id: 'repo', + path: '/repo', + displayName: 'repo', + badgeColor: '', + addedAt: 1, + executionHostId: 'runtime:fixture', + connectionId: 'first' + } + state.repos = [repo] + const target = makeWorktree({ id: 'feature', repoId: 'repo', hostId: 'runtime:fixture' }) + let finish: (status: GitStatusResult) => void = () => { + throw new Error('Status read has not started') + } + vi.mocked(getRuntimeGitStatus) + .mockResolvedValueOnce({ entries: [], conflictOperation: 'unknown' }) + .mockImplementationOnce( + () => + new Promise((resolve) => { + finish = resolve + }) + ) + const props = { + isOpen: true, + deleteTargets: [target], + visibleTargets: [target], + repoMap: new Map([['repo', repo]]) + } + const view = renderHook(useDeleteWorktreeStatusHydration, { initialProps: props }) + await waitFor(() => expect(view.result.current.get('runtime:fixture|feature')).toEqual([])) + + state.repos = [{ ...repo, connectionId: 'second' }] + view.rerender({ ...props, repoMap: new Map([['repo', state.repos[0]!]]) }) + expect(view.result.current.get('runtime:fixture|feature')).toBeUndefined() + await act(async () => { + finish({ + entries: [{ path: 'second-host.ts', status: 'added', area: 'staged' }], + conflictOperation: 'unknown' + }) + }) + expect(view.result.current.get('runtime:fixture|feature')).toEqual([ + { path: 'second-host.ts', status: 'added', area: 'staged' } + ]) + }) +}) diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/use-delete-worktree-status-hydration.ts b/src/renderer/src/components/sidebar/workspace-actions/deletion/use-delete-worktree-status-hydration.ts index 278f9e55013..0c992e9a6ef 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/use-delete-worktree-status-hydration.ts +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/use-delete-worktree-status-hydration.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { useEffect, useEffectEvent, useState } from 'react' import { useAppStore } from '@/store' import { getConnectionId } from '@/lib/workspace/ownership/connection-context' import { runtimeTargetForWorkspaceOwner } from '@/lib/workspace/ownership/resolve-owner' @@ -51,20 +51,24 @@ export function useDeleteWorktreeStatusHydration({ repoMap: ReadonlyMap }): ReadonlyMap { const repos = useAppStore((state) => state.repos) - const generation = isOpen ? deleteTargets.map(getWorktreeHostIdentity).join('\n') : '' - const generationRef = useRef(generation) - const [statusByIdentity, setStatusByIdentity] = useState< - Map - >(() => new Map()) - const currentStatusByIdentity = - generationRef.current === generation ? statusByIdentity : EMPTY_STATUS_BY_IDENTITY - - useEffect(() => { - generationRef.current = generation - setStatusByIdentity(new Map()) - if (!isOpen) { - return - } + const generation = isOpen + ? deleteTargets + .map((target) => + JSON.stringify({ + identity: getWorktreeHostIdentity(target), + isMainWorktree: target.isMainWorktree, + isFolderWorkspace: isFolderWorkspaceDelete(repoMap, target), + context: resolveDeleteTargetGitContext(target, repos) + }) + ) + .sort() + .join('\n') + : '' + const [result, setResult] = useState<{ + generation: string + statuses: Map + }>({ generation: '', statuses: new Map() }) + const startChecks = useEffectEvent((signal: AbortSignal) => { const gitStatusByWorktree = useAppStore.getState().gitStatusByWorktree const currentState = useAppStore.getState() const targets = orderDeleteWorktreeStatusHydrationTargets({ @@ -75,39 +79,56 @@ export function useDeleteWorktreeStatusHydration({ activeWorktreeId: currentState.activeWorktreeId, activeExecutionHostId: currentState.activeWorkspaceExecutionHostId }) - const controller = new AbortController() for (const target of targets) { const identity = getWorktreeHostIdentity(target) + const publish = (entries: GitStatusResult['entries'] | null): void => + setResult((current) => ({ + generation, + statuses: new Map(current.generation === generation ? current.statuses : []).set( + identity, + entries + ) + })) const existingStatus = target.hostId ? undefined : gitStatusByWorktree[target.id] if (existingStatus) { - setStatusByIdentity((current) => new Map(current).set(identity, existingStatus)) + publish(existingStatus) continue } const gitContext = resolveDeleteTargetGitContext(target, repos) if (!gitContext) { - setStatusByIdentity((current) => new Map(current).set(identity, null)) + publish(null) continue } void getRuntimeGitStatus(gitContext, { admissionTier: 'background', includeLineStats: false, - signal: controller.signal + signal }) .then((status) => { - if (!controller.signal.aborted && generationRef.current === generation) { - setStatusByIdentity((current) => new Map(current).set(identity, status.entries)) + if (!signal.aborted) { + publish(status.entries) } }) .catch(() => { - if (!controller.signal.aborted && generationRef.current === generation) { - setStatusByIdentity((current) => new Map(current).set(identity, null)) + if (!signal.aborted) { + publish(null) } }) } + }) + + // Catalog refreshes replace arrays; restart only when targets or their read routes change. + useEffect(() => { + setResult({ generation, statuses: new Map() }) + if (!isOpen) { + return + } + const controller = new AbortController() + startChecks(controller.signal) return () => { controller.abort() } - }, [deleteTargets, generation, isOpen, repoMap, repos, visibleTargets]) + }, [generation, isOpen]) - return currentStatusByIdentity + return result.generation === generation ? result.statuses : EMPTY_STATUS_BY_IDENTITY } From 5d4b66cb1739fa40c0186496e9ba4fe2b37b877b Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:44:49 -0800 Subject: [PATCH 040/178] fix(computer): bound subtree snapshot retention across repeated reads (STA-15107) (#41832) * fix(computer): bound subtree snapshot retention across repeated reads (STA-15107) * perf(computer): bucket subtree identities and combine root regression * fix(computer): retain elided roots and keep unique-ID reads fast --- .../desktop-script-provider-types.ts | 4 + .../desktop-script-subtree-snapshot.test.ts | 159 +++++++++++++++++- .../desktop-script-subtree-snapshot.ts | 151 ++++++++++++++--- src/shared/computer-use-tree-text.ts | 2 +- 4 files changed, 295 insertions(+), 21 deletions(-) diff --git a/src/main/computer/desktop-script/desktop-script-provider-types.ts b/src/main/computer/desktop-script/desktop-script-provider-types.ts index 662d9230323..8b57089c263 100644 --- a/src/main/computer/desktop-script/desktop-script-provider-types.ts +++ b/src/main/computer/desktop-script/desktop-script-provider-types.ts @@ -79,6 +79,10 @@ export type BridgeSnapshot = { elements?: BridgeElement[] /** Set by the client when treeLines show only this element's subtree. */ rootElementIndex?: number + /** Cache only: for elements a subtree read returned, the root of the latest read that returned them. */ + subtreeReadRoots?: ReadonlyMap + /** Cache only: the next index a subtree read may assign, so dropped indexes are never reused. */ + nextElementIndex?: number } export type BridgeWindow = { diff --git a/src/main/computer/desktop-script/snapshots/desktop-script-subtree-snapshot.test.ts b/src/main/computer/desktop-script/snapshots/desktop-script-subtree-snapshot.test.ts index 319685e8c91..d41bc05332e 100644 --- a/src/main/computer/desktop-script/snapshots/desktop-script-subtree-snapshot.test.ts +++ b/src/main/computer/desktop-script/snapshots/desktop-script-subtree-snapshot.test.ts @@ -7,7 +7,10 @@ import { sampleCapabilities } from '../desktop-script-provider-test-harness' -function bridgeSnapshot(treeLines: string[], elements: { index: number; runtimeId: number[] }[]) { +function bridgeSnapshot( + treeLines: string[], + elements: { index: number; runtimeId: number[]; name?: string }[] +) { return { ...sampleBridgeSnapshot('Mail', ''), windowIndex: 0, treeLines, elements } } @@ -70,3 +73,157 @@ describe('DesktopScriptProviderClient subtree reads', () => { expect(clicked).toMatchObject({ runtimeId: [0, 0, 1] }) }) }) + +describe('DesktopScriptProviderClient repeated subtree reads', () => { + afterEach(resetDesktopScriptProviderTestHarness) + + it('forgets children a live list replaced and keeps indexes outside the list', async () => { + const client = await createDesktopScriptProviderClient('linux', '/tmp/runtime.py') + mockBridgeResponse({ + ok: true, + snapshot: bridgeSnapshot( + ['0 frame Mail', '\t1 list Inbox', '\t2 button Send'], + [ + { index: 0, runtimeId: [0] }, + { index: 1, runtimeId: [0, 0] }, + { index: 2, runtimeId: [0, 1] } + ] + ) + }) + await client.snapshot({ app: 'Mail' }) + + // The list recreates its child with a fresh runtime id on every read; each gets a never-used index. + let shownChild = '' + for (const generation of [1, 2]) { + mockBridgeResponse({ + ok: true, + snapshot: bridgeSnapshot( + ['0 list Inbox', '\t1 button A'], + [ + { index: 0, runtimeId: [0, 0] }, + { index: 1, runtimeId: [1, generation] } + ] + ) + }) + const read = await client.snapshot({ app: 'Mail', rootElementIndex: 1 }) + shownChild = read.snapshot.treeText.split('\n')[5] + } + expect(shownChild).toBe('\t4 button A') + + let sentRoot: unknown + mockBridgeResponse( + { + ok: true, + snapshot: bridgeSnapshot(['0 button Send'], [{ index: 0, runtimeId: [0, 1] }]) + }, + (operation) => { + sentRoot = operation.root + } + ) + await client.snapshot({ app: 'Mail', rootElementIndex: 2 }) + expect(sentRoot).toMatchObject({ runtimeId: [0, 1] }) + + // Answer the click in case the replaced child's index still resolves, so the assertion fails cleanly. + mockBridgeResponse({ ok: true, capabilities: sampleCapabilities() }) + mockBridgeResponse({ + ok: true, + snapshot: bridgeSnapshot(['0 frame Mail'], [{ index: 0, runtimeId: [0] }]) + }) + await expect(client.action('click', { app: 'Mail', elementIndex: 3 })).rejects.toMatchObject({ + code: 'element_not_found' + }) + }) + + it('renumbers a different element at a known runtime id but keeps the requested root', async () => { + const client = await createDesktopScriptProviderClient('windows', 'runtime.ps1') + mockBridgeResponse({ + ok: true, + snapshot: bridgeSnapshot( + ['0 window Inbox - Mail', '\t1 button Archive'], + [ + { index: 0, runtimeId: [42, 7], name: 'Inbox - Mail' }, + { index: 1, runtimeId: [42, 8], name: 'Archive' } + ] + ) + }) + await client.snapshot({ app: 'Mail' }) + + // Both renamed; only the root is known to be the same element, since the read started from it. + mockBridgeResponse({ + ok: true, + snapshot: bridgeSnapshot( + ['0 window Sent - Mail', '\t1 button Delete'], + [ + { index: 0, runtimeId: [42, 7], name: 'Sent - Mail' }, + { index: 1, runtimeId: [42, 8], name: 'Delete' } + ] + ) + }) + const read = await client.snapshot({ app: 'Mail', rootElementIndex: 0 }) + + expect(read.snapshot.treeText.split('\n').slice(4, 6)).toEqual([ + '0 window Sent - Mail', + '\t2 button Delete' + ]) + }) + + it('keeps an elided root readable under cache pressure and forgets its replaced children', async () => { + const client = await createDesktopScriptProviderClient('linux', '/tmp/runtime.py') + const outside = Array.from({ length: 1198 }, (_, index) => ({ + index: index + 2, + runtimeId: [10, index] + })) + mockBridgeResponse({ + ok: true, + snapshot: bridgeSnapshot( + ['0 frame Mail', '\t1 panel Pane', ...outside.map(({ index }) => `\t${index} list Other`)], + [{ index: 0, runtimeId: [0] }, { index: 1, runtimeId: [1] }, ...outside] + ) + }) + await client.snapshot({ app: 'Mail' }) + + const expanded = Array.from({ length: 1199 }, (_, index) => ({ + index: index + 1, + runtimeId: [20, index] + })) + mockBridgeResponse({ + ok: true, + snapshot: bridgeSnapshot( + ['0 list Other', ...expanded.map(({ index }) => `\t${index} button Other`)], + [{ index: 0, runtimeId: [10, 0] }, ...expanded] + ) + }) + await client.snapshot({ app: 'Mail', rootElementIndex: 2 }) + + mockBridgeResponse({ + ok: true, + snapshot: bridgeSnapshot( + ['0 list Items', '\t1 button A', '\t2 button B'], + [ + { index: 0, runtimeId: [30, 0] }, + { index: 1, runtimeId: [30, 1] }, + { index: 2, runtimeId: [30, 2] } + ] + ) + }) + await client.snapshot({ app: 'Mail', rootElementIndex: 1 }) + + mockBridgeResponse({ + ok: true, + snapshot: bridgeSnapshot(['0 panel Pane'], [{ index: 0, runtimeId: [1] }]) + }) + const repeated = await client.snapshot({ app: 'Mail', rootElementIndex: 1 }) + expect(repeated.snapshot.treeText.split('\n')[4]).toBe('1 panel Pane') + + mockBridgeResponse({ ok: true, capabilities: sampleCapabilities() }) + mockBridgeResponse({ + ok: true, + snapshot: bridgeSnapshot(['0 frame Mail'], [{ index: 0, runtimeId: [0] }]) + }) + await expect(client.action('click', { app: 'Mail', elementIndex: 2399 })).rejects.toMatchObject( + { + code: 'element_not_found' + } + ) + }) +}) diff --git a/src/main/computer/desktop-script/snapshots/desktop-script-subtree-snapshot.ts b/src/main/computer/desktop-script/snapshots/desktop-script-subtree-snapshot.ts index 4a569d6d26b..4d1b8407314 100644 --- a/src/main/computer/desktop-script/snapshots/desktop-script-subtree-snapshot.ts +++ b/src/main/computer/desktop-script/snapshots/desktop-script-subtree-snapshot.ts @@ -46,10 +46,14 @@ export async function readSubtreeSnapshot( return renderSnapshot(shown, params.noScreenshot === true) } +// Two maximal provider walks: both providers stop at 1200 elements. +const MAX_CACHED_SUBTREE_SNAPSHOT_ELEMENTS = 2400 + /** * Gives a subtree read the indexes the window's cached snapshot already uses: elements it showed - * keep theirs, new ones get unused indexes. Returns what to show and what to cache, where the - * cache keeps the earlier elements so their indexes still resolve for actions. + * keep theirs, new ones get never-used indexes. Returns what to show and what to cache, where the + * cache keeps earlier elements so their indexes still resolve for actions, except those an earlier + * read of this root returned and this one did not, and the least recently returned beyond the cap. */ function indexSubtreeSnapshot( base: BridgeSnapshot, @@ -57,41 +61,128 @@ function indexSubtreeSnapshot( rootElementIndex: number ): { shown: BridgeSnapshot; cached: BridgeSnapshot } { const baseElements = base.elements ?? [] - const baseIndexByRuntimeId = new Map() - for (const element of baseElements) { + const baseByRuntimeId = new Map>() + let nextIndex = base.nextElementIndex ?? 0 + let requestedRoot: BridgeElement | undefined + // Build backwards so duplicate candidates pop in their original order. + for (let position = baseElements.length - 1; position >= 0; position--) { + const element = baseElements[position] + nextIndex = Math.max(nextIndex, element.index + 1) + if (element.index === rootElementIndex) { + requestedRoot = element + } const key = runtimeIdKey(element) - if (key !== null && !baseIndexByRuntimeId.has(key)) { - baseIndexByRuntimeId.set(key, element.index) + if (key === null) { + continue + } + const known = baseByRuntimeId.get(key) + if (!known) { + baseByRuntimeId.set(key, element) + continue + } + const signatures = + known instanceof Map ? known : new Map([[elementSignatureKey(known), [known]]]) + const signature = elementSignatureKey(element) + const candidates = signatures.get(signature) + if (candidates) { + candidates.push(element) + } else { + signatures.set(signature, [element]) + } + if (signatures !== known) { + baseByRuntimeId.set(key, signatures) } } - let nextIndex = baseElements.reduce((max, element) => Math.max(max, element.index), -1) + 1 - const indexes = new Map() + const requestedRootKey = requestedRoot ? runtimeIdKey(requestedRoot) : null + const indexes: number[] = [] const claimed = new Set() - for (const element of subtree.elements ?? []) { + const subtreeElements = subtree.elements ?? [] + const walked: BridgeElement[] = [] + walked.length = subtreeElements.length + const subtreeReadRoots = new Map() + for (let position = 0; position < subtreeElements.length; position++) { + const element = subtreeElements[position] const key = runtimeIdKey(element) - const known = key === null ? undefined : baseIndexByRuntimeId.get(key) - const index = known !== undefined && !claimed.has(known) ? known : nextIndex++ + // Why: the provider resolved the rendered root from the requested handle, so it is that element + // even if its name changed; Windows resolves index 0 only as the window, never by runtime id. + const isRequestedRoot = position === 0 && key !== null && key === requestedRootKey + let known = isRequestedRoot ? requestedRoot : undefined + if (!isRequestedRoot) { + const candidate = key === null ? undefined : baseByRuntimeId.get(key) + if (candidate instanceof Map) { + const candidates = candidate.get(elementSignatureKey(element)) + known = candidates?.pop() + while (known && claimed.has(known.index)) { + known = candidates?.pop() + } + } else if ( + candidate && + !claimed.has(candidate.index) && + sameElementSignature(candidate, element) + ) { + known = candidate + } + } + const index = known?.index ?? nextIndex++ claimed.add(index) - indexes.set(element.index, index) + indexes[element.index] = index + walked[position] = { ...element, index } + if (index !== rootElementIndex) { + subtreeReadRoots.set(index, rootElementIndex) + } } - const walked = (subtree.elements ?? []).map((element) => ({ - ...element, - index: indexes.get(element.index) ?? element.index - })) const focusedElementId = subtree.focusedElementId === null || subtree.focusedElementId === undefined ? subtree.focusedElementId - : (indexes.get(subtree.focusedElementId) ?? null) + : (indexes[subtree.focusedElementId] ?? null) + const indexLookup = { get: (index: number) => indexes[index] } const shown: BridgeSnapshot = { ...subtree, - treeLines: subtree.treeLines?.map((line) => renumberComputerTreeLine(line, indexes)), + treeLines: subtree.treeLines?.map((line) => renumberComputerTreeLine(line, indexLookup)), focusedElementId, elements: walked, rootElementIndex } + const baseReadRoots = base.subtreeReadRoots + const elidedRoot = requestedRoot && !claimed.has(rootElementIndex) ? requestedRoot : undefined + const retentionBudget = Math.max( + 0, + MAX_CACHED_SUBTREE_SNAPSHOT_ELEMENTS - walked.length - (elidedRoot ? 1 : 0) + ) + const retained: BridgeElement[] = [] + // Start at the newest cached elements and stop once the remaining budget is full. + for ( + let position = baseElements.length - 1; + position >= 0 && retained.length < retentionBudget; + position-- + ) { + const element = baseElements[position] + const readRoot = baseReadRoots?.get(element.index) + if ( + element.index !== rootElementIndex && + !claimed.has(element.index) && + readRoot !== rootElementIndex + ) { + retained.push(element) + if (readRoot !== undefined) { + subtreeReadRoots.set(element.index, readRoot) + } + } + } + retained.reverse() + const elements = retained.concat(walked) + if (elidedRoot) { + elements.push(elidedRoot) + } + const requestedReadRoot = baseReadRoots?.get(rootElementIndex) + if (requestedReadRoot !== undefined) { + subtreeReadRoots.set(rootElementIndex, requestedReadRoot) + } const cached: BridgeSnapshot = { ...shown, - elements: [...baseElements.filter((element) => !claimed.has(element.index)), ...walked] + elements, + subtreeReadRoots, + nextElementIndex: nextIndex } return { shown, cached } } @@ -101,3 +192,25 @@ function runtimeIdKey(element: BridgeElement): string | null { ? JSON.stringify(element.runtimeId) : null } + +// Mirrors the Linux resolver's same_element_signature check. +function sameElementSignature(left: BridgeElement, right: BridgeElement): boolean { + const leftActions = left.actions ?? [] + const rightActions = right.actions ?? [] + return ( + (left.controlType ?? '') === (right.controlType ?? '') && + (left.name ?? '') === (right.name ?? '') && + (left.automationId ?? '') === (right.automationId ?? '') && + leftActions.length === rightActions.length && + leftActions.every((action, position) => action === rightActions[position]) + ) +} + +function elementSignatureKey(element: BridgeElement): string { + return JSON.stringify([ + element.controlType ?? '', + element.name ?? '', + element.automationId ?? '', + element.actions ?? [] + ]) +} diff --git a/src/shared/computer-use-tree-text.ts b/src/shared/computer-use-tree-text.ts index 2e73c73e6b0..2b40ecae370 100644 --- a/src/shared/computer-use-tree-text.ts +++ b/src/shared/computer-use-tree-text.ts @@ -40,7 +40,7 @@ export function parseComputerTreeText(treeText: string): ComputerTreeText { /** Rewrites the index of an element line; other lines pass through. */ export function renumberComputerTreeLine( line: string, - indexes: ReadonlyMap + indexes: Pick, 'get'> ): string { const match = ELEMENT_LINE.exec(line) const index = match ? indexes.get(Number(match[2])) : undefined From 5bca0bf3421fe1562f5382ea9f97d2ab3ceaf543 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:46:31 -0800 Subject: [PATCH 041/178] perf(mobile): build all-tabs sheet rows only while the sheet is shown (STA-15103) (#41819) The tab bar built every tab's sheet row, including the agent preview over each terminal's last message, on every tab snapshot and 60 s tick, and the always-mounted closed sheet ran its own clock and row elements. The bar now builds only the title/dot/group summary it draws; the sheet's clock, frozen groups and detail rows live in content mounted only while the drawer is shown. --- .../src/session/tabs/MobileSessionTabBar.tsx | 13 +- .../tabs/MobileSessionTabsSheet.test.tsx | 122 ++++++++++++++++++ .../session/tabs/MobileSessionTabsSheet.tsx | 83 ++++++++---- .../session/tabs/mobile-session-tab-rows.ts | 40 ++++-- 4 files changed, 211 insertions(+), 47 deletions(-) create mode 100644 mobile/src/session/tabs/MobileSessionTabsSheet.test.tsx diff --git a/mobile/src/session/tabs/MobileSessionTabBar.tsx b/mobile/src/session/tabs/MobileSessionTabBar.tsx index 7dca5a6de06..bd630336b30 100644 --- a/mobile/src/session/tabs/MobileSessionTabBar.tsx +++ b/mobile/src/session/tabs/MobileSessionTabBar.tsx @@ -23,7 +23,10 @@ import { agentStateLabel, type AgentDotState } from '../../worktree/agent-row-di import { colors, feedback, radii, spacing } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' -import { buildMobileSessionTabRow, type MobileSessionTabRow } from './mobile-session-tab-rows' +import { + buildMobileSessionTabSummary, + type MobileSessionTabSummary +} from './mobile-session-tab-rows' import { MobileSessionTabMark } from './MobileSessionTabMark' import { MobileSessionTabsSheet } from './MobileSessionTabsSheet' import type { MobileSessionController } from '../use-mobile-session-controller' @@ -84,11 +87,11 @@ export function MobileSessionTabBar({ const keepsKeyboard = useKeyboardPersistingTaps('handled') const now = useNow(60_000) const rows = useMemo( - () => visibleTabs.map((tab) => buildMobileSessionTabRow(tab, terminals, now)), + () => visibleTabs.map((tab) => buildMobileSessionTabSummary(tab, terminals, now)), [visibleTabs, terminals, now] ) const activeIndex = rows.findIndex((row) => row.tab.id === activeSessionTabId) - const active: MobileSessionTabRow | undefined = rows[activeIndex] + const active: MobileSessionTabSummary | undefined = rows[activeIndex] const previous = activeIndex > 0 ? rows[activeIndex - 1] : undefined const next = activeIndex !== -1 ? rows[activeIndex + 1] : undefined const othersNeedYou = rows.some( @@ -108,7 +111,7 @@ export function MobileSessionTabBar({ ? { duration: 0, reduceMotion: ReduceMotion.Never } : { duration: SWIPE_SETTLE_MS, easing: Easing.bezier(...SHEET_CURVE) } ) - const step = (target: MobileSessionTabRow | undefined, direction: 1 | -1) => { + const step = (target: MobileSessionTabSummary | undefined, direction: 1 | -1) => { if (!target) { offset.value = settle(0) return @@ -269,7 +272,7 @@ export function MobileSessionTabBar({ ) : null} setSheetOpen(false)} /> diff --git a/mobile/src/session/tabs/MobileSessionTabsSheet.test.tsx b/mobile/src/session/tabs/MobileSessionTabsSheet.test.tsx new file mode 100644 index 00000000000..4ea55afbcb6 --- /dev/null +++ b/mobile/src/session/tabs/MobileSessionTabsSheet.test.tsx @@ -0,0 +1,122 @@ +import { createElement, type ReactNode } from 'react' +import { act, create, type ReactTestRenderer } from 'react-test-renderer' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { MobileSessionController } from '../use-mobile-session-controller' +import type { MobileSessionTabSummary } from './mobile-session-tab-rows' + +vi.mock('react-native', async () => { + const { createElement: h } = await import('react') + return { + AppState: { currentState: 'active', addEventListener: () => ({ remove: () => {} }) }, + // Calls a render-function child the way the real Pressable does, so rows draw. + Pressable: ({ + children, + ...props + }: { + children?: ReactNode | ((state: { pressed: boolean }) => ReactNode) + }) => + h( + 'Pressable', + props, + typeof children === 'function' ? children({ pressed: false }) : children + ), + StyleSheet: { create: (styles: T) => styles, absoluteFill: {} }, + Text: 'Text', + View: 'View' + } +}) +vi.mock('lucide-react-native', () => ({ Plus: 'Plus', X: 'X' })) +// Like the real drawer, draws its content only while presented. +vi.mock('../../components/bottom-drawer/BottomDrawer', () => ({ + BottomDrawer: ({ visible, children }: { visible: boolean; children: ReactNode }) => + visible ? children : null +})) +vi.mock('../../components/SwipeRowActions', () => ({ + SwipeRowActions: ({ + children + }: { + children: (guard: (onPress: () => void) => () => void) => ReactNode + }) => children((onPress) => onPress) +})) +vi.mock('../../components/list/ListGroup', () => ({ + ListGroup: ({ children }: { children: ReactNode }) => children, + SectionHeader: 'SectionHeader' +})) +vi.mock('../../components/AgentStateDot', () => ({ AgentStateDot: 'AgentStateDot' })) +vi.mock('../../components/nav-bar/NavBarButton', () => ({ NavBarButton: 'NavBarButton' })) +vi.mock('../../components/nav-bar/SheetHeader', () => ({ SheetHeader: 'SheetHeader' })) +vi.mock('./MobileSessionTabMark', () => ({ MobileSessionTabMark: 'MobileSessionTabMark' })) + +const { MobileSessionTabsSheet } = await import('./MobileSessionTabsSheet') + +const NOW = 1_700_000_000_000 + +function terminalTab(id: string, stateStartedAt: number): MobileSessionTabSummary { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the sheet reads only these status fields; the rest of the host's status entry is unused here. + return { + tab: { + type: 'terminal', + id, + title: id, + isActive: false, + terminal: null, + agentStatus: { + state: 'waiting', + prompt: 'Fix the bug', + updatedAt: stateStartedAt, + stateStartedAt + } + }, + title: id, + agentId: null, + dotState: 'waiting', + group: 'needs-you' + } as unknown as MobileSessionTabSummary +} + +// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the sheet reads only these controller members. +const controller = { + activeSessionTabId: 'open-tab', + worktreeName: 'main', + switchSessionTab: () => {}, + handleCloseSessionTab: async () => {}, + handleOpenNewTab: () => {}, + createTabBusy: false, + connState: 'connected' +} as unknown as MobileSessionController + +describe('MobileSessionTabsSheet', () => { + let renderer: ReactTestRenderer | null = null + + beforeEach(() => { + vi.useFakeTimers({ now: NOW }) + }) + afterEach(() => { + act(() => renderer?.unmount()) + renderer = null + vi.useRealTimers() + }) + + it('shows how long ago each tab changed as of opening, not as of when the bar mounted', () => { + const tabs = [terminalTab('open-tab', NOW), terminalTab('other-tab', NOW - 30_000)] + const sheet = (visible: boolean) => + createElement(MobileSessionTabsSheet, { controller, tabs, visible, onClose: () => {} }) + act(() => { + renderer = create(sheet(false)) + }) + // Opened 45 s later, before any minute tick: the other tab changed 75 s ago. + act(() => { + vi.advanceTimersByTime(45_000) + }) + act(() => renderer!.update(sheet(true))) + + const otherRow = renderer!.root.find( + (node) => + String(node.type) === 'Pressable' && node.props.accessibilityState?.selected === false + ) + const times = otherRow + .findAll((node) => String(node.type) === 'Text') + .map((node) => node.props.children) + expect(times).toContain('1m') + }) +}) diff --git a/mobile/src/session/tabs/MobileSessionTabsSheet.tsx b/mobile/src/session/tabs/MobileSessionTabsSheet.tsx index f8cd5cd1981..6b51ca5f75d 100644 --- a/mobile/src/session/tabs/MobileSessionTabsSheet.tsx +++ b/mobile/src/session/tabs/MobileSessionTabsSheet.tsx @@ -1,4 +1,4 @@ -import { Fragment, useRef, useState } from 'react' +import { Fragment, useMemo, useRef, useState } from 'react' import { Pressable, Text, View } from 'react-native' import { Plus, X } from 'lucide-react-native' import { BottomDrawer } from '../../components/bottom-drawer/BottomDrawer' @@ -13,7 +13,12 @@ import { formatTimeAgo } from '../../worktree/agent-row-display' import { colors, radii, spacing } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' -import type { MobileSessionTabGroup, MobileSessionTabRow } from './mobile-session-tab-rows' +import { + buildMobileSessionTabRow, + type MobileSessionTabGroup, + type MobileSessionTabRow, + type MobileSessionTabSummary +} from './mobile-session-tab-rows' import { MobileSessionTabMark } from './MobileSessionTabMark' import type { MobileSessionController } from '../use-mobile-session-controller' @@ -24,14 +29,51 @@ const GROUP_ORDER: readonly MobileSessionTabGroup[] = ['needs-you', 'working', ' /** Every tab in the workspace, what needs you first; tap one to go there, swipe one to close it. */ export function MobileSessionTabsSheet({ controller, - rows, + tabs, visible, onClose }: { controller: MobileSessionController - rows: readonly MobileSessionTabRow[] + tabs: readonly MobileSessionTabSummary[] visible: boolean onClose: () => void +}) { + const afterCloseRef = useRef<(() => void) | null>(null) + // Why: the drawer draws its content only while shown, so a closed sheet runs no clock and builds no rows (STA-15103). + return ( + { + const afterClose = afterCloseRef.current + afterCloseRef.current = null + afterClose?.() + }} + > + { + // The new-tab sheet is its own drawer; open it once this one is gone. + afterCloseRef.current = controller.handleOpenNewTab + onClose() + }} + /> + + ) +} + +function MobileSessionTabsSheetContent({ + controller, + tabs, + onClose, + onOpenNewTab +}: { + controller: MobileSessionController + tabs: readonly MobileSessionTabSummary[] + onClose: () => void + onOpenNewTab: () => void }) { useMobileColorScheme() const t = useMobileTranslation() @@ -41,19 +83,17 @@ export function MobileSessionTabsSheet({ worktreeName, switchSessionTab, handleCloseSessionTab, - handleOpenNewTab, createTabBusy, connState } = controller - // Sections hold still while the sheet is open, so a row never moves under a finger. - const [frozenGroups, setFrozenGroups] = useState>() - if (visible && frozenGroups === undefined) { - setFrozenGroups(new Map(rows.map((row) => [row.tab.id, row.group]))) - } - const afterCloseRef = useRef<(() => void) | null>(null) + // Sections hold still while the sheet is shown (closing included), so a row never moves under a finger. + const [frozenGroups] = useState>( + () => new Map(tabs.map((tab) => [tab.tab.id, tab.group])) + ) + const rows = useMemo(() => tabs.map((tab) => buildMobileSessionTabRow(tab)), [tabs]) const sections = GROUP_ORDER.map((group) => ({ group, - rows: rows.filter((row) => (frozenGroups?.get(row.tab.id) ?? row.group) === group) + rows: rows.filter((row) => (frozenGroups.get(row.tab.id) ?? row.group) === group) })).filter((section) => section.rows.length > 0) const sectionTitles: Record = { 'needs-you': t('session.tabsSheet.needsYou', 'Needs you'), @@ -131,16 +171,7 @@ export function MobileSessionTabsSheet({ } return ( - { - setFrozenGroups(undefined) - const afterClose = afterCloseRef.current - afterCloseRef.current = null - afterClose?.() - }} - > + <> @@ -148,11 +179,7 @@ export function MobileSessionTabsSheet({ icon={Plus} disabled={createTabBusy || connState !== 'connected'} accessibilityLabel={t('session.tabStrip.newTab', 'New tab')} - onPress={() => { - // The new-tab sheet is its own drawer; open it once this one is gone. - afterCloseRef.current = handleOpenNewTab - onClose() - }} + onPress={onOpenNewTab} /> @@ -168,7 +195,7 @@ export function MobileSessionTabsSheet({ ))} - + ) } diff --git a/mobile/src/session/tabs/mobile-session-tab-rows.ts b/mobile/src/session/tabs/mobile-session-tab-rows.ts index 1b2854512f6..e667bbc4b16 100644 --- a/mobile/src/session/tabs/mobile-session-tab-rows.ts +++ b/mobile/src/session/tabs/mobile-session-tab-rows.ts @@ -16,13 +16,18 @@ import type { MobileSessionTab } from './mobile-session-tabs' export type MobileSessionTabGroup = 'needs-you' | 'working' | 'other' -export type MobileSessionTabRow = { +/** What the tab bar draws for every tab; cheap enough to rebuild on each tab snapshot and clock tick. */ +export type MobileSessionTabSummary = { tab: MobileSessionTab title: string /** The agent whose icon marks the tab; null draws the tab kind's icon. */ agentId: string | null dotState: AgentDotState | undefined group: MobileSessionTabGroup +} + +/** A row of the all-tabs sheet: the summary plus what only the open sheet shows. */ +export type MobileSessionTabRow = MobileSessionTabSummary & { /** The second line: the state, then what the tab is doing or showing. */ detail: string timeAt: number | null @@ -88,14 +93,30 @@ function tabKindLabel(tab: MobileSessionTab): string { } } -export function buildMobileSessionTabRow( +export function buildMobileSessionTabSummary( tab: MobileSessionTab, terminals: readonly { handle: string; agentIdentity?: string }[], now: number -): MobileSessionTabRow { +): MobileSessionTabSummary { const hostAgentIdentity = getMobileTerminalTabHostAgentIdentity(tab, terminals) - const title = getMobileSessionTabTitle(tab, hostAgentIdentity) const dotState = mobileSessionTabDotState(tab, now) + return { + tab, + title: getMobileSessionTabTitle(tab, hostAgentIdentity), + agentId: + tab.type === 'agent-session' + ? tab.agent + : tab.type === 'terminal' + ? resolveMobileTerminalTabAgentId(tab, hostAgentIdentity) + : null, + dotState, + group: tabGroup(dotState) + } +} + +// Why: the preview scans each agent's last message, so only the open sheet pays for it (STA-15103). +export function buildMobileSessionTabRow(summary: MobileSessionTabSummary): MobileSessionTabRow { + const { tab, title, dotState } = summary const stateWord = dotState && dotState !== 'idle' ? agentStateLabel(dotState) : '' const content = tabContent(tab, title, dotState) const timeAt = @@ -105,16 +126,7 @@ export function buildMobileSessionTabRow( ? tab.chatStatus.updatedAt : null return { - tab, - title, - agentId: - tab.type === 'agent-session' - ? tab.agent - : tab.type === 'terminal' - ? resolveMobileTerminalTabAgentId(tab, hostAgentIdentity) - : null, - dotState, - group: tabGroup(dotState), + ...summary, detail: [stateWord, content].filter(Boolean).join(' · ') || tabKindLabel(tab), timeAt } From b114149bd5309bea83692ae06cd419aa51c568c5 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 12 Nov 2026 23:49:42 -0800 Subject: [PATCH 042/178] test: remove mocked automation projection and retry suites (#41890) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 1 - .../AutomationsPage.behavior.test.tsx | 2 - ...tionsPage.cross-authority-actions.spec.tsx | 116 ------------- .../AutomationsPage.notice-recovery.spec.tsx | 122 -------------- .../agent-status-pending-retry-gate.test.ts | 156 ------------------ 5 files changed, 397 deletions(-) delete mode 100644 src/renderer/src/components/automations/AutomationsPage.cross-authority-actions.spec.tsx delete mode 100644 src/renderer/src/components/automations/AutomationsPage.notice-recovery.spec.tsx delete mode 100644 src/renderer/src/hooks/ipc-events/agent-status-pending-retry-gate.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 97e7cb34c64..dd6da1357fc 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -5433,7 +5433,6 @@ "src/renderer/src/hooks/editor-external-watch-selected-host.test.ts": 1988, "src/renderer/src/hooks/editor-runtime-file-watch.test.ts": 58, "src/renderer/src/hooks/ipc-events/agent-status-pane-routing-index-retention.test.ts": 4878, - "src/renderer/src/hooks/ipc-events/agent-status-pending-retry-gate.test.ts": 466, "src/renderer/src/hooks/ipc-events/agent-status-terminal-title-tab-write.test.ts": 2247, "src/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.owner.test.ts": 623, "src/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.split.test.ts": 699, diff --git a/src/renderer/src/components/automations/AutomationsPage.behavior.test.tsx b/src/renderer/src/components/automations/AutomationsPage.behavior.test.tsx index 50df7c0acf6..c5510a4f3e1 100644 --- a/src/renderer/src/components/automations/AutomationsPage.behavior.test.tsx +++ b/src/renderer/src/components/automations/AutomationsPage.behavior.test.tsx @@ -1,6 +1,4 @@ // @vitest-environment happy-dom -import './AutomationsPage.cross-authority-actions.spec' import './AutomationsPage.escape-precedence.spec' import './AutomationsPage.external-scope.spec' -import './AutomationsPage.notice-recovery.spec' import './AutomationsPage.refresh-selection.spec' diff --git a/src/renderer/src/components/automations/AutomationsPage.cross-authority-actions.spec.tsx b/src/renderer/src/components/automations/AutomationsPage.cross-authority-actions.spec.tsx deleted file mode 100644 index 5e7a062f7d0..00000000000 --- a/src/renderer/src/components/automations/AutomationsPage.cross-authority-actions.spec.tsx +++ /dev/null @@ -1,116 +0,0 @@ -// @vitest-environment happy-dom - -/** Automation IDs are authority-local: desktop-first/runtime-last collisions must still route each action through its own captured row owner. */ - -import { act } from 'react' -import { describe, expect, it } from 'vitest' -import { - api, - installAutomationsPageHarness, - mocks, - renderPage, - runtimeHost, - scopedList, - SELF_PRECONDITION, - settleHostQueries -} from './automations-page-test-harness' -import { listedRows } from './automations-page-listed-items' -import { makeAutomation } from './automations-page-fixtures' - -/** Desktop and runtime both hold `a-1`; the runtime's row is committed last. */ -async function collidingHosts(): Promise { - runtimeHost([makeAutomation({ id: 'a-1', name: 'Remote nightly' })], []) - const desktop = makeAutomation({ id: 'a-1', name: 'Desktop nightly' }) - scopedList([desktop]) - api.automations.list.mockResolvedValue([desktop]) - mocks.state.selectedAutomationId = 'a-1' -} - -function selectDesktopRow(): string { - const row = listedRows().find((candidate) => candidate.automation.name === 'Desktop nightly') - expect(row).toBeDefined() - return row?.key ?? '' -} - -/** Collect runtime automation RPCs only; exclude local calls so wrong-authority actions cannot hide in request noise. */ -function runtimeAutomationCalls(): string[] { - return mocks.callRuntimeRpc.mock.calls - .filter((call) => (call[0] as { kind?: string } | null)?.kind === 'environment') - .map((call) => String(call[1])) - .filter((method) => method.startsWith('automation.')) -} - -describe('AutomationsPage row actions under a colliding automation id', () => { - installAutomationsPageHarness() - - it('runs the selected desktop row on the desktop, not on the runtime holding the same id', async () => { - await collidingHosts() - const { container } = await renderPage() - await settleHostQueries() - expect( - [...container.querySelectorAll('[data-testid="automation-row"]')].map( - (row) => row.textContent - ) - ).toEqual(['Desktop nightly', 'Remote nightly']) - - await act(async () => { - mocks.listPanel?.selectAutomationRow(selectDesktopRow()) - }) - expect(mocks.detailPane?.selected?.name).toBe('Desktop nightly') - mocks.callRuntimeRpc.mockClear() - await act(async () => { - mocks.detailPane?.runNow(mocks.detailPane.selected!) - }) - - expect(runtimeAutomationCalls()).not.toContain('automation.runNow') - expect(api.automations.runNow).toHaveBeenCalledWith({ - id: 'a-1', - expectedOwner: SELF_PRECONDITION - }) - }) - - it('pauses the selected desktop row on the desktop', async () => { - await collidingHosts() - await renderPage() - await settleHostQueries() - - await act(async () => { - mocks.listPanel?.selectAutomationRow(selectDesktopRow()) - }) - mocks.callRuntimeRpc.mockClear() - await act(async () => { - mocks.detailPane?.toggleAutomation(mocks.detailPane.selected!) - }) - - expect(api.automations.update).toHaveBeenCalledWith({ - id: 'a-1', - updates: { enabled: false }, - expectedOwner: SELF_PRECONDITION, - destination: undefined - }) - expect(runtimeAutomationCalls()).not.toContain('automation.update') - }) - - it('deletes the selected desktop row on the desktop', async () => { - await collidingHosts() - await renderPage() - await settleHostQueries() - - await act(async () => { - mocks.listPanel?.selectAutomationRow(selectDesktopRow()) - }) - await act(async () => { - mocks.detailPane?.requestDeleteAutomation(mocks.detailPane.selected!) - }) - mocks.callRuntimeRpc.mockClear() - await act(async () => { - mocks.deleteDialog?.onConfirm() - }) - - expect(api.automations.delete).toHaveBeenCalledWith({ - id: 'a-1', - expectedOwner: SELF_PRECONDITION - }) - expect(runtimeAutomationCalls()).not.toContain('automation.delete') - }) -}) diff --git a/src/renderer/src/components/automations/AutomationsPage.notice-recovery.spec.tsx b/src/renderer/src/components/automations/AutomationsPage.notice-recovery.spec.tsx deleted file mode 100644 index cdf01d49e5d..00000000000 --- a/src/renderer/src/components/automations/AutomationsPage.notice-recovery.spec.tsx +++ /dev/null @@ -1,122 +0,0 @@ -// @vitest-environment happy-dom - -/** Route Retry/Reconnect/Update server to the refused row owner or captured create destination; the current list filter cannot identify it. */ - -import { act } from 'react' -import { describe, expect, it, vi } from 'vitest' -import { - addRuntimeProject, - api, - installAutomationsPageHarness, - mocks, - renderPage, - runtimeHost, - RUNTIME_ID, - RUNTIME_SELF_FILTER, - scopedList, - settleHostQueries -} from './automations-page-test-harness' -import { RUNTIME_REPO_ID, RUNTIME_WORKSPACE_ID } from './automations-page-runtime-fixtures' -import { listedRow } from './automations-page-listed-items' -import { makeAutomation } from './automations-page-fixtures' - -const FENCING_REQUIRED = 'The host changed: automation_owner_fencing_required' - -/** Fails one runtime RPC the way an under-versioned server does, leaving the rest answering. */ -function runtimeRpcRefuses(method: string): void { - const previous = mocks.callRuntimeRpc.getMockImplementation() - mocks.callRuntimeRpc.mockImplementation( - async (target: unknown, called: string, params: unknown, options: unknown) => { - if (called === method) { - throw new Error(FENCING_REQUIRED) - } - return await previous?.(target, called, params, options) - } - ) -} - -function watchSettings(): { open: ReturnType; target: ReturnType } { - const open = vi.fn() - const target = vi.fn() - mocks.state.openSettingsPage = open - mocks.state.openSettingsTarget = target - return { open, target } -} - -describe('AutomationsPage notice recovery', () => { - installAutomationsPageHarness() - - it('sends the create dialog Update server to where that destination host is versioned', async () => { - api.automations.list.mockResolvedValue([]) - scopedList([]) - runtimeHost([], []) - runtimeRpcRefuses('automation.create') - // The runtime's own project: a desktop repo id is refused before any RPC. - addRuntimeProject() - mocks.state.automationHostFilter = RUNTIME_SELF_FILTER - const settings = watchSettings() - - await renderPage() - await settleHostQueries() - await act(async () => { - mocks.listPanel?.openCreateDialog() - }) - await act(async () => { - mocks.editorDialog?.onDraftChange((current) => ({ - ...(current as Record), - name: 'Sweep', - prompt: 'Do the sweep', - projectId: RUNTIME_REPO_ID, - workspaceMode: 'existing', - workspaceId: RUNTIME_WORKSPACE_ID - })) - }) - await act(async () => { - mocks.editorDialog?.onSave() - }) - - expect(mocks.editorDialog?.notice?.recovery).toBe('update-server') - await act(async () => { - mocks.editorDialog?.onNoticeRecover?.('update-server') - }) - - expect(settings.open).toHaveBeenCalled() - expect(settings.target).toHaveBeenCalledWith({ - pane: 'servers', - repoId: null, - sectionId: RUNTIME_ID - }) - }) - - it('sends a row Update server to the row own host, not to the unfiltered list', async () => { - const runtimeAutomation = makeAutomation({ id: 'a-runtime', name: 'On the box' }) - api.automations.list.mockResolvedValue([]) - scopedList([]) - runtimeHost([runtimeAutomation], []) - runtimeRpcRefuses('automation.update') - const settings = watchSettings() - - const { container } = await renderPage() - await settleHostQueries() - await act(async () => { - mocks.listPanel?.toggleAutomation(listedRow(runtimeAutomation.id)) - }) - - const notice = container.querySelector('[data-testid="automation-owner-conflict"]') - const recover = [...(notice?.querySelectorAll('button') ?? [])].find( - (button) => button.textContent === 'Update server' - ) - expect(recover).toBeDefined() - await act(async () => { - recover?.click() - }) - - // All hosts selects no host, so only the row's captured owner names one. - expect(settings.open).toHaveBeenCalled() - expect(settings.target).toHaveBeenCalledWith({ - pane: 'servers', - repoId: null, - sectionId: RUNTIME_ID - }) - }) -}) diff --git a/src/renderer/src/hooks/ipc-events/agent-status-pending-retry-gate.test.ts b/src/renderer/src/hooks/ipc-events/agent-status-pending-retry-gate.test.ts deleted file mode 100644 index 1053f0e0ddc..00000000000 --- a/src/renderer/src/hooks/ipc-events/agent-status-pending-retry-gate.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { AgentStatusBatchUpdate } from '../../store/slices/agent-status/agent-status-contract' -import { - buildStoreState, - FUTURE_LEAF_ID, - FUTURE_PANE_KEY, - type AgentStatusSetData, - type StoreLike, - type StoreSubscribeListener -} from '../ipc-events-agent-status-store-test-fixtures' - -type PendingRetryHarness = { - bridge: { disposeAsyncState: () => void; unsubscribeStore: () => void } - emitPendingStatus: () => void - publish: (mutate: (state: StoreLike) => void) => void - setAgentStatuses: ReturnType - transactAgentStatuses: ReturnType -} - -async function createPendingRetryHarness(): Promise { - const subscribeListenerRef: { current: StoreSubscribeListener | null } = { current: null } - const onSetListenerRef: { current: ((data: AgentStatusSetData) => void) | null } = { - current: null - } - const setAgentStatuses = vi.fn((updates: readonly AgentStatusBatchUpdate[]) => - updates.map(() => true) - ) - const storeState = buildStoreState({ - setAgentStatuses, - workspaceSessionReady: true, - tabsByWorktree: {}, - terminalLayoutsByTabId: {}, - settings: { terminalFontSize: 13, notifications: { enabled: false } } - }) - const transactImplementation = storeState.transactAgentStatuses as (...args: unknown[]) => unknown - const transactAgentStatuses = vi.fn(transactImplementation) - storeState.transactAgentStatuses = transactAgentStatuses - - vi.doMock('../../store', () => ({ - useAppStore: { - subscribe: vi.fn((listener: StoreSubscribeListener) => { - subscribeListenerRef.current = listener - return () => { - subscribeListenerRef.current = null - } - }), - getState: () => storeState - } - })) - vi.doMock('../agent-hook-completion-notifications', () => ({ - observeAgentHookCompletionForNotification: vi.fn(), - syncAgentHookCompletionNotificationsForStoreUpdate: vi.fn() - })) - vi.stubGlobal('window', { - api: { - agentStatus: { - onSet: (listener: (data: AgentStatusSetData) => void) => { - onSetListenerRef.current = listener - return () => { - onSetListenerRef.current = null - } - } - } - } - }) - - const { registerAgentStatusIpcBridge } = await import('./agent-status-ipc-bridge') - const bridge = registerAgentStatusIpcBridge([]) - if (!subscribeListenerRef.current || !onSetListenerRef.current) { - throw new Error('Expected agent-status bridge listeners') - } - - return { - bridge, - emitPendingStatus: () => { - onSetListenerRef.current?.({ - paneKey: FUTURE_PANE_KEY, - state: 'working', - prompt: 'pending attribution', - agentType: 'claude', - receivedAt: 1_700_000_000_000, - stateStartedAt: 1_700_000_000_000 - }) - }, - publish: (mutate) => { - const previousState = { ...storeState } - mutate(storeState) - subscribeListenerRef.current?.(storeState, previousState) - }, - setAgentStatuses, - transactAgentStatuses - } -} - -describe('agent status pending retry gate', () => { - beforeEach(() => { - vi.resetModules() - vi.useFakeTimers() - vi.setSystemTime(1_700_000_100_000) - }) - - afterEach(() => { - vi.useRealTimers() - vi.unstubAllGlobals() - }) - - it('skips retry transactions for unrelated publications and retries on hydration', async () => { - const harness = await createPendingRetryHarness() - harness.emitPendingStatus() - - for (let updateIndex = 0; updateIndex < 2_400; updateIndex += 1) { - harness.publish((state) => { - state.cosmeticUpdateIndex = updateIndex - }) - } - - expect(harness.transactAgentStatuses).not.toHaveBeenCalled() - - harness.publish((state) => { - state.tabsByWorktree = { - 'wt-1': [{ id: 'tab-future', ptyId: 'pty-1', worktreeId: 'wt-1', title: 'Future' }] - } - state.terminalLayoutsByTabId = { - 'tab-future': { - root: { type: 'leaf', leafId: FUTURE_LEAF_ID }, - activeLeafId: FUTURE_LEAF_ID, - expandedLeafId: null - } - } - }) - - expect(harness.transactAgentStatuses).toHaveBeenCalledTimes(1) - expect(harness.setAgentStatuses).toHaveBeenCalledWith([ - expect.objectContaining({ paneKey: FUTURE_PANE_KEY }) - ]) - vi.advanceTimersByTime(100) - expect(harness.transactAgentStatuses).toHaveBeenCalledTimes(1) - harness.bridge.unsubscribeStore() - harness.bridge.disposeAsyncState() - }) - - it('keeps the timer fallback when routing references stay unchanged', async () => { - const harness = await createPendingRetryHarness() - harness.emitPendingStatus() - harness.publish((state) => { - state.cosmeticUpdateIndex = 1 - }) - - expect(harness.transactAgentStatuses).not.toHaveBeenCalled() - vi.advanceTimersByTime(100) - expect(harness.transactAgentStatuses).toHaveBeenCalledTimes(1) - expect(harness.setAgentStatuses).toHaveBeenCalledWith([]) - harness.bridge.unsubscribeStore() - harness.bridge.disposeAsyncState() - }) -}) From 66db0a6a5057321edc789b382f0277b5fbf76074 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:51:02 -0800 Subject: [PATCH 043/178] Stop routed GitHub link previews rescanning repos on unrelated store updates (#41809) * fix(native-chat): stop routed GitHub previews rescanning repos on unrelated store updates Co-Authored-By: Claude Opus 5.5 * test(native-chat): assert routed preview stays mounted instead of counting scans Co-Authored-By: Claude Opus 5.5 * fix(native-chat): refresh routed previews when cached slugs recover * perf(native-chat): update preview routes from changed slug keys --------- Co-authored-by: Claude Opus 5.5 --- .../previews/NativeChatGitHubLinkPeek.tsx | 50 ++++- .../native-chat-github-link-preview.ts | 60 ++++-- .../previews/native-chat-github-route-repo.ts | 39 +++- .../native-chat-github-route-store.ts | 176 ++++++++++++++++ ...-chat-github-routed-preview-store.test.tsx | 197 ++++++++++++++++++ .../lib/workspace/catalog/repo-slug-cache.ts | 30 +++ 6 files changed, 513 insertions(+), 39 deletions(-) create mode 100644 src/renderer/src/components/native-chat/links/previews/native-chat-github-route-store.ts create mode 100644 src/renderer/src/components/native-chat/links/previews/native-chat-github-routed-preview-store.test.tsx diff --git a/src/renderer/src/components/native-chat/links/previews/NativeChatGitHubLinkPeek.tsx b/src/renderer/src/components/native-chat/links/previews/NativeChatGitHubLinkPeek.tsx index e90c95efad8..3188ec068c0 100644 --- a/src/renderer/src/components/native-chat/links/previews/NativeChatGitHubLinkPeek.tsx +++ b/src/renderer/src/components/native-chat/links/previews/NativeChatGitHubLinkPeek.tsx @@ -1,4 +1,4 @@ -import type { ReactNode } from 'react' +import { useMemo, useSyncExternalStore, type ReactNode } from 'react' import { useAppStore } from '@/store/index' import { Button } from '@/components/ui/button' import { GitHubIcon } from '@/components/icons/GitHubIcon' @@ -28,6 +28,13 @@ import { type NativeChatGitHubRepo } from './native-chat-github-repo' import { useNativeChatHoverPreview } from './use-native-chat-hover-preview' +import { + createGitHubPreviewRouteStore, + type GitHubPreviewRouteStore +} from './native-chat-github-route-store' + +const subscribeNothing = (): (() => void) => () => {} +const readNoRepo = (): null => null function PeekFooter({ item }: { item: GitHubWorkItem }): React.JSX.Element { const t = useTranslate() @@ -79,17 +86,23 @@ export type GitHubPeekSource = | { kind: 'repo'; repo: Repo } | { kind: 'routed'; authorityKey: HostAuthorityKey } -function peekLookup(source: GitHubPeekSource, link: GitHubIssueOrPRLink) { +function peekLookup( + source: GitHubPeekSource, + link: GitHubIssueOrPRLink, + key: string, + route: GitHubPreviewRouteStore | null +) { return source.kind === 'repo' ? { - key: githubLinkPreviewKey(source.repo, link), + key, read: () => readCachedGitHubLinkPreview(source.repo, link), load: () => loadGitHubLinkPreview(source.repo, link) } : { - key: routedGitHubLinkPreviewKey(source.authorityKey, link), - read: () => readCachedRoutedGitHubLinkPreview(source.authorityKey, link), - load: () => loadRoutedGitHubLinkPreview(source.authorityKey, link) + key, + read: () => + readCachedRoutedGitHubLinkPreview(source.authorityKey, link, route ?? undefined), + load: () => loadRoutedGitHubLinkPreview(source.authorityKey, link, route ?? undefined) } } @@ -105,15 +118,30 @@ export function NativeChatGitHubPeekBody({ * Null shows just the name until then. Undefined for the chat repo's own links. */ otherRepoFallback?: ReactNode }): ReactNode { - const key = useAppStore((state) => - source.kind === 'repo' - ? githubLinkPreviewKey(source.repo, link) - : routedGitHubLinkPreviewKey(source.authorityKey, link, state.repos) + // Select the catalog itself so unrelated store updates don't rescan it for a route. + const routeRepos = useAppStore((state) => (source.kind === 'routed' ? state.repos : null)) + const route = useMemo( + () => + source.kind === 'routed' + ? createGitHubPreviewRouteStore(routeRepos ?? [], source.authorityKey, link) + : null, + [source, link, routeRepos] + ) + const routedRepo = useSyncExternalStore( + route?.subscribe ?? subscribeNothing, + route?.read ?? readNoRepo + ) + const key = useMemo( + () => + source.kind === 'repo' + ? githubLinkPreviewKey(source.repo, link) + : routedGitHubLinkPreviewKey(source.authorityKey, link, routedRepo), + [source, link, routedRepo] ) return ( diff --git a/src/renderer/src/components/native-chat/links/previews/native-chat-github-link-preview.ts b/src/renderer/src/components/native-chat/links/previews/native-chat-github-link-preview.ts index d3e1e3fbfa6..2b7ebe3a6d0 100644 --- a/src/renderer/src/components/native-chat/links/previews/native-chat-github-link-preview.ts +++ b/src/renderer/src/components/native-chat/links/previews/native-chat-github-link-preview.ts @@ -7,6 +7,7 @@ import { githubRepoIdentityKey } from '../../../../../../shared/github/repositor import { buildTaskSourceContextFromRepo } from '../../../../../../shared/work-items/task-source-context' import type { HostAuthorityKey } from '../../../../../../shared/host-authority' import { createHoverPreviewCache } from './native-chat-hover-preview-cache' +import type { GitHubPreviewRouteStore } from './native-chat-github-route-store' import { findGitHubPreviewRouteRepo, githubPreviewRepoAuthorityKey, @@ -91,9 +92,14 @@ export function reloadGitHubLinkPreview( export function routedGitHubLinkPreviewKey( routeAuthority: HostAuthorityKey, link: GitHubIssueOrPRLink, - repos: readonly Repo[] = useAppStore.getState().repos + route: readonly Repo[] | Repo | null = useAppStore.getState().repos ): string { - const repo = readGitHubPreviewRouteRepo(repos, routeAuthority, link) + const repo = + route === null + ? null + : 'id' in route + ? route + : readGitHubPreviewRouteRepo(route, routeAuthority, link) return repo ? githubLinkPreviewKey(repo, link) : `${routeAuthority}:unrouted:${githubRepoIdentityKey(link.slug)}#${link.number}:${link.type}` @@ -101,35 +107,55 @@ export function routedGitHubLinkPreviewKey( export function readCachedRoutedGitHubLinkPreview( routeAuthority: HostAuthorityKey, - link: GitHubIssueOrPRLink + link: GitHubIssueOrPRLink, + route?: GitHubPreviewRouteStore ): GitHubWorkItem | null { - return previews.read(routedGitHubLinkPreviewKey(routeAuthority, link)) + return previews.read(routedGitHubLinkPreviewKey(routeAuthority, link, route?.read())) } -export function loadRoutedGitHubLinkPreview( +export async function loadRoutedGitHubLinkPreview( routeAuthority: HostAuthorityKey, - link: GitHubIssueOrPRLink + link: GitHubIssueOrPRLink, + route?: GitHubPreviewRouteStore ): Promise { - return loadPreviewFromCatalog( - routeAuthority, - link, - (repos) => findGitHubPreviewRouteRepo(repos, routeAuthority, link), - (repos) => readGitHubPreviewRouteRepo(repos, routeAuthority, link) - ) + const snapshot = route?.repos === useAppStore.getState().repos ? route : undefined + // Keep routing current until a pending request settles, including after its card unmounts. + const stop = snapshot?.retain() + try { + return await loadPreviewFromCatalog( + routeAuthority, + link, + (repos) => + snapshot?.repos === repos + ? snapshot.find() + : findGitHubPreviewRouteRepo(repos, routeAuthority, link), + (repos) => + snapshot?.repos === repos + ? snapshot.read() + : readGitHubPreviewRouteRepo(repos, routeAuthority, link), + snapshot?.identities + ) + } finally { + stop?.() + } } async function loadPreviewFromCatalog( routeAuthority: HostAuthorityKey, link: GitHubIssueOrPRLink, select: (repos: readonly Repo[]) => Promise, - read: (repos: readonly Repo[]) => Repo | null + read: (repos: readonly Repo[]) => Repo | null, + capturedIdentities?: ReadonlySet ): Promise { const repos = useAppStore.getState().repos - const captured = new Set( - githubPreviewRouteCandidates(repos, routeAuthority).map(githubPreviewRepoIdentity) - ) + const captured = + capturedIdentities ?? + new Set(githubPreviewRouteCandidates(repos, routeAuthority).map(githubPreviewRepoIdentity)) const retired = new Set() - const stop = useAppStore.subscribe((state) => { + const stop = useAppStore.subscribe((state, previous) => { + if (state.repos === previous.repos) { + return + } const live = new Set( githubPreviewRouteCandidates(state.repos, routeAuthority).map(githubPreviewRepoIdentity) ) diff --git a/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-repo.ts b/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-repo.ts index 4d605a5a814..70e80139dd3 100644 --- a/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-repo.ts +++ b/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-repo.ts @@ -82,17 +82,34 @@ export function pickGitHubPreviewRouteRepo( ): Repo | null { const linkKey = githubRepoIdentityKey(link.slug) const linkHost = githubHostFromIdentityKey(linkKey) - const resolved = candidates.filter((row): row is { repo: Repo; originKey: string } => - Boolean(row.originKey) - ) - const clone = - resolved.find(({ originKey }) => originKey === linkKey) ?? - resolved.find(({ repo, originKey }) => repoUpstreamIdentityKey(repo, originKey) === linkKey) - return ( - clone?.repo ?? - resolved.find(({ originKey }) => githubHostFromIdentityKey(originKey) === linkHost)?.repo ?? - null - ) + let selected: Repo | null = null + let rank = 3 + for (const { repo, originKey } of candidates) { + const candidateRank = githubPreviewRouteRank(repo, originKey, linkKey, linkHost) + if (candidateRank < rank) { + selected = repo + rank = candidateRank + } + } + return selected +} + +export function githubPreviewRouteRank( + repo: Repo, + originKey: string | null, + linkKey: string, + linkHost: string | undefined +): number { + if (!originKey) { + return 3 + } + if (originKey === linkKey) { + return 0 + } + if (repoUpstreamIdentityKey(repo, originKey) === linkKey) { + return 1 + } + return githubHostFromIdentityKey(originKey) === linkHost ? 2 : 3 } export async function findGitHubPreviewRouteRepo( diff --git a/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-store.ts b/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-store.ts new file mode 100644 index 00000000000..f1a8392aaec --- /dev/null +++ b/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-store.ts @@ -0,0 +1,176 @@ +import type { Repo } from '../../../../../../shared/repo-types' +import type { HostAuthorityKey } from '../../../../../../shared/host-authority' +import type { GitHubIssueOrPRLink } from '../../../../../../shared/github/repositories/links' +import { + githubHostFromIdentityKey, + githubRepoIdentityKey +} from '../../../../../../shared/github/repositories/repository-identity-key' +import { + getRepoSlugCacheVersion, + nextRepoSlugFailureRetryDelay, + readRepoSlugCache, + repoSlugCacheKey, + subscribeRepoSlugCache +} from '@/lib/workspace/catalog/repo-slug-cache' +import { + findGitHubPreviewRouteRepo, + githubPreviewRepoIdentity, + githubPreviewRouteCandidates, + githubPreviewRouteRank +} from './native-chat-github-route-repo' + +type Candidate = { repo: Repo; order: number; rank: number; cacheKey: string | null } + +export type GitHubPreviewRouteStore = ReturnType + +export function createGitHubPreviewRouteStore( + repos: readonly Repo[], + authority: HostAuthorityKey, + link: GitHubIssueOrPRLink +) { + const linkKey = githubRepoIdentityKey(link.slug) + const linkHost = githubHostFromIdentityKey(linkKey) + const candidates = githubPreviewRouteCandidates(repos, authority).map((repo, order) => ({ + repo, + order, + rank: 3, + cacheKey: repoSlugCacheKey(repo) + })) + const identities = new Set(candidates.map(({ repo }) => githubPreviewRepoIdentity(repo))) + const byCacheKey = new Map() + for (const candidate of candidates) { + if (candidate.cacheKey) { + const entries = byCacheKey.get(candidate.cacheKey) ?? [] + entries.push(candidate) + byCacheKey.set(candidate.cacheKey, entries) + } + } + const listeners = new Set<() => void>() + let stop: (() => void) | null = null + let retainers = 0 + let version = -1 + let selected: Candidate | null = null + let resolving: Promise | null = null + let resolved = false + let retryAt = Number.POSITIVE_INFINITY + let notificationPending = false + + const outranks = (candidate: Candidate, current: Candidate | null): boolean => + candidate.rank < 3 && + (!current || + candidate.rank < current.rank || + (candidate.rank === current.rank && candidate.order < current.order)) + const updateRank = (candidate: Candidate): void => { + const cached = candidate.cacheKey ? readRepoSlugCache(candidate.cacheKey) : null + if (!cached?.hit) { + resolved = false + } + if (candidate.cacheKey && cached?.hit && cached.value === null) { + const now = Date.now() + const delay = nextRepoSlugFailureRetryDelay(new Set([candidate.cacheKey]), now) + if (delay !== null) { + retryAt = Math.min(retryAt, now + delay) + } + } + candidate.rank = githubPreviewRouteRank( + candidate.repo, + cached?.hit ? cached.value : null, + linkKey, + linkHost + ) + } + const select = (): void => { + selected = null + for (const candidate of candidates) { + if (outranks(candidate, selected)) { + selected = candidate + } + } + } + const refresh = (): void => { + for (const candidate of candidates) { + updateRank(candidate) + } + select() + version = getRepoSlugCacheVersion() + } + const read = (): Repo | null => { + if (version !== getRepoSlugCacheVersion()) { + refresh() + } + return selected?.repo ?? null + } + const changed = (cacheKey: string | null): void => { + const previous = selected?.repo ?? null + if (cacheKey === null) { + refresh() + } else { + let reselect = false + for (const candidate of byCacheKey.get(cacheKey) ?? []) { + const rank = candidate.rank + updateRank(candidate) + if (candidate === selected && candidate.rank > rank) { + reselect = true + } + if (outranks(candidate, selected)) { + selected = candidate + } + } + // Losing the selected binding needs a replacement; cold resolutions only promote one row. + if (reselect) { + select() + } + version = getRepoSlugCacheVersion() + } + if (previous !== (selected?.repo ?? null) && !notificationPending) { + notificationPending = true + // Update every card's index before React reads their snapshots together. + queueMicrotask(() => { + notificationPending = false + for (const listener of listeners) { + listener() + } + }) + } + } + const retain = (): (() => void) => { + read() + retainers += 1 + stop ??= subscribeRepoSlugCache(changed) + return () => { + retainers -= 1 + if (retainers === 0) { + stop?.() + stop = null + } + } + } + const subscribe = (listener: () => void): (() => void) => { + listeners.add(listener) + const release = retain() + return () => { + listeners.delete(listener) + release() + } + } + const find = async (): Promise => { + const repo = read() + if (resolved && Date.now() < retryAt) { + return repo + } + resolving ??= findGitHubPreviewRouteRepo(repos, authority, link) + .then((result) => { + resolved = true + const now = Date.now() + const delay = nextRepoSlugFailureRetryDelay(new Set(byCacheKey.keys()), now) + retryAt = delay === null ? Number.POSITIVE_INFINITY : now + delay + return result + }) + .finally(() => { + resolving = null + }) + await resolving + return read() + } + return { repos, identities, read, subscribe, retain, find } +} diff --git a/src/renderer/src/components/native-chat/links/previews/native-chat-github-routed-preview-store.test.tsx b/src/renderer/src/components/native-chat/links/previews/native-chat-github-routed-preview-store.test.tsx new file mode 100644 index 00000000000..3c5b69f1614 --- /dev/null +++ b/src/renderer/src/components/native-chat/links/previews/native-chat-github-routed-preview-store.test.tsx @@ -0,0 +1,197 @@ +// @vitest-environment happy-dom + +import { act } from 'react' +import { createRoot, type Root } from 'react-dom/client' +import { afterEach, beforeEach, expect, it, vi } from 'vitest' +import type { Repo } from '../../../../../../shared/repo-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import { parseGitHubIssueOrPRLink } from '../../../../../../shared/github/repositories/links' +import { + clearRepoSlugCacheValues, + rememberRepoSlug, + repoSlugCacheKey +} from '@/lib/workspace/catalog/repo-slug-cache' +import { useRepoSlugIndex } from '@/lib/workspace/catalog/repo-slug-index' + +const mocks = await vi.hoisted(async () => { + const { create } = await import('zustand') + return { + store: create<{ repos: Repo[]; tick: number }>(() => ({ repos: [], tick: 0 })), + endpoint: vi.fn(), + slug: vi.fn() + } +}) +vi.mock('@/store/index', () => ({ useAppStore: mocks.store })) + +const { NativeChatGitHubPeekBody } = await import('./NativeChatGitHubLinkPeek') + +globalThis.IS_REACT_ACT_ENVIRONMENT = true + +function repo(index: number): Repo { + return { + id: `repo-${index}`, + path: `/repos/${index}`, + displayName: `Repo ${index}`, + badgeColor: '', + addedAt: 0 + } +} + +function preview(number: number, title: string): GitHubWorkItem { + return { + id: `pr-${number}`, + type: 'pr', + number, + title, + state: 'open', + url: `https://github.com/acme/repo-2/pull/${number}`, + labels: [], + updatedAt: '', + author: null, + repoId: 'repo-2' + } +} + +function ProjectSlugIndex(): null { + useRepoSlugIndex() + return null +} + +const repos = Array.from({ length: 3 }, (_, index) => repo(index)) +let container: HTMLDivElement +let root: Root + +beforeEach(() => { + clearRepoSlugCacheValues() + mocks.store.setState({ repos, tick: 0 }) + mocks.endpoint.mockReset() + mocks.endpoint.mockReturnValue(new Promise(() => {})) + mocks.slug.mockReset() + mocks.slug.mockImplementation(async ({ repoId }: { repoId: string }) => ({ + owner: 'acme', + repo: repoId + })) + vi.stubGlobal('api', { gh: { workItemByOwnerRepo: mocks.endpoint, repoSlug: mocks.slug } }) + container = document.createElement('div') + document.body.appendChild(container) + root = createRoot(container) +}) +afterEach(() => { + act(() => root.unmount()) + container.remove() + vi.useRealTimers() + vi.unstubAllGlobals() +}) + +it('shows the pending response after an unrelated store update', async () => { + const response = Promise.withResolvers() + const started = Promise.withResolvers() + mocks.endpoint.mockImplementationOnce(() => { + started.resolve() + return response.promise + }) + const link = parseGitHubIssueOrPRLink('https://github.com/acme/repo-2/pull/42')! + await act(async () => { + root.render( + + ) + }) + await started.promise + await act(async () => { + mocks.store.setState({ tick: 1 }) + }) + await act(async () => { + response.resolve(preview(42, 'Pending preview completed')) + }) + expect(container.textContent).toContain('Pending preview completed') +}) + +it('shows the new route response and ignores a late response from the removed route', async () => { + const oldResponse = Promise.withResolvers() + const newResponse = Promise.withResolvers() + const oldStarted = Promise.withResolvers() + const newStarted = Promise.withResolvers() + mocks.endpoint.mockImplementation(({ repoId }: { repoId: string }) => { + if (repoId === 'repo-2') { + oldStarted.resolve() + return oldResponse.promise + } + newStarted.resolve() + return newResponse.promise + }) + const link = parseGitHubIssueOrPRLink('https://github.com/acme/repo-2/pull/43')! + await act(async () => { + root.render( + + ) + }) + await oldStarted.promise + await act(async () => { + mocks.store.setState({ repos: repos.filter((row) => row.id !== 'repo-2') }) + }) + await newStarted.promise + await act(async () => { + newResponse.resolve(preview(43, 'Current route preview')) + }) + expect(container.textContent).toContain('Current route preview') + await act(async () => { + oldResponse.resolve(preview(43, 'Outdated route preview')) + }) + expect(container.textContent).toContain('Current route preview') + expect(container.textContent).not.toContain('Outdated route preview') +}) + +it('recovers an open fallback when the project slug index retries a failed slug', async () => { + vi.useFakeTimers() + mocks.store.setState({ repos: [repos[2]] }) + mocks.slug.mockResolvedValueOnce(null) + mocks.endpoint.mockResolvedValue(preview(44, 'Recovered preview')) + const link = parseGitHubIssueOrPRLink('https://github.com/acme/repo-2/pull/44')! + await act(async () => { + root.render( + <> + Link fallback
} + /> + + + ) + }) + expect(container.textContent).toBe('Link fallback') + await act(async () => { + await vi.advanceTimersByTimeAsync(60_000) + }) + await act(async () => { + mocks.store.setState({ tick: 1 }) + }) + expect(container.textContent).toContain('Recovered preview') + expect(container.textContent).not.toContain('Link fallback') +}) + +it('promotes a warmed clone and keeps its preview across unrelated slug writes', async () => { + rememberRepoSlug(repoSlugCacheKey(repos[0])!, 'acme/router') + rememberRepoSlug(repoSlugCacheKey(repos[1])!, null) + rememberRepoSlug(repoSlugCacheKey(repos[2])!, null) + mocks.endpoint.mockImplementation(async ({ repoId }: { repoId: string }) => + preview(45, repoId === 'repo-2' ? 'Clone preview' : 'Host route preview') + ) + const link = parseGitHubIssueOrPRLink('https://github.com/acme/repo-2/pull/45')! + await act(async () => { + root.render( + + ) + }) + expect(container.textContent).toContain('Host route preview') + await act(async () => { + rememberRepoSlug(repoSlugCacheKey(repos[2])!, 'acme/repo-2') + }) + expect(container.textContent).toContain('Clone preview') + await act(async () => { + rememberRepoSlug(repoSlugCacheKey(repos[1])!, 'acme/other') + rememberRepoSlug('local:unregistered', 'acme/repo-2') + }) + expect(container.textContent).toContain('Clone preview') + expect(container.textContent).not.toContain('Host route preview') +}) diff --git a/src/renderer/src/lib/workspace/catalog/repo-slug-cache.ts b/src/renderer/src/lib/workspace/catalog/repo-slug-cache.ts index 22e71a0dbf6..d1c91421343 100644 --- a/src/renderer/src/lib/workspace/catalog/repo-slug-cache.ts +++ b/src/renderer/src/lib/workspace/catalog/repo-slug-cache.ts @@ -16,8 +16,26 @@ export type RepoSlugMatches = { origin: Repo[]; upstream: Repo[] } /** Cache by runtime scope/repo ID, briefly retaining null failures to avoid per-cell retries while recovering after external GHES login. */ export const slugByRepoId = new Map() const slugFailureExpiresAtByRepoId = new Map() +const slugCacheListeners = new Set<(cacheKey: string | null) => void>() +let slugCacheVersion = 0 export const REPO_SLUG_FAILURE_TTL_MS = 60_000 +export function subscribeRepoSlugCache(listener: (cacheKey: string | null) => void): () => void { + slugCacheListeners.add(listener) + return () => slugCacheListeners.delete(listener) +} + +export function getRepoSlugCacheVersion(): number { + return slugCacheVersion +} + +function notifyRepoSlugCacheChanged(cacheKey: string | null): void { + slugCacheVersion += 1 + for (const listener of slugCacheListeners) { + listener(cacheKey) + } +} + export function readRepoSlugCache( cacheKey: string, now = Date.now() @@ -36,22 +54,34 @@ export function readRepoSlugCache( } export function rememberRepoSlug(cacheKey: string, value: string | null, now = Date.now()): void { + const previous = slugByRepoId.get(cacheKey) ?? null slugByRepoId.set(cacheKey, value) if (value === null) { slugFailureExpiresAtByRepoId.set(cacheKey, now + REPO_SLUG_FAILURE_TTL_MS) } else { slugFailureExpiresAtByRepoId.delete(cacheKey) } + if (previous !== value) { + notifyRepoSlugCacheChanged(cacheKey) + } } export function deleteRepoSlugCacheKey(cacheKey: string): void { + const previous = slugByRepoId.get(cacheKey) slugByRepoId.delete(cacheKey) slugFailureExpiresAtByRepoId.delete(cacheKey) + if (previous !== undefined) { + notifyRepoSlugCacheChanged(cacheKey) + } } export function clearRepoSlugCacheValues(): void { + const hadValues = slugByRepoId.size > 0 slugByRepoId.clear() slugFailureExpiresAtByRepoId.clear() + if (hadValues) { + notifyRepoSlugCacheChanged(null) + } } export function nextRepoSlugFailureRetryDelay( From bd3d62d3d578e30bb76af456de8b0df56ebd86ca Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:54:23 -0800 Subject: [PATCH 044/178] refactor: organize workspace cleanup by concrete owners (#41887) --- config/flaky-test-pattern-baseline.txt | 2 +- config/react-compiler-bailout-baseline.txt | 6 +- .../scripts/ci/sharding/ci-shard-timings.json | 26 ++++---- .../protected-copy-keys.mjs | 4 ++ .../WorkspaceCleanupDialog.tsx | 34 +++++------ ...se-workspace-cleanup-browse-state.test.tsx | 2 +- .../use-workspace-cleanup-browse-state.ts | 2 +- .../use-workspace-cleanup-facet-rows.ts | 16 ++--- .../use-workspace-cleanup-ignore.ts | 4 +- .../workspace-cleanup-active-facets.ts | 2 +- ...workspace-cleanup-applied-filter-chips.tsx | 4 +- .../workspace-cleanup-browse-toolbar.tsx | 4 +- .../workspace-cleanup-facet-controls.tsx | 0 .../workspace-cleanup-facet-labels.ts | 4 +- .../workspace-cleanup-facet-panel-model.ts | 6 +- .../workspace-cleanup-filter-bar.tsx | 2 +- .../workspace-cleanup-git-review-facets.tsx | 6 +- .../workspace-cleanup-lifecycle-facets.tsx | 2 +- .../workspace-cleanup-selection-model.ts | 0 .../workspace-cleanup-sort-header.tsx | 4 +- ...se-workspace-cleanup-git-evidence.test.tsx | 36 +---------- .../use-workspace-cleanup-git-evidence.ts | 6 +- .../workspace-cleanup-git-evidence.ts | 6 +- .../workspace-cleanup-host-index.test.ts} | 6 +- .../workspace-cleanup-host-indexes.ts} | 12 ++-- .../workspace-cleanup-review-lookup.ts} | 45 +++----------- .../use-workspace-cleanup-row-order.test.ts | 2 +- .../use-workspace-cleanup-row-order.ts | 2 +- .../workspace-cleanup-candidate-labels.ts | 6 +- .../workspace-cleanup-candidate-list.tsx | 0 .../workspace-cleanup-candidate-row-data.ts | 5 +- ...orkspace-cleanup-candidate-row-details.tsx | 4 +- ...rkspace-cleanup-candidate-row-trailing.tsx | 4 +- ...orkspace-cleanup-candidate-row.fixture.tsx | 12 ++-- .../workspace-cleanup-candidate-row.tsx | 8 +-- .../workspace-cleanup-flat-list.test.tsx | 4 +- .../workspace-cleanup-host-label.ts | 9 +-- .../workspace-cleanup-metadata-chip.tsx | 0 .../workspace-cleanup-relative-time.ts | 1 + .../workspace-cleanup-review-labels.ts} | 4 +- .../workspace-cleanup-row-list.tsx | 12 ++-- .../workspace-cleanup-status-pill.tsx | 0 ...anupDialog.stale-while-revalidate.test.tsx | 12 ++-- .../use-workspace-cleanup-dialog-lifecycle.ts | 4 +- ...orkspace-cleanup-dialog-header.fixture.tsx | 12 ++-- .../workspace-cleanup-dialog-header.tsx | 2 +- .../workspace-cleanup-dialog-notices.tsx | 2 +- .../workspace-cleanup-freshness.tsx | 4 +- ...orkspace-cleanup-candidate-filter-sort.ts} | 42 +++++++++---- ...e-cleanup-candidate-state.test-fixture.ts} | 10 ++-- .../workspace-cleanup-facet-matchers.ts | 2 +- .../workspace-cleanup-facet-row-caches.ts | 12 ++-- .../workspace-cleanup-facet-sort.test.ts | 2 +- .../workspace-cleanup-facet-sort.ts | 4 +- .../workspace-cleanup-facet.test.fixture.ts | 14 ++--- .../{ => query}/workspace-cleanup-facets.ts | 23 ++++---- .../workspace-cleanup-query.test.ts | 2 +- .../{ => query}/workspace-cleanup-query.ts | 4 +- .../use-workspace-cleanup-agent-stop-gate.ts | 4 +- .../workspace-cleanup-agent-stop-approval.ts | 2 +- .../workspace-cleanup-confirm-stop-agents.tsx | 10 ++-- .../workspace-cleanup-ancestor-skips.ts | 6 +- ...background-removal-late-settlement.test.ts | 2 +- ...rkspace-cleanup-background-removal.test.ts | 59 +------------------ .../workspace-cleanup-background-removal.ts | 5 +- ...pace-cleanup-post-batch-late-settlement.ts | 4 +- ...rkspace-cleanup-removal-settlement.test.ts | 2 +- .../workspace-cleanup-removal-settlement.ts | 4 +- ...e-cleanup-removal-timeout-recovery.test.ts | 2 +- .../workspace-cleanup-removal-toasts.ts | 2 +- ...eanup-skipped-ancestor-reclassification.ts | 2 +- ...kspace-cleanup-removal.agent-stop.test.tsx | 10 ++-- .../use-workspace-cleanup-removal.ts | 8 +-- ...se-workspace-cleanup-unverified-removal.ts | 8 +-- .../workspace-cleanup-confirm-remove.tsx | 24 ++++---- .../workspace-cleanup-deletion-phases.ts | 8 +-- ...orkspace-cleanup-forget-locally-button.tsx | 4 +- ...rkspace-cleanup-removal-candidates.test.ts | 4 +- .../workspace-cleanup-removal-candidates.ts | 6 +- .../workspace-cleanup-snapshot-prune-batch.ts | 4 +- .../use-workspace-cleanup-scan-lifecycle.ts | 0 .../workspace-cleanup-scan-notice.ts | 2 +- 82 files changed, 278 insertions(+), 369 deletions(-) rename src/renderer/src/components/workspace-cleanup/{ => browse}/use-workspace-cleanup-browse-state.test.tsx (97%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/use-workspace-cleanup-browse-state.ts (97%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/use-workspace-cleanup-facet-rows.ts (94%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/use-workspace-cleanup-ignore.ts (93%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-active-facets.ts (96%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-applied-filter-chips.tsx (97%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-browse-toolbar.tsx (94%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-facet-controls.tsx (100%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-facet-labels.ts (97%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-facet-panel-model.ts (81%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-filter-bar.tsx (99%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-git-review-facets.tsx (96%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-lifecycle-facets.tsx (98%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-selection-model.ts (100%) rename src/renderer/src/components/workspace-cleanup/{ => browse}/workspace-cleanup-sort-header.tsx (97%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-evidence}/use-workspace-cleanup-git-evidence.test.tsx (74%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-evidence}/use-workspace-cleanup-git-evidence.ts (97%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-evidence}/workspace-cleanup-git-evidence.ts (94%) rename src/renderer/src/components/workspace-cleanup/{workspace-cleanup-facet-filter.test.ts => candidate-evidence/workspace-cleanup-host-index.test.ts} (90%) rename src/renderer/src/components/workspace-cleanup/{workspace-cleanup-host-identity.ts => candidate-evidence/workspace-cleanup-host-indexes.ts} (88%) rename src/renderer/src/components/workspace-cleanup/{workspace-cleanup-presentation.ts => candidate-evidence/workspace-cleanup-review-lookup.ts} (79%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/use-workspace-cleanup-row-order.test.ts (90%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/use-workspace-cleanup-row-order.ts (96%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-candidate-labels.ts (95%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-candidate-list.tsx (100%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-candidate-row-data.ts (97%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-candidate-row-details.tsx (95%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-candidate-row-trailing.tsx (97%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-candidate-row.fixture.tsx (95%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-candidate-row.tsx (97%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-flat-list.test.tsx (94%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-host-label.ts (70%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-metadata-chip.tsx (100%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-relative-time.ts (97%) rename src/renderer/src/components/workspace-cleanup/{workspace-cleanup-row-labels.ts => candidate-list/workspace-cleanup-review-labels.ts} (72%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-row-list.tsx (92%) rename src/renderer/src/components/workspace-cleanup/{ => candidate-list}/workspace-cleanup-status-pill.tsx (100%) rename src/renderer/src/components/workspace-cleanup/{ => dialog}/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx (95%) rename src/renderer/src/components/workspace-cleanup/{ => dialog}/use-workspace-cleanup-dialog-lifecycle.ts (94%) rename src/renderer/src/components/workspace-cleanup/{ => dialog}/workspace-cleanup-dialog-header.fixture.tsx (84%) rename src/renderer/src/components/workspace-cleanup/{ => dialog}/workspace-cleanup-dialog-header.tsx (95%) rename src/renderer/src/components/workspace-cleanup/{ => dialog}/workspace-cleanup-dialog-notices.tsx (96%) rename src/renderer/src/components/workspace-cleanup/{ => dialog}/workspace-cleanup-freshness.tsx (90%) rename src/renderer/src/components/workspace-cleanup/{workspace-cleanup-filter-sort.ts => query/workspace-cleanup-candidate-filter-sort.ts} (86%) rename src/renderer/src/components/workspace-cleanup/{workspace-cleanup-presentation-fixtures.ts => query/workspace-cleanup-candidate-state.test-fixture.ts} (86%) rename src/renderer/src/components/workspace-cleanup/{ => query}/workspace-cleanup-facet-matchers.ts (98%) rename src/renderer/src/components/workspace-cleanup/{ => query}/workspace-cleanup-facet-row-caches.ts (90%) rename src/renderer/src/components/workspace-cleanup/{ => query}/workspace-cleanup-facet-sort.test.ts (87%) rename src/renderer/src/components/workspace-cleanup/{ => query}/workspace-cleanup-facet-sort.ts (96%) rename src/renderer/src/components/workspace-cleanup/{ => query}/workspace-cleanup-facet.test.fixture.ts (86%) rename src/renderer/src/components/workspace-cleanup/{ => query}/workspace-cleanup-facets.ts (90%) rename src/renderer/src/components/workspace-cleanup/{ => query}/workspace-cleanup-query.test.ts (93%) rename src/renderer/src/components/workspace-cleanup/{ => query}/workspace-cleanup-query.ts (96%) rename src/renderer/src/components/workspace-cleanup/{ => removal/agent-consent}/use-workspace-cleanup-agent-stop-gate.ts (94%) rename src/renderer/src/components/workspace-cleanup/{ => removal/agent-consent}/workspace-cleanup-agent-stop-approval.ts (84%) rename src/renderer/src/components/workspace-cleanup/{ => removal/agent-consent}/workspace-cleanup-confirm-stop-agents.tsx (91%) rename src/renderer/src/components/workspace-cleanup/{ => removal/settlement}/workspace-cleanup-ancestor-skips.ts (87%) rename src/renderer/src/components/workspace-cleanup/{ => removal/settlement}/workspace-cleanup-background-removal-late-settlement.test.ts (98%) rename src/renderer/src/components/workspace-cleanup/{ => removal/settlement}/workspace-cleanup-background-removal.test.ts (68%) rename src/renderer/src/components/workspace-cleanup/{ => removal/settlement}/workspace-cleanup-background-removal.ts (98%) rename src/renderer/src/components/workspace-cleanup/{ => removal/settlement}/workspace-cleanup-post-batch-late-settlement.ts (98%) rename src/renderer/src/components/workspace-cleanup/{ => removal/settlement}/workspace-cleanup-removal-settlement.test.ts (95%) rename src/renderer/src/components/workspace-cleanup/{ => removal/settlement}/workspace-cleanup-removal-settlement.ts (96%) rename src/renderer/src/components/workspace-cleanup/{ => removal/settlement}/workspace-cleanup-removal-timeout-recovery.test.ts (99%) rename src/renderer/src/components/workspace-cleanup/{ => removal/settlement}/workspace-cleanup-removal-toasts.ts (92%) rename src/renderer/src/components/workspace-cleanup/{ => removal/settlement}/workspace-cleanup-skipped-ancestor-reclassification.ts (95%) rename src/renderer/src/components/workspace-cleanup/{ => removal}/use-workspace-cleanup-removal.agent-stop.test.tsx (92%) rename src/renderer/src/components/workspace-cleanup/{ => removal}/use-workspace-cleanup-removal.ts (97%) rename src/renderer/src/components/workspace-cleanup/{ => removal}/use-workspace-cleanup-unverified-removal.ts (93%) rename src/renderer/src/components/workspace-cleanup/{ => removal}/workspace-cleanup-confirm-remove.tsx (91%) rename src/renderer/src/components/workspace-cleanup/{ => removal}/workspace-cleanup-deletion-phases.ts (84%) rename src/renderer/src/components/workspace-cleanup/{ => removal}/workspace-cleanup-forget-locally-button.tsx (88%) rename src/renderer/src/components/workspace-cleanup/{ => removal}/workspace-cleanup-removal-candidates.test.ts (82%) rename src/renderer/src/components/workspace-cleanup/{ => removal}/workspace-cleanup-removal-candidates.ts (80%) rename src/renderer/src/components/workspace-cleanup/{ => removal}/workspace-cleanup-snapshot-prune-batch.ts (81%) rename src/renderer/src/components/workspace-cleanup/{ => scanning}/use-workspace-cleanup-scan-lifecycle.ts (100%) rename src/renderer/src/components/workspace-cleanup/{ => scanning}/workspace-cleanup-scan-notice.ts (96%) diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index c647a955b37..7ccccd20106 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -526,7 +526,7 @@ 2 test-no-wall-clock:src/renderer/src/components/terminal-pane/pty-connection/emulation/fish-color-scheme-child-stdin.node-pty.test.ts 2 test-no-wall-clock:src/renderer/src/components/terminal-pane/snapshots/remote-snapshot-alt-screen-host-chain.test.ts 1 test-no-wall-clock:src/renderer/src/components/terminal/initial-terminal-structured-launch.test.tsx -2 test-no-wall-clock:src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-removal.agent-stop.test.tsx +2 test-no-wall-clock:src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-removal.agent-stop.test.tsx 1 test-no-wall-clock:src/renderer/src/components/worktree-jump/WorktreeJumpPalette.recent-tabs.behavior.test.tsx 2 test-no-wall-clock:src/renderer/src/hooks/ipc-events/agent-status-terminal-title-tab-write.test.ts 1 test-no-wall-clock:src/renderer/src/lib/workspace/activation/worktree-activation-emptied-workspace-reseed.test.ts diff --git a/config/react-compiler-bailout-baseline.txt b/config/react-compiler-bailout-baseline.txt index 43049616dfa..a0db79c5703 100644 --- a/config/react-compiler-bailout-baseline.txt +++ b/config/react-compiler-bailout-baseline.txt @@ -668,9 +668,9 @@ 1 src/renderer/src/components/use-terminal-window-lifecycle.ts 1 src/renderer/src/components/use-terminal-workspace-foundation.ts 1 src/renderer/src/components/virtualized-list.tsx -1 src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-facet-rows.ts -2 src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-git-evidence.ts -1 src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-list.tsx +1 src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-facet-rows.ts +2 src/renderer/src/components/workspace-cleanup/candidate-evidence/use-workspace-cleanup-git-evidence.ts +1 src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-list.tsx 2 src/renderer/src/components/workspace-emoji/useWorkspaceEmojiShortcodeInput.ts 1 src/renderer/src/components/worktree-jump/use-worktree-jump-palette-list-entries.ts 1 src/renderer/src/components/worktree-jump/use-worktree-jump-palette-local-state.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index dd6da1357fc..ae79c365b7d 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -5388,19 +5388,19 @@ "src/renderer/src/components/use-terminal-window-lifecycle.frame-flush.test.tsx": 2164, "src/renderer/src/components/virtualized-list.test.tsx": 446, "src/renderer/src/components/window-close-request-coordinator.test.ts": 361, - "src/renderer/src/components/workspace-cleanup/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx": 3269, - "src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-browse-state.test.tsx": 248, - "src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-git-evidence.test.tsx": 539, - "src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-removal.agent-stop.test.tsx": 2285, - "src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-row-order.test.ts": 242, - "src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal-late-settlement.test.ts": 1880, - "src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal.test.ts": 283, - "src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-filter.test.ts": 341, - "src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-sort.test.ts": 264, - "src/renderer/src/components/workspace-cleanup/workspace-cleanup-flat-list.test.tsx": 2594, - "src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-candidates.test.ts": 206, - "src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-settlement.test.ts": 203, - "src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-timeout-recovery.test.ts": 1817, + "src/renderer/src/components/workspace-cleanup/dialog/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx": 3269, + "src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-browse-state.test.tsx": 248, + "src/renderer/src/components/workspace-cleanup/candidate-evidence/use-workspace-cleanup-git-evidence.test.tsx": 539, + "src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-removal.agent-stop.test.tsx": 2285, + "src/renderer/src/components/workspace-cleanup/candidate-list/use-workspace-cleanup-row-order.test.ts": 242, + "src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal-late-settlement.test.ts": 1880, + "src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal.test.ts": 283, + "src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-host-index.test.ts": 341, + "src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.test.ts": 264, + "src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-flat-list.test.tsx": 2594, + "src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-removal-candidates.test.ts": 206, + "src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.test.ts": 203, + "src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-timeout-recovery.test.ts": 1817, "src/renderer/src/components/workspace-emoji/WorkspaceEmojiPickerButton.test.tsx": 1086, "src/renderer/src/components/workspace-emoji/WorkspaceEmojiPickerPanel.test.tsx": 763, "src/renderer/src/components/workspace-emoji/useWorkspaceEmojiShortcodeInput.test.tsx": 365, diff --git a/config/scripts/localization/catalog-validation/protected-copy-keys.mjs b/config/scripts/localization/catalog-validation/protected-copy-keys.mjs index 1bedc742604..fb2df9b9f2e 100644 --- a/config/scripts/localization/catalog-validation/protected-copy-keys.mjs +++ b/config/scripts/localization/catalog-validation/protected-copy-keys.mjs @@ -25,6 +25,7 @@ const DESTRUCTIVE_CONTROL_RE = /variant=["']destructive["']|variant:\s*["']destr const CONFIRMATION_DIALOG_RE = /<(?:Alert)?Dialog(?:Content|Footer)\b/ const NATIVE_MESSAGE_BOX_RE = /\bshowMessageBox(?:Sync)?\(/ const PROTECTED_COPY_PRAGMA_RE = /^\s*\/\/ @protected-copy\b/m +const PROTECTED_COPY_FILE_SCOPE_RE = /^\s*\/\/ @protected-copy scope=file\s*$/m const RELATIVE_IMPORT_RE = /(?:^|\n)\s*(?:import|export)\s[^'"]*?from\s+['"](\.\/[^'"]+)['"]/g const PLURAL_SUFFIX_RE = /_(?:zero|one|two|few|many|other)$/ const RESOLVE_EXTENSIONS = ['', '.ts', '.tsx', '/index.ts', '/index.tsx'] @@ -75,6 +76,9 @@ export function collectProtectedCopyKeys(sources, englishPaths) { const marked = [...sources.keys()].filter((file) => protectedCopyMarker(sources.get(file))) const files = new Set(marked) for (const file of marked) { + if (PROTECTED_COPY_FILE_SCOPE_RE.test(sources.get(file))) { + continue + } for (const imported of siblingImports(file, sources.get(file))) { if (sources.has(imported)) { files.add(imported) diff --git a/src/renderer/src/components/workspace-cleanup/WorkspaceCleanupDialog.tsx b/src/renderer/src/components/workspace-cleanup/WorkspaceCleanupDialog.tsx index 37e3c636400..c85a5d82b04 100644 --- a/src/renderer/src/components/workspace-cleanup/WorkspaceCleanupDialog.tsx +++ b/src/renderer/src/components/workspace-cleanup/WorkspaceCleanupDialog.tsx @@ -15,37 +15,37 @@ import { getWorkspaceCleanupCandidateIdentity, resolveWorkspaceCleanupRemovalHostId } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' -import { WorkspaceCleanupBrowseToolbar } from './workspace-cleanup-browse-toolbar' -import { WorkspaceCleanupConfirmRemove } from './workspace-cleanup-confirm-remove' -import { WorkspaceCleanupConfirmStopAgents } from './workspace-cleanup-confirm-stop-agents' -import { WorkspaceCleanupDialogHeader } from './workspace-cleanup-dialog-header' +import { WorkspaceCleanupBrowseToolbar } from './browse/workspace-cleanup-browse-toolbar' +import { WorkspaceCleanupConfirmRemove } from './removal/workspace-cleanup-confirm-remove' +import { WorkspaceCleanupConfirmStopAgents } from './removal/agent-consent/workspace-cleanup-confirm-stop-agents' +import { WorkspaceCleanupDialogHeader } from './dialog/workspace-cleanup-dialog-header' import { getWorkspaceCleanupDeletionPhaseByIdentity, selectWorkspaceCleanupDeletionPhases -} from './workspace-cleanup-deletion-phases' +} from './removal/workspace-cleanup-deletion-phases' import { WorkspaceCleanupInitialScanBanner, WorkspaceCleanupNotice, WorkspaceCleanupSkeletonRows -} from './workspace-cleanup-dialog-notices' -import { WorkspaceCleanupRowList } from './workspace-cleanup-row-list' -import { formatWorkspaceCleanupScanNotice } from './workspace-cleanup-scan-notice' -import { applyWorkspaceCleanupGitEvidence } from './workspace-cleanup-git-evidence' -import { useWorkspaceCleanupBrowseState } from './use-workspace-cleanup-browse-state' +} from './dialog/workspace-cleanup-dialog-notices' +import { WorkspaceCleanupRowList } from './candidate-list/workspace-cleanup-row-list' +import { formatWorkspaceCleanupScanNotice } from './scanning/workspace-cleanup-scan-notice' +import { applyWorkspaceCleanupGitEvidence } from './candidate-evidence/workspace-cleanup-git-evidence' +import { useWorkspaceCleanupBrowseState } from './browse/use-workspace-cleanup-browse-state' import { useWorkspaceCleanupDialogLifecycle, type WorkspaceCleanupDialogLifecycle -} from './use-workspace-cleanup-dialog-lifecycle' -import { useWorkspaceCleanupFacetRows } from './use-workspace-cleanup-facet-rows' -import { useWorkspaceCleanupGitEvidence } from './use-workspace-cleanup-git-evidence' -import { useWorkspaceCleanupIgnore } from './use-workspace-cleanup-ignore' -import { useWorkspaceCleanupRowOrder } from './use-workspace-cleanup-row-order' -import { openWorkspaceCleanupForgetLocally } from './workspace-cleanup-forget-locally-button' +} from './dialog/use-workspace-cleanup-dialog-lifecycle' +import { useWorkspaceCleanupFacetRows } from './browse/use-workspace-cleanup-facet-rows' +import { useWorkspaceCleanupGitEvidence } from './candidate-evidence/use-workspace-cleanup-git-evidence' +import { useWorkspaceCleanupIgnore } from './browse/use-workspace-cleanup-ignore' +import { useWorkspaceCleanupRowOrder } from './candidate-list/use-workspace-cleanup-row-order' +import { openWorkspaceCleanupForgetLocally } from './removal/workspace-cleanup-forget-locally-button' import { formatVanishedSelectionNotice, formatWithheldSelectionNotice, toggleSetMember -} from './workspace-cleanup-selection-model' +} from './browse/workspace-cleanup-selection-model' export default function WorkspaceCleanupDialog(): React.JSX.Element | null { const lifecycle = useWorkspaceCleanupDialogLifecycle() diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-browse-state.test.tsx b/src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-browse-state.test.tsx similarity index 97% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-browse-state.test.tsx rename to src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-browse-state.test.tsx index 179294c47e8..f60b6674fac 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-browse-state.test.tsx +++ b/src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-browse-state.test.tsx @@ -8,7 +8,7 @@ import { createWorkspaceCleanupBrowseSlice, resetWorkspaceCleanupBrowsePersistTimer } from '@/store/slices/workspace-cleanup/workspace-cleanup-browse' -import { createDefaultWorkspaceCleanupBrowseState } from '../../../../shared/workspace-cleanup/workspace-cleanup-browse-state' +import { createDefaultWorkspaceCleanupBrowseState } from '../../../../../shared/workspace-cleanup/workspace-cleanup-browse-state' import { useWorkspaceCleanupBrowseState, diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-browse-state.ts b/src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-browse-state.ts similarity index 97% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-browse-state.ts rename to src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-browse-state.ts index b03a3ae5aba..f472fb79b36 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-browse-state.ts +++ b/src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-browse-state.ts @@ -5,7 +5,7 @@ import { type WorkspaceCleanupFilterState, type WorkspaceCleanupSortField, type WorkspaceCleanupSortState -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' export type WorkspaceCleanupBrowseController = { filters: WorkspaceCleanupFilterState diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-facet-rows.ts b/src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-facet-rows.ts similarity index 94% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-facet-rows.ts rename to src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-facet-rows.ts index f8f276e3a63..af2ab760638 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-facet-rows.ts +++ b/src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-facet-rows.ts @@ -3,25 +3,25 @@ import { useShallow } from 'zustand/react/shallow' import { useAppStore } from '@/store/index' import { getLiveAgentStatusByWorktreeId } from '@/lib/workspace/activity/worktree-activity-state' import { translateWorkspaceBoardStatusLabel } from '@/components/sidebar/workspace-board-status-label' -import type { HostedReviewProvider } from '../../../../shared/source-control/hosted-review/hosted-review-contracts' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { HostedReviewProvider } from '../../../../../shared/source-control/hosted-review/hosted-review-contracts' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import type { WorkspaceCleanupFilterState, WorkspaceCleanupSortState -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' import { countWorkspaceCleanupMeasuredRows, type WorkspaceCleanupFacets -} from './workspace-cleanup-facets' +} from '../query/workspace-cleanup-facets' import { countWorkspaceCleanupFacetMatches, filterWorkspaceCleanupFacets, runWorkspaceCleanupQuery -} from './workspace-cleanup-query' +} from '../query/workspace-cleanup-query' import { buildWorkspaceCleanupReviewLookup, type WorkspaceCleanupReviewInfo -} from './workspace-cleanup-presentation' +} from '../candidate-evidence/workspace-cleanup-review-lookup' import type { WorkspaceCleanupFacetCounts, WorkspaceCleanupFacetOptions @@ -30,13 +30,13 @@ import { buildWorkspaceCleanupSizeIndex, buildWorkspaceCleanupWorktreeIndex, countWorkspaceCleanupCandidateIds -} from './workspace-cleanup-host-identity' +} from '../candidate-evidence/workspace-cleanup-host-indexes' import { computeWorkspaceCleanupFacetList, computeWorkspaceCleanupReviewInfoIndex, createWorkspaceCleanupFacetListCache, createWorkspaceCleanupReviewInfoCache -} from './workspace-cleanup-facet-row-caches' +} from '../query/workspace-cleanup-facet-row-caches' import { selectWorktreesByRepo } from '@/store/workspaces/catalog/workspace-record-fields' import { useUiLanguageRevision } from '@/i18n/use-ui-language-revision' diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-ignore.ts b/src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-ignore.ts similarity index 93% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-ignore.ts rename to src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-ignore.ts index 5f722425bc0..e3e5ebb2453 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-ignore.ts +++ b/src/renderer/src/components/workspace-cleanup/browse/use-workspace-cleanup-ignore.ts @@ -3,8 +3,8 @@ import { toast } from 'sonner' import { useMountedRef } from '@/hooks/useMountedRef' import { useAppStore } from '@/store' import { translate } from '@/i18n/i18n' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { getWorkspaceCleanupCandidateIdentity } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { getWorkspaceCleanupCandidateIdentity } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' function describeError(err: unknown): string { return err instanceof Error ? err.message : String(err) diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-active-facets.ts b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-active-facets.ts similarity index 96% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-active-facets.ts rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-active-facets.ts index e47342749d4..17d208a4597 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-active-facets.ts +++ b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-active-facets.ts @@ -1,7 +1,7 @@ import { createDefaultWorkspaceCleanupFilterState, type WorkspaceCleanupFilterState -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' export type WorkspaceCleanupFacetGroupKey = keyof Omit diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-applied-filter-chips.tsx b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-applied-filter-chips.tsx similarity index 97% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-applied-filter-chips.tsx rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-applied-filter-chips.tsx index 570a1afd4c2..3aa9f2c4b1f 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-applied-filter-chips.tsx +++ b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-applied-filter-chips.tsx @@ -8,8 +8,8 @@ import { toMegabytes } from './workspace-cleanup-facet-panel-model' import { type WorkspaceCleanupAppliedFilter, listAppliedWorkspaceCleanupFilters -} from '../../../../shared/workspace-cleanup/workspace-cleanup-applied-filters' -import type { WorkspaceCleanupFilterState } from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-applied-filters' +import type { WorkspaceCleanupFilterState } from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' // Show narrowing filters outside the panel; derive labels per render so language changes cannot reuse stale translations. // Why not memoized: the labels are translated at build time, so caching them on diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-browse-toolbar.tsx b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-browse-toolbar.tsx similarity index 94% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-browse-toolbar.tsx rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-browse-toolbar.tsx index ac57bca8e42..2dcb266c757 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-browse-toolbar.tsx +++ b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-browse-toolbar.tsx @@ -3,10 +3,10 @@ import { useTranslate } from '@/i18n/use-translate' import type { WorkspaceCleanupBrowseController } from './use-workspace-cleanup-browse-state' import type { WorkspaceCleanupFacetRows } from './use-workspace-cleanup-facet-rows' import { listActiveWorkspaceCleanupFacetGroups } from './workspace-cleanup-active-facets' -import { WorkspaceCleanupEmptyState } from './workspace-cleanup-dialog-notices' +import { WorkspaceCleanupEmptyState } from '../dialog/workspace-cleanup-dialog-notices' import { WorkspaceCleanupFilterBar } from './workspace-cleanup-filter-bar' import { WorkspaceCleanupSortHeader } from './workspace-cleanup-sort-header' -import type { WorkspaceSpaceScanProgress } from '../../../../shared/workspace-disk-space/workspace-space-types' +import type { WorkspaceSpaceScanProgress } from '../../../../../shared/workspace-disk-space/workspace-space-types' export function WorkspaceCleanupBrowseToolbar({ browse, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-controls.tsx b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-facet-controls.tsx similarity index 100% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-controls.tsx rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-facet-controls.tsx diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-labels.ts b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-facet-labels.ts similarity index 97% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-labels.ts rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-facet-labels.ts index 39e1003e940..1f20a1cf523 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-labels.ts +++ b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-facet-labels.ts @@ -1,6 +1,6 @@ import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import type { HostedReviewProvider } from '../../../../shared/source-control/hosted-review/hosted-review-contracts' +import type { HostedReviewProvider } from '../../../../../shared/source-control/hosted-review/hosted-review-contracts' import type { WorkspaceCleanupAgentState, WorkspaceCleanupBlockerMode, @@ -11,7 +11,7 @@ import type { WorkspaceCleanupSortField, WorkspaceCleanupTicketSource, WorkspaceCleanupTriState -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' export function getWorkspaceCleanupSortFieldLabel( field: WorkspaceCleanupSortField, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-panel-model.ts b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-facet-panel-model.ts similarity index 81% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-panel-model.ts rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-facet-panel-model.ts index 8f5c965348a..981c08f94cd 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-panel-model.ts +++ b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-facet-panel-model.ts @@ -1,6 +1,6 @@ -import type { ExecutionHostId } from '../../../../shared/execution-host' -import type { HostedReviewProvider } from '../../../../shared/source-control/hosted-review/hosted-review-contracts' -import type { WorkspaceCleanupFilterState } from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +import type { ExecutionHostId } from '../../../../../shared/execution-host' +import type { HostedReviewProvider } from '../../../../../shared/source-control/hosted-review/hosted-review-contracts' +import type { WorkspaceCleanupFilterState } from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' export type WorkspaceCleanupFacetCounts = Record< keyof Omit, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-filter-bar.tsx b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-filter-bar.tsx similarity index 99% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-filter-bar.tsx rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-filter-bar.tsx index 5e780d175d2..c908d9729d2 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-filter-bar.tsx +++ b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-filter-bar.tsx @@ -11,7 +11,7 @@ import { useAppliedWorkspaceCleanupFilters, WorkspaceCleanupAppliedFilterChips } from './workspace-cleanup-applied-filter-chips' -import type { WorkspaceCleanupAppliedFilter } from '../../../../shared/workspace-cleanup/workspace-cleanup-applied-filters' +import type { WorkspaceCleanupAppliedFilter } from '../../../../../shared/workspace-cleanup/workspace-cleanup-applied-filters' import { WorkspaceCleanupGitReviewFacets } from './workspace-cleanup-git-review-facets' import { WorkspaceCleanupLifecycleFacets } from './workspace-cleanup-lifecycle-facets' import type { WorkspaceCleanupFacetGroupProps } from './workspace-cleanup-facet-panel-model' diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-git-review-facets.tsx b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-git-review-facets.tsx similarity index 96% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-git-review-facets.tsx rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-git-review-facets.tsx index 2c003374b20..460dd1c39d1 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-git-review-facets.tsx +++ b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-git-review-facets.tsx @@ -1,6 +1,6 @@ import React from 'react' import { useTranslate } from '@/i18n/use-translate' -import { getTranslatedExecutionHostLabel } from '../sidebar/execution-host-label' +import { getTranslatedExecutionHostLabel } from '../../sidebar/execution-host-label' import { WORKSPACE_CLEANUP_BLOCKER_MODE_VALUES, WORKSPACE_CLEANUP_BLOCKER_VALUES, @@ -9,8 +9,8 @@ import { WORKSPACE_CLEANUP_REVIEW_STATE_VALUES, WORKSPACE_CLEANUP_TICKET_SOURCE_VALUES, WORKSPACE_CLEANUP_TRI_STATE_VALUES -} from '../../../../shared/workspace-cleanup/workspace-cleanup-facet-rankings' -import { getWorkspaceCleanupBlockerLabel } from './workspace-cleanup-candidate-labels' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-facet-rankings' +import { getWorkspaceCleanupBlockerLabel } from '../candidate-list/workspace-cleanup-candidate-labels' import { FacetChoice, FacetNumberField, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-lifecycle-facets.tsx b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-lifecycle-facets.tsx similarity index 98% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-lifecycle-facets.tsx rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-lifecycle-facets.tsx index 46ea05b3b34..a697ed5fda0 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-lifecycle-facets.tsx +++ b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-lifecycle-facets.tsx @@ -5,7 +5,7 @@ import { WORKSPACE_CLEANUP_IDLE_SIGNAL_VALUES, WORKSPACE_CLEANUP_PRESENCE_VALUES, WORKSPACE_CLEANUP_TRI_STATE_VALUES -} from '../../../../shared/workspace-cleanup/workspace-cleanup-facet-rankings' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-facet-rankings' import { FacetCheckbox, FacetChoice, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-selection-model.ts b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-selection-model.ts similarity index 100% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-selection-model.ts rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-selection-model.ts diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-sort-header.tsx b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-sort-header.tsx similarity index 97% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-sort-header.tsx rename to src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-sort-header.tsx index efc37758bfc..f96cea3f1b4 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-sort-header.tsx +++ b/src/renderer/src/components/workspace-cleanup/browse/workspace-cleanup-sort-header.tsx @@ -10,11 +10,11 @@ import { DropdownMenuTrigger } from '@/components/ui/dropdown-menu' import { useTranslate } from '@/i18n/use-translate' -import { WORKSPACE_CLEANUP_SORT_FIELD_VALUES } from '../../../../shared/workspace-cleanup/workspace-cleanup-facet-rankings' +import { WORKSPACE_CLEANUP_SORT_FIELD_VALUES } from '../../../../../shared/workspace-cleanup/workspace-cleanup-facet-rankings' import type { WorkspaceCleanupSortField, WorkspaceCleanupSortState -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' import { getWorkspaceCleanupSortFieldLabel } from './workspace-cleanup-facet-labels' export function WorkspaceCleanupSortHeader({ diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-git-evidence.test.tsx b/src/renderer/src/components/workspace-cleanup/candidate-evidence/use-workspace-cleanup-git-evidence.test.tsx similarity index 74% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-git-evidence.test.tsx rename to src/renderer/src/components/workspace-cleanup/candidate-evidence/use-workspace-cleanup-git-evidence.test.tsx index 2dd8f9e4245..8119080701f 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-git-evidence.test.tsx +++ b/src/renderer/src/components/workspace-cleanup/candidate-evidence/use-workspace-cleanup-git-evidence.test.tsx @@ -5,8 +5,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import type { WorkspaceCleanupScanProgress, WorkspaceCleanupScanResult -} from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { makeFacetCandidate } from './workspace-cleanup-facet.test.fixture' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { makeFacetCandidate } from '../query/workspace-cleanup-facet.test.fixture' import { useWorkspaceCleanupGitEvidence } from './use-workspace-cleanup-git-evidence' const holders = vi.hoisted(() => ({ cancelScan: vi.fn(), scan: vi.fn() })) @@ -102,38 +102,6 @@ describe('useWorkspaceCleanupGitEvidence', () => { expect(view.result.current.pendingWorktreeIds.size).toBe(5) }) - it('chunks dispatches at the shared target batch limit so no queued id is dropped', async () => { - const first = Array.from({ length: 300 }, (_, i) => deferredCandidate(`a${i}`)) - const grown = [...first, ...Array.from({ length: 600 }, (_, i) => deferredCandidate(`b${i}`))] - const view = renderHook( - ({ candidates }: { candidates: ReturnType[] }) => - useWorkspaceCleanupGitEvidence({ enabled: true, candidates, scannedAt: 1 }), - { initialProps: { candidates: first } } - ) - - await waitFor(() => expect(holders.scan).toHaveBeenCalledTimes(1)) - expect(pending[0]?.worktreeIds).toHaveLength(300) - - view.rerender({ candidates: grown }) - await waitFor(() => expect(view.result.current.totalCount).toBe(900)) - expect(holders.scan).toHaveBeenCalledTimes(1) - - await act(async () => { - pending[0]?.resolve({ scannedAt: 1, candidates: [], errors: [] }) - }) - await waitFor(() => expect(holders.scan).toHaveBeenCalledTimes(2)) - expect(pending[1]?.worktreeIds).toHaveLength(500) - - await act(async () => { - pending[1]?.resolve({ scannedAt: 1, candidates: [], errors: [] }) - }) - await waitFor(() => expect(holders.scan).toHaveBeenCalledTimes(3)) - expect(pending[2]?.worktreeIds).toHaveLength(100) - - const dispatched = pending.flatMap((request) => request.worktreeIds) - expect(new Set(dispatched).size).toBe(900) - }) - it('restarts evidence collection when the settled scan snapshot changes', async () => { const candidates = [deferredCandidate('a')] const view = renderHook( diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-git-evidence.ts b/src/renderer/src/components/workspace-cleanup/candidate-evidence/use-workspace-cleanup-git-evidence.ts similarity index 97% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-git-evidence.ts rename to src/renderer/src/components/workspace-cleanup/candidate-evidence/use-workspace-cleanup-git-evidence.ts index 9ae4bf5f83b..48f23a84f26 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-git-evidence.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-evidence/use-workspace-cleanup-git-evidence.ts @@ -1,13 +1,13 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' import { useMountedRef } from '@/hooks/useMountedRef' -import { getWorkspaceCleanupCandidateIdentity } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import { getWorkspaceCleanupCandidateIdentity } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import { selectWorkspaceCleanupGitEvidenceTargets } from './workspace-cleanup-git-evidence' import { type WorkspaceCleanupCandidate, WORKSPACE_CLEANUP_TARGET_BATCH_LIMIT as WORKSPACE_CLEANUP_GIT_EVIDENCE_MAX_TARGETS -} from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { createNonSecureContextUuid as createBrowserUuid } from '../../../../shared/non-secure-context-uuid' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { createNonSecureContextUuid as createBrowserUuid } from '../../../../../shared/non-secure-context-uuid' import { useAppStore } from '@/store' import { cancelWorkspaceCleanupHostScan, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-git-evidence.ts b/src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-git-evidence.ts similarity index 94% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-git-evidence.ts rename to src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-git-evidence.ts index 554dd2fc5da..e16d5f460e7 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-git-evidence.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-git-evidence.ts @@ -3,13 +3,13 @@ import { type WorkspaceCleanupCandidate, type WorkspaceCleanupReason, WORKSPACE_CLEANUP_TARGET_BATCH_LIMIT as WORKSPACE_CLEANUP_GIT_EVIDENCE_MAX_TARGETS -} from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { getWorkspaceCleanupCandidateIdentity } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { getWorkspaceCleanupCandidateIdentity } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import type { WorkspaceCleanupFilterState, WorkspaceCleanupSortField, WorkspaceCleanupSortState -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' /** Broad scans may defer Git reads; resolve unknown rows on demand before trusting Git-based filtering or sorting. */ export function hasWorkspaceCleanupGitEvidence(candidate: WorkspaceCleanupCandidate): boolean { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-filter.test.ts b/src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-host-index.test.ts similarity index 90% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-filter.test.ts rename to src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-host-index.test.ts index 106770fc7f2..c10fe076afc 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-filter.test.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-host-index.test.ts @@ -2,9 +2,9 @@ import { describe, expect, it } from 'vitest' import { buildWorkspaceCleanupSizeIndex, buildWorkspaceCleanupWorktreeIndex -} from './workspace-cleanup-host-identity' -import { getWorkspaceCleanupHostIdentity } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' -import { makeFacetCandidate } from './workspace-cleanup-facet.test.fixture' +} from './workspace-cleanup-host-indexes' +import { getWorkspaceCleanupHostIdentity } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import { makeFacetCandidate } from '../query/workspace-cleanup-facet.test.fixture' describe('facet building', () => { it('keeps same-id worktrees and sizes isolated by host', () => { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-host-identity.ts b/src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-host-indexes.ts similarity index 88% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-host-identity.ts rename to src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-host-indexes.ts index 96857c55a99..15a56730e3e 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-host-identity.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-host-indexes.ts @@ -2,15 +2,15 @@ import { getRepoExecutionHostId, normalizeExecutionHostId, type ExecutionHostId -} from '../../../../shared/execution-host' -import type { Repo } from '../../../../shared/repo-types' -import type { Worktree } from '../../../../shared/worktree/types' +} from '../../../../../shared/execution-host' +import type { Repo } from '../../../../../shared/repo-types' +import type { Worktree } from '../../../../../shared/worktree/types' import type { WorkspaceSpaceWorktree, WorkspaceSpaceWorktreeMeasurement -} from '../../../../shared/workspace-disk-space/workspace-space-types' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { getWorkspaceCleanupHostIdentity } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +} from '../../../../../shared/workspace-disk-space/workspace-space-types' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { getWorkspaceCleanupHostIdentity } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' /** Structural subset of `Worktree` kept in sync with the persisted source. */ export type WorkspaceCleanupWorktreeFacts = Pick & diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation.ts b/src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-review-lookup.ts similarity index 79% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation.ts rename to src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-review-lookup.ts index ac913c7e51d..9f9bc6e9fd3 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-review-lookup.ts @@ -1,40 +1,21 @@ +// @protected-copy scope=file import { getHostedReviewCacheKey } from '@/store/slices/hosted-review/hosted-review-cache-identity' import type { AppState } from '@/store/types' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import { getRepoExecutionHostId } from '../../../../shared/execution-host' +import { getRepoExecutionHostId } from '../../../../../shared/execution-host' import type { HostedReviewInfo, HostedReviewProvider -} from '../../../../shared/source-control/hosted-review/hosted-review-contracts' -import type { Repo } from '../../../../shared/repo-types' -import type { Worktree } from '../../../../shared/worktree/types' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { isGitHubPRSuppressed } from '../../../../shared/worktree/github-pr-suppression' -import './workspace-cleanup-host-identity' -import { getWorkspaceCleanupCandidateHostId } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +} from '../../../../../shared/source-control/hosted-review/hosted-review-contracts' +import type { Repo } from '../../../../../shared/repo-types' +import type { Worktree } from '../../../../../shared/worktree/types' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { isGitHubPRSuppressed } from '../../../../../shared/worktree/github-pr-suppression' +import './workspace-cleanup-host-indexes' +import { getWorkspaceCleanupCandidateHostId } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import { selectWorktreesByRepo } from '@/store/workspaces/catalog/workspace-record-fields' -export type WorkspaceCleanupSortKey = 'activity' | 'name' | 'repo' | 'review' | 'git' -export type WorkspaceCleanupSortDirection = 'asc' | 'desc' -export type WorkspaceCleanupTimeFilter = 'all' | '30d' | '90d' | 'archived' -export type WorkspaceCleanupReviewFilter = - | 'all' - | 'no-review' - | 'has-review' - | 'open-review' - | 'closed-review' -export type WorkspaceCleanupGitFilter = 'all' | 'clean' | 'dirty' | 'unpushed' | 'unknown' -export type WorkspaceCleanupContextFilter = 'all' | 'has-context' | 'no-context' - -export type WorkspaceCleanupFilters = { - query: string - time: WorkspaceCleanupTimeFilter - review: WorkspaceCleanupReviewFilter - git: WorkspaceCleanupGitFilter - context: WorkspaceCleanupContextFilter -} - export type WorkspaceCleanupReviewInfo = { hasReview: boolean label: string | null @@ -53,14 +34,6 @@ export type WorkspaceCleanupReviewLookup = { worktreesByRepoAndId: ReadonlyMap } -export { - filterWorkspaceCleanupCandidates, - getWorkspaceCleanupGitState, - getWorkspaceCleanupSearchText, - hasWorkspaceCleanupLocalContext, - sortWorkspaceCleanupCandidates -} from './workspace-cleanup-filter-sort' - export function getWorkspaceCleanupReviewInfo( candidate: WorkspaceCleanupCandidate, state: WorkspaceCleanupRendererStateInputs, diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-row-order.test.ts b/src/renderer/src/components/workspace-cleanup/candidate-list/use-workspace-cleanup-row-order.test.ts similarity index 90% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-row-order.test.ts rename to src/renderer/src/components/workspace-cleanup/candidate-list/use-workspace-cleanup-row-order.test.ts index aa2c8d9ce9b..773c8fafc05 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-row-order.test.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/use-workspace-cleanup-row-order.test.ts @@ -3,7 +3,7 @@ import { renderHook } from '@testing-library/react' import { describe, expect, it } from 'vitest' import { useWorkspaceCleanupRowOrder } from './use-workspace-cleanup-row-order' -import type { WorkspaceCleanupSortState } from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +import type { WorkspaceCleanupSortState } from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' type Row = { worktreeId: string } diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-row-order.ts b/src/renderer/src/components/workspace-cleanup/candidate-list/use-workspace-cleanup-row-order.ts similarity index 96% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-row-order.ts rename to src/renderer/src/components/workspace-cleanup/candidate-list/use-workspace-cleanup-row-order.ts index 9331744a6af..af35a80bfd1 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-row-order.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/use-workspace-cleanup-row-order.ts @@ -1,5 +1,5 @@ import { useEffect, useEffectEvent, useMemo, useRef, useState } from 'react' -import type { WorkspaceCleanupSortState } from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +import type { WorkspaceCleanupSortState } from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' type OrderedRow = { worktreeId: string } diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-labels.ts b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-labels.ts similarity index 95% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-labels.ts rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-labels.ts index cda2061e688..70c1fe1b97a 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-labels.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-labels.ts @@ -1,8 +1,8 @@ import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import type { WorkspaceCleanupBlocker } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import type { WorkspaceCleanupGitState } from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' -import type { WorktreeAtRiskLocalFiles } from '../../../../shared/worktree/at-risk-local-files' +import type { WorkspaceCleanupBlocker } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupGitState } from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +import type { WorktreeAtRiskLocalFiles } from '../../../../../shared/worktree/at-risk-local-files' import { formatAtRiskLocalFileNames } from '@/lib/workspace/display/at-risk-local-files-label' type ContextDetailKind = 'terminal' | 'editor' | 'browser' | 'diff' | 'agent' diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-list.tsx b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-list.tsx similarity index 100% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-list.tsx rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-list.tsx diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-data.ts b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row-data.ts similarity index 97% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-data.ts rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row-data.ts index a28ff7532bd..31f86c98056 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-data.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row-data.ts @@ -1,11 +1,12 @@ +// @protected-copy scope=file import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' import { isWorkspaceCleanupHeadLanded, type WorkspaceCleanupBlocker, type WorkspaceCleanupCandidate -} from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { getWorkspaceCleanupGitState } from './workspace-cleanup-presentation' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { getWorkspaceCleanupGitState } from '../query/workspace-cleanup-candidate-filter-sort' import { formatUnpushedCommitCount, formatWorkspaceCleanupContextDetail, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-details.tsx b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row-details.tsx similarity index 95% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-details.tsx rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row-details.tsx index ca2459094ee..f16dc97d6c7 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-details.tsx +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row-details.tsx @@ -2,9 +2,9 @@ import React from 'react' import { cn } from '@/lib/class-names' import { useTranslate, type Translate } from '@/i18n/use-translate' import { CopyButton } from '@/components/right-sidebar/checks-panel/comments/comment-controls' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import { formatGitStatus } from './workspace-cleanup-candidate-row-data' -import { getWorkspaceCleanupGitState } from './workspace-cleanup-presentation' +import { getWorkspaceCleanupGitState } from '../query/workspace-cleanup-candidate-filter-sort' type CandidateRowDetailsProps = { /** Non-git flags; git safety already reads in the Git line. */ diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-trailing.tsx b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row-trailing.tsx similarity index 97% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-trailing.tsx rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row-trailing.tsx index afb5b0d6867..40fabef505d 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-trailing.tsx +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row-trailing.tsx @@ -4,8 +4,8 @@ import { Button } from '@/components/ui/button' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { cn } from '@/lib/class-names' import { useTranslate } from '@/i18n/use-translate' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { WorkspaceCleanupForgetLocallyButton } from './workspace-cleanup-forget-locally-button' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { WorkspaceCleanupForgetLocallyButton } from '../removal/workspace-cleanup-forget-locally-button' import { getWorkspaceCleanupCandidateAccessibleName } from './workspace-cleanup-host-label' type CandidateRowTrailingProps = { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.fixture.tsx b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row.fixture.tsx similarity index 95% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.fixture.tsx rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row.fixture.tsx index f2c63a7f0aa..2c74e9b76d6 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.fixture.tsx +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row.fixture.tsx @@ -1,11 +1,11 @@ -import { defineFixture } from '../../component-fixtures/component-fixture' -import { REPO_COLORS } from '../../../../shared/repo-badge-color' -import type { ExecutionHostId } from '../../../../shared/execution-host' -import type { Repo } from '../../../../shared/repo-types' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import { defineFixture } from '../../../component-fixtures/component-fixture' +import { REPO_COLORS } from '../../../../../shared/repo-badge-color' +import type { ExecutionHostId } from '../../../../../shared/execution-host' +import type { Repo } from '../../../../../shared/repo-types' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import type { WorkspaceCleanupFailure } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' import { CandidateRow, type WorkspaceCleanupDeletionPhase } from './workspace-cleanup-candidate-row' -import type { WorkspaceCleanupReviewInfo } from './workspace-cleanup-presentation' +import type { WorkspaceCleanupReviewInfo } from '../candidate-evidence/workspace-cleanup-review-lookup' const NOW = 1_700_000_000_000 const LOCAL: ExecutionHostId = 'local' diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.tsx b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row.tsx similarity index 97% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.tsx rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row.tsx index 449c85aa33c..f7e6a1b93a1 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.tsx +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-candidate-row.tsx @@ -17,8 +17,8 @@ import { useUiLanguageRevision } from '@/i18n/use-ui-language-revision' import { canQueueWorkspaceCleanupCandidate, type WorkspaceCleanupCandidate -} from '../../../../shared/workspace-cleanup/workspace-cleanup' -import type { WorkspaceCleanupReviewInfo } from './workspace-cleanup-presentation' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupReviewInfo } from '../candidate-evidence/workspace-cleanup-review-lookup' import { CandidateRowDetails } from './workspace-cleanup-candidate-row-details' import { formatBranchSafetyDetails, @@ -39,13 +39,13 @@ import { } from './workspace-cleanup-metadata-chip' import { RepoIconGlyph } from '@/components/repo/repo-icon' import { resolveRepoHeaderColor } from '@/components/sidebar/worktree-list/rows/project-header-color' -import type { Repo } from '../../../../shared/repo-types' +import type { Repo } from '../../../../../shared/repo-types' import { CandidateRowTrailing } from './workspace-cleanup-candidate-row-trailing' import { getWorkspaceCleanupCandidateAccessibleName, getWorkspaceCleanupCandidateHostLabel } from './workspace-cleanup-host-label' -import { getReviewTooltip } from './workspace-cleanup-row-labels' +import { getReviewTooltip } from './workspace-cleanup-review-labels' import type { WorkspaceCleanupFailure } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' export type WorkspaceCleanupDeletionPhase = 'deleting' | 'queued' diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-flat-list.test.tsx b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-flat-list.test.tsx similarity index 94% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-flat-list.test.tsx rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-flat-list.test.tsx index e0fe0e3ad1b..b5f2c6b64af 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-flat-list.test.tsx +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-flat-list.test.tsx @@ -5,9 +5,9 @@ import { createRoot, type Root } from 'react-dom/client' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { CandidateRow } from './workspace-cleanup-candidate-row' import { WorkspaceCleanupCandidateList } from './workspace-cleanup-candidate-list' -import { makeNamedFacets } from './workspace-cleanup-facet.test.fixture' +import { makeNamedFacets } from '../query/workspace-cleanup-facet.test.fixture' -import type { WorkspaceCleanupFacets } from './workspace-cleanup-facets' +import type { WorkspaceCleanupFacets } from '../query/workspace-cleanup-facets' vi.mock('@/components/ui/tooltip', () => ({ Tooltip: ({ children }: { children: ReactNode }) => <>{children}, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-host-label.ts b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-host-label.ts similarity index 70% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-host-label.ts rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-host-label.ts index 2684a7ef620..9680306f2f3 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-host-label.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-host-label.ts @@ -1,9 +1,10 @@ +// @protected-copy scope=file import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import { getTranslatedExecutionHostLabel } from '../sidebar/execution-host-label' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import './workspace-cleanup-host-identity' -import { resolveWorkspaceCleanupRemovalHostId } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import { getTranslatedExecutionHostLabel } from '../../sidebar/execution-host-label' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import '../candidate-evidence/workspace-cleanup-host-indexes' +import { resolveWorkspaceCleanupRemovalHostId } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' export function getWorkspaceCleanupCandidateHostLabel( candidate: WorkspaceCleanupCandidate, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-metadata-chip.tsx b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-metadata-chip.tsx similarity index 100% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-metadata-chip.tsx rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-metadata-chip.tsx diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-relative-time.ts b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-relative-time.ts similarity index 97% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-relative-time.ts rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-relative-time.ts index 744d344fcf0..f0ba48672ac 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-relative-time.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-relative-time.ts @@ -1,3 +1,4 @@ +// @protected-copy scope=file import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-row-labels.ts b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-review-labels.ts similarity index 72% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-row-labels.ts rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-review-labels.ts index 13bd980ab7f..f24a2565b59 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-row-labels.ts +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-review-labels.ts @@ -1,5 +1,5 @@ -import type { WorkspaceCleanupReviewInfo } from './workspace-cleanup-presentation' -import { getWorkspaceCleanupReviewStateLabel } from './workspace-cleanup-facet-labels' +import type { WorkspaceCleanupReviewInfo } from '../candidate-evidence/workspace-cleanup-review-lookup' +import { getWorkspaceCleanupReviewStateLabel } from '../browse/workspace-cleanup-facet-labels' export function getReviewTooltip(reviewInfo: WorkspaceCleanupReviewInfo): string { return [reviewInfo.label, getReviewStateSrText(reviewInfo)].filter(Boolean).join(' · ') diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-row-list.tsx b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-row-list.tsx similarity index 92% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-row-list.tsx rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-row-list.tsx index a8637aaf135..93b5aaec867 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-row-list.tsx +++ b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-row-list.tsx @@ -1,19 +1,19 @@ import React, { useMemo } from 'react' import { useAppStore } from '@/store' import { useTranslate } from '@/i18n/use-translate' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { formatBytes } from '../../../../shared/format-bytes' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { formatBytes } from '../../../../../shared/format-bytes' import { CandidateRow, type WorkspaceCleanupDeletionPhase } from './workspace-cleanup-candidate-row' import { WorkspaceCleanupCandidateList } from './workspace-cleanup-candidate-list' -import { WorkspaceCleanupEmptyState } from './workspace-cleanup-dialog-notices' -import type { WorkspaceCleanupFacets } from './workspace-cleanup-facets' +import { WorkspaceCleanupEmptyState } from '../dialog/workspace-cleanup-dialog-notices' +import type { WorkspaceCleanupFacets } from '../query/workspace-cleanup-facets' import { formatWorkspaceCleanupRelativeTime } from './workspace-cleanup-relative-time' import type { WorkspaceCleanupFailure } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' import { findWorkspaceCleanupCandidateRepo, groupWorkspaceCleanupReposById -} from './workspace-cleanup-presentation' -import { getWorkspaceCleanupCandidateHostId } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +} from '../candidate-evidence/workspace-cleanup-review-lookup' +import { getWorkspaceCleanupCandidateHostId } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' export type WorkspaceCleanupRowListState = { rows: readonly WorkspaceCleanupFacets[] diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-status-pill.tsx b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-status-pill.tsx similarity index 100% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-status-pill.tsx rename to src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-status-pill.tsx diff --git a/src/renderer/src/components/workspace-cleanup/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx b/src/renderer/src/components/workspace-cleanup/dialog/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx similarity index 95% rename from src/renderer/src/components/workspace-cleanup/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx rename to src/renderer/src/components/workspace-cleanup/dialog/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx index e754f53cc3d..b8bd91fbbf0 100644 --- a/src/renderer/src/components/workspace-cleanup/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx +++ b/src/renderer/src/components/workspace-cleanup/dialog/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx @@ -7,15 +7,15 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { WorkspaceCleanupScanProgress, WorkspaceCleanupScanResult -} from '../../../../shared/workspace-cleanup/workspace-cleanup' -import type { WorkspaceCleanupBrowseState } from '../../../../shared/workspace-cleanup/workspace-cleanup-browse-state' -import { createTestStore, seedStore } from '../../store/testing/app-store-test-fixture' -import { resetWorkspaceCleanupBrowsePersistTimer } from '../../store/slices/workspace-cleanup/workspace-cleanup-browse' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupBrowseState } from '../../../../../shared/workspace-cleanup/workspace-cleanup-browse-state' +import { createTestStore, seedStore } from '../../../store/testing/app-store-test-fixture' +import { resetWorkspaceCleanupBrowsePersistTimer } from '../../../store/slices/workspace-cleanup/workspace-cleanup-browse' import { NOW, makeCandidate -} from '../../store/slices/workspace-cleanup/workspace-cleanup-slice-test-harness' -import WorkspaceCleanupDialog from './WorkspaceCleanupDialog' +} from '../../../store/slices/workspace-cleanup/workspace-cleanup-slice-test-harness' +import WorkspaceCleanupDialog from '../WorkspaceCleanupDialog' const holders = vi.hoisted(() => ({ store: null as unknown as { (selector: (s: unknown) => unknown): unknown } & { diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-dialog-lifecycle.ts b/src/renderer/src/components/workspace-cleanup/dialog/use-workspace-cleanup-dialog-lifecycle.ts similarity index 94% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-dialog-lifecycle.ts rename to src/renderer/src/components/workspace-cleanup/dialog/use-workspace-cleanup-dialog-lifecycle.ts index 275054105b8..1b0dd087592 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-dialog-lifecycle.ts +++ b/src/renderer/src/components/workspace-cleanup/dialog/use-workspace-cleanup-dialog-lifecycle.ts @@ -3,8 +3,8 @@ import { useAppStore } from '@/store' import { useWorkspaceCleanupRemoval, type WorkspaceCleanupRemovalController -} from './use-workspace-cleanup-removal' -import { useWorkspaceCleanupScanLifecycle } from './use-workspace-cleanup-scan-lifecycle' +} from '../removal/use-workspace-cleanup-removal' +import { useWorkspaceCleanupScanLifecycle } from '../scanning/use-workspace-cleanup-scan-lifecycle' const WORKSPACE_CLEANUP_CLOSE_LINGER_MS = 300 diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-dialog-header.fixture.tsx b/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-header.fixture.tsx similarity index 84% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-dialog-header.fixture.tsx rename to src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-header.fixture.tsx index 4d07c9a8864..74d873c8fcf 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-dialog-header.fixture.tsx +++ b/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-header.fixture.tsx @@ -1,14 +1,14 @@ -import { defineFixture } from '../../component-fixtures/component-fixture' +import { defineFixture } from '../../../component-fixtures/component-fixture' import { Dialog } from '@/components/ui/dialog' -import type { WorkspaceCleanupScanProgress } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupScanProgress } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import { createDefaultWorkspaceCleanupFilterState, DEFAULT_WORKSPACE_CLEANUP_SORT -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' import { WorkspaceCleanupDialogHeader } from './workspace-cleanup-dialog-header' -import { WorkspaceCleanupFilterBar } from './workspace-cleanup-filter-bar' -import type { WorkspaceCleanupFacetCounts } from './workspace-cleanup-facet-panel-model' -import { WorkspaceCleanupSortHeader } from './workspace-cleanup-sort-header' +import { WorkspaceCleanupFilterBar } from '../browse/workspace-cleanup-filter-bar' +import type { WorkspaceCleanupFacetCounts } from '../browse/workspace-cleanup-facet-panel-model' +import { WorkspaceCleanupSortHeader } from '../browse/workspace-cleanup-sort-header' const NOW = Date.now() const COUNTS: WorkspaceCleanupFacetCounts = { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-dialog-header.tsx b/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-header.tsx similarity index 95% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-dialog-header.tsx rename to src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-header.tsx index 4006be96ab9..47a895eb5ab 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-dialog-header.tsx +++ b/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-header.tsx @@ -5,7 +5,7 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { cn } from '@/lib/class-names' import { useTranslate } from '@/i18n/use-translate' -import type { WorkspaceCleanupScanProgress } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupScanProgress } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import { WorkspaceCleanupFreshness } from './workspace-cleanup-freshness' export function WorkspaceCleanupDialogHeader({ diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-dialog-notices.tsx b/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-notices.tsx similarity index 96% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-dialog-notices.tsx rename to src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-notices.tsx index e882e802677..eec4f02fade 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-dialog-notices.tsx +++ b/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-notices.tsx @@ -2,7 +2,7 @@ import React from 'react' import { AlertTriangle, Loader2 } from 'lucide-react' import { Button } from '@/components/ui/button' import { useTranslate } from '@/i18n/use-translate' -import type { WorkspaceCleanupScanProgress } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupScanProgress } from '../../../../../shared/workspace-cleanup/workspace-cleanup' export function WorkspaceCleanupInitialScanBanner({ progress diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-freshness.tsx b/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-freshness.tsx similarity index 90% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-freshness.tsx rename to src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-freshness.tsx index ce91e427a7e..82c9f0b74fd 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-freshness.tsx +++ b/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-freshness.tsx @@ -3,8 +3,8 @@ import { useNow } from '@/hooks/use-now' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' -import type { WorkspaceCleanupScanProgress } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { formatWorkspaceCleanupRelativeTime } from './workspace-cleanup-relative-time' +import type { WorkspaceCleanupScanProgress } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { formatWorkspaceCleanupRelativeTime } from '../candidate-list/workspace-cleanup-relative-time' /** Show the displayed snapshot’s age while a nonblocking rescan indicator streams updates. */ export function WorkspaceCleanupFreshness({ diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-filter-sort.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-candidate-filter-sort.ts similarity index 86% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-filter-sort.ts rename to src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-candidate-filter-sort.ts index ce286db125c..29ffbefb0ad 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-filter-sort.ts +++ b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-candidate-filter-sort.ts @@ -2,19 +2,35 @@ import { compareDisplayName } from '@/lib/workspace/display/worktree-display-nam import { isWorkspaceCleanupHeadLanded, type WorkspaceCleanupCandidate -} from '../../../../shared/workspace-cleanup/workspace-cleanup' -import type { WorkspaceCleanupGitState } from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' -import { getWorkspaceCleanupGitChipLabel } from './workspace-cleanup-candidate-labels' -import type { - WorkspaceCleanupContextFilter, - WorkspaceCleanupFilters, - WorkspaceCleanupGitFilter, - WorkspaceCleanupReviewFilter, - WorkspaceCleanupReviewInfo, - WorkspaceCleanupSortDirection, - WorkspaceCleanupSortKey, - WorkspaceCleanupTimeFilter -} from './workspace-cleanup-presentation' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupGitState } from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +import { getWorkspaceCleanupGitChipLabel } from '../candidate-list/workspace-cleanup-candidate-labels' +import type { WorkspaceCleanupReviewInfo } from '../candidate-evidence/workspace-cleanup-review-lookup' + +export type WorkspaceCleanupSortKey = 'activity' | 'name' | 'repo' | 'review' | 'git' + +export type WorkspaceCleanupSortDirection = 'asc' | 'desc' + +export type WorkspaceCleanupTimeFilter = 'all' | '30d' | '90d' | 'archived' + +export type WorkspaceCleanupReviewFilter = + | 'all' + | 'no-review' + | 'has-review' + | 'open-review' + | 'closed-review' + +export type WorkspaceCleanupGitFilter = 'all' | 'clean' | 'dirty' | 'unpushed' | 'unknown' + +export type WorkspaceCleanupContextFilter = 'all' | 'has-context' | 'no-context' + +export type WorkspaceCleanupFilters = { + query: string + time: WorkspaceCleanupTimeFilter + review: WorkspaceCleanupReviewFilter + git: WorkspaceCleanupGitFilter + context: WorkspaceCleanupContextFilter +} const DAY_MS = 24 * 60 * 60 * 1000 diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation-fixtures.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-candidate-state.test-fixture.ts similarity index 86% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation-fixtures.ts rename to src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-candidate-state.test-fixture.ts index ea4920b9d8f..ac7da037f0c 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation-fixtures.ts +++ b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-candidate-state.test-fixture.ts @@ -1,10 +1,10 @@ import type { AppState } from '@/store/types' import { translate } from '@/i18n/i18n' -import type { HostedReviewInfo } from '../../../../shared/source-control/hosted-review/hosted-review-contracts' -import type { Repo } from '../../../../shared/repo-types' -import type { Worktree } from '../../../../shared/worktree/types' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import type { WorkspaceCleanupFilters } from './workspace-cleanup-presentation' +import type { HostedReviewInfo } from '../../../../../shared/source-control/hosted-review/hosted-review-contracts' +import type { Repo } from '../../../../../shared/repo-types' +import type { Worktree } from '../../../../../shared/worktree/types' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupFilters } from './workspace-cleanup-candidate-filter-sort' export const NOW = 1_700_000_000_000 diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-matchers.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-matchers.ts similarity index 98% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-matchers.ts rename to src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-matchers.ts index e72201979ed..2fa45937bf2 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-matchers.ts +++ b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-matchers.ts @@ -12,7 +12,7 @@ import type { WorkspaceCleanupStatusFilter, WorkspaceCleanupTicketFilter, WorkspaceCleanupTriState -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' export const WORKSPACE_CLEANUP_DAY_MS = 24 * 60 * 60 * 1000 diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-row-caches.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-row-caches.ts similarity index 90% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-row-caches.ts rename to src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-row-caches.ts index 071deff9599..093d150b951 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-row-caches.ts +++ b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-row-caches.ts @@ -1,6 +1,6 @@ -import type { LiveAgentWorktreeStatus } from '../../../../shared/sidebar-list/workspace-activity-facts' -import type { WorkspaceStatusDefinition } from '../../../../shared/worktree/types' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { LiveAgentWorktreeStatus } from '../../../../../shared/sidebar-list/workspace-activity-facts' +import type { WorkspaceStatusDefinition } from '../../../../../shared/worktree/types' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import { buildWorkspaceCleanupFacets, type WorkspaceCleanupFacetSources, @@ -11,13 +11,13 @@ import { type WorkspaceCleanupRendererStateInputs, type WorkspaceCleanupReviewInfo, type WorkspaceCleanupReviewLookup -} from './workspace-cleanup-presentation' -import './workspace-cleanup-host-identity' +} from '../candidate-evidence/workspace-cleanup-review-lookup' +import '../candidate-evidence/workspace-cleanup-host-indexes' import { getWorkspaceCleanupCandidateHostId, getWorkspaceCleanupCandidateIdentity, getWorkspaceCleanupHostIdentity -} from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import { getWorktreeVisitTimestamp } from '@/lib/workspace/activation/worktree-visit-recency' import type { Translate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-sort.test.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.test.ts similarity index 87% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-sort.test.ts rename to src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.test.ts index 273ea4380ba..30d335b3083 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-sort.test.ts +++ b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest' import { makeNamedFacets } from './workspace-cleanup-facet.test.fixture' import { sortWorkspaceCleanupFacets } from './workspace-cleanup-facet-sort' -import type { WorkspaceCleanupSortField } from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +import type { WorkspaceCleanupSortField } from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' function names( rows: ReturnType[], diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-sort.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.ts similarity index 96% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-sort.ts rename to src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.ts index 3b4910fca64..0071f121bfc 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-sort.ts +++ b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.ts @@ -4,11 +4,11 @@ import { getWorkspaceCleanupBlockerSeverity, getWorkspaceCleanupGitRank, getWorkspaceCleanupReviewRank -} from '../../../../shared/workspace-cleanup/workspace-cleanup-facet-rankings' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-facet-rankings' import type { WorkspaceCleanupSortField, WorkspaceCleanupSortState -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' /** Missing signals sort last in either direction; flipping reverses only rows with evidence. */ const ABSENT_LAST_FIELDS: ReadonlySet = new Set([ diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet.test.fixture.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet.test.fixture.ts similarity index 86% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet.test.fixture.ts rename to src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet.test.fixture.ts index 21a567c6f37..ba6c9e85fa1 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet.test.fixture.ts +++ b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet.test.fixture.ts @@ -1,14 +1,14 @@ -import type { LiveAgentWorktreeStatus } from '../../../../shared/sidebar-list/workspace-activity-facts' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { cloneDefaultWorkspaceStatuses } from '../../../../shared/workspace-statuses' -import { getWorkspaceCleanupCandidateIdentity } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import type { LiveAgentWorktreeStatus } from '../../../../../shared/sidebar-list/workspace-activity-facts' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { cloneDefaultWorkspaceStatuses } from '../../../../../shared/workspace-statuses' +import { getWorkspaceCleanupCandidateIdentity } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import { buildWorkspaceCleanupFacets, type WorkspaceCleanupFacetSources, - type WorkspaceCleanupFacets, - type WorkspaceCleanupWorktreeFacts + type WorkspaceCleanupFacets } from './workspace-cleanup-facets' -import type { WorkspaceCleanupReviewInfo } from './workspace-cleanup-presentation' +import type { WorkspaceCleanupWorktreeFacts } from '../candidate-evidence/workspace-cleanup-host-indexes' +import type { WorkspaceCleanupReviewInfo } from '../candidate-evidence/workspace-cleanup-review-lookup' export const FACET_NOW = 1_700_000_000_000 export const DAY = 24 * 60 * 60 * 1000 diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facets.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facets.ts similarity index 90% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-facets.ts rename to src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facets.ts index c809dca9e69..a6f059eee7a 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-facets.ts +++ b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facets.ts @@ -1,32 +1,31 @@ -import type { LiveAgentWorktreeStatus } from '../../../../shared/sidebar-list/workspace-activity-facts' -import type { ExecutionHostId } from '../../../../shared/execution-host' -import type { WorkspaceStatusDefinition } from '../../../../shared/worktree/types' +import type { LiveAgentWorktreeStatus } from '../../../../../shared/sidebar-list/workspace-activity-facts' +import type { ExecutionHostId } from '../../../../../shared/execution-host' +import type { WorkspaceStatusDefinition } from '../../../../../shared/worktree/types' import { getWorkspaceCleanupCandidateIdentity, getWorkspaceCleanupCandidateHostId, getWorkspaceCleanupHostIdentity -} from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' -import { getWorkspaceStatus } from '../../../../shared/workspace-statuses' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import { getWorkspaceStatus } from '../../../../../shared/workspace-statuses' import { canQueueWorkspaceCleanupCandidate, WORKSPACE_CLEANUP_BULK_SELECT_EXCLUSIONS, type WorkspaceCleanupBlocker, type WorkspaceCleanupCandidate -} from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { getWorkspaceCleanupGitState } from './workspace-cleanup-filter-sort' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { getWorkspaceCleanupGitState } from './workspace-cleanup-candidate-filter-sort' import type { WorkspaceCleanupAgentState, WorkspaceCleanupGitState, WorkspaceCleanupReviewState, WorkspaceCleanupTicketSource -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' -import type { WorkspaceCleanupReviewInfo } from './workspace-cleanup-presentation' -import type { WorkspaceCleanupWorktreeFacts } from './workspace-cleanup-host-identity' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +import type { WorkspaceCleanupReviewInfo } from '../candidate-evidence/workspace-cleanup-review-lookup' +import type { WorkspaceCleanupWorktreeFacts } from '../candidate-evidence/workspace-cleanup-host-indexes' import { getWorktreeVisitTimestamp } from '@/lib/workspace/activation/worktree-visit-recency' -import { getWorkspaceCleanupGitChipLabel } from './workspace-cleanup-candidate-labels' +import { getWorkspaceCleanupGitChipLabel } from '../candidate-list/workspace-cleanup-candidate-labels' import type { Translate } from '@/i18n/use-translate' -export type { WorkspaceCleanupWorktreeFacts } from './workspace-cleanup-host-identity' export type WorkspaceCleanupFacetSources = { worktreeById?: ReadonlyMap diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-query.test.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-query.test.ts similarity index 93% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-query.test.ts rename to src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-query.test.ts index fad0636825e..0fa5250fab8 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-query.test.ts +++ b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-query.test.ts @@ -1,5 +1,5 @@ import { expect, it } from 'vitest' -import { createDefaultWorkspaceCleanupFilterState } from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +import { createDefaultWorkspaceCleanupFilterState } from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' import { makeFacetCandidate } from './workspace-cleanup-facet.test.fixture' import { queryWorkspaceCleanupCandidates } from './workspace-cleanup-query' diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-query.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-query.ts similarity index 96% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-query.ts rename to src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-query.ts index b1cdc9f8b82..4f29a6fb6a8 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-query.ts +++ b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-query.ts @@ -1,4 +1,4 @@ -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import { buildWorkspaceCleanupFacetList, type WorkspaceCleanupFacetSources, @@ -20,7 +20,7 @@ import { sortWorkspaceCleanupFacets } from './workspace-cleanup-facet-sort' import type { WorkspaceCleanupFilterState, WorkspaceCleanupSortState -} from '../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' export type WorkspaceCleanupQuery = { filters: WorkspaceCleanupFilterState diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-agent-stop-gate.ts b/src/renderer/src/components/workspace-cleanup/removal/agent-consent/use-workspace-cleanup-agent-stop-gate.ts similarity index 94% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-agent-stop-gate.ts rename to src/renderer/src/components/workspace-cleanup/removal/agent-consent/use-workspace-cleanup-agent-stop-gate.ts index e90e2a0318d..e08754f6a42 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-agent-stop-gate.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/agent-consent/use-workspace-cleanup-agent-stop-gate.ts @@ -1,7 +1,7 @@ import { useCallback, useState } from 'react' import { useAppStore } from '@/store' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { getWorkspaceCleanupCandidateIdentity } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import type { WorkspaceCleanupCandidate } from '../../../../../../shared/workspace-cleanup/workspace-cleanup' +import { getWorkspaceCleanupCandidateIdentity } from '../../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import { getWorkspaceCleanupAgentEvidence } from '@/store/slices/workspace-cleanup/workspace-cleanup-agent-evidence' import { withWorkspaceCleanupAgentStopApproval } from './workspace-cleanup-agent-stop-approval' diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-agent-stop-approval.ts b/src/renderer/src/components/workspace-cleanup/removal/agent-consent/workspace-cleanup-agent-stop-approval.ts similarity index 84% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-agent-stop-approval.ts rename to src/renderer/src/components/workspace-cleanup/removal/agent-consent/workspace-cleanup-agent-stop-approval.ts index ef87323cc90..ff388dfe663 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-agent-stop-approval.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/agent-consent/workspace-cleanup-agent-stop-approval.ts @@ -1,4 +1,4 @@ -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../../shared/workspace-cleanup/workspace-cleanup' /** Stamp explicit agent-stop consent; scan live-agent labels alone are not approval and must be rewritten before final dispatch. */ export function withWorkspaceCleanupAgentStopApproval( diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-confirm-stop-agents.tsx b/src/renderer/src/components/workspace-cleanup/removal/agent-consent/workspace-cleanup-confirm-stop-agents.tsx similarity index 91% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-confirm-stop-agents.tsx rename to src/renderer/src/components/workspace-cleanup/removal/agent-consent/workspace-cleanup-confirm-stop-agents.tsx index e54a36c8a36..84971ab0c1a 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-confirm-stop-agents.tsx +++ b/src/renderer/src/components/workspace-cleanup/removal/agent-consent/workspace-cleanup-confirm-stop-agents.tsx @@ -5,14 +5,14 @@ import { DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/co import { ScrollArea } from '@/components/ui/scroll-area' import { cn } from '@/lib/class-names' import { useTranslate } from '@/i18n/use-translate' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import './workspace-cleanup-host-identity' -import { getWorkspaceCleanupCandidateIdentity } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import type { WorkspaceCleanupCandidate } from '../../../../../../shared/workspace-cleanup/workspace-cleanup' +import '../../candidate-evidence/workspace-cleanup-host-indexes' +import { getWorkspaceCleanupCandidateIdentity } from '../../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import { getWorkspaceCleanupCandidateAccessibleName, getWorkspaceCleanupCandidateHostLabel -} from './workspace-cleanup-host-label' -import { WorkspaceCleanupMetadataChip } from './workspace-cleanup-metadata-chip' +} from '../../candidate-list/workspace-cleanup-host-label' +import { WorkspaceCleanupMetadataChip } from '../../candidate-list/workspace-cleanup-metadata-chip' export function WorkspaceCleanupConfirmStopAgents({ candidates, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-ancestor-skips.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-ancestor-skips.ts similarity index 87% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-ancestor-skips.ts rename to src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-ancestor-skips.ts index d68b0135857..fca1bcb04b1 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-ancestor-skips.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-ancestor-skips.ts @@ -1,11 +1,11 @@ -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../../shared/workspace-cleanup/workspace-cleanup' import { isPathInsideOrEqual, normalizeRuntimePathForComparison -} from '../../../../shared/cross-platform-path' +} from '../../../../../../shared/cross-platform-path' import type { WorkspaceCleanupFailure } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' import { translate } from '@/i18n/i18n' -import { getWorkspaceCleanupCandidateHostId } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import { getWorkspaceCleanupCandidateHostId } from '../../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' // Ancestor skips remain provisional while blockers remove; authoritative settlement hardens or lifts them. export type SkippedWorkspaceCleanupAncestor = { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal-late-settlement.test.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal-late-settlement.test.ts similarity index 98% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal-late-settlement.test.ts rename to src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal-late-settlement.test.ts index a56f2ecca6c..ac94dda238a 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal-late-settlement.test.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal-late-settlement.test.ts @@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { toast } from 'sonner' import type { WorkspaceCleanupRemoveResult } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' import { startWorkspaceCleanupBackgroundRemoval } from './workspace-cleanup-background-removal' -import { makeCandidate } from './workspace-cleanup-presentation-fixtures' +import { makeCandidate } from '../../query/workspace-cleanup-candidate-state.test-fixture' vi.mock('sonner', () => ({ toast: { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal.test.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal.test.ts similarity index 68% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal.test.ts rename to src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal.test.ts index 0a48889c8f9..f0503c2ebf5 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal.test.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal.test.ts @@ -1,7 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { startWorkspaceCleanupBackgroundRemoval } from './workspace-cleanup-background-removal' -import { makeCandidate } from './workspace-cleanup-presentation-fixtures' -import { getWorkspaceCleanupHostIdentity } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import { makeCandidate } from '../../query/workspace-cleanup-candidate-state.test-fixture' vi.mock('sonner', () => ({ toast: { @@ -11,7 +10,7 @@ vi.mock('sonner', () => ({ } })) -vi.mock('../sidebar/workspace-actions/deletion/preserved-branch-batch-toast', () => ({ +vi.mock('../../../sidebar/workspace-actions/deletion/preserved-branch-batch-toast', () => ({ showPreservedBranchBatchToast: vi.fn() })) @@ -149,58 +148,4 @@ describe('startWorkspaceCleanupBackgroundRemoval', () => { ] }) }) - - it('does not skip a paired-runtime ancestor after another runtime child fails', async () => { - const failedChild = makeCandidate({ - worktreeId: 'repo-1::/repo/parent/child', - displayName: 'child', - branch: 'child', - path: '/repo/parent/child', - executionHostId: 'runtime:hub-a' - }) - const unrelatedParent = makeCandidate({ - worktreeId: 'repo-2::/repo/parent', - repoId: 'repo-2', - repoName: 'Repo 2', - displayName: 'parent', - branch: 'parent', - path: '/repo/parent', - executionHostId: 'runtime:hub-b' - }) - const removeCandidates = vi - .fn() - .mockResolvedValueOnce({ - removedIds: [], - removedIdentities: [], - failures: [ - { - worktreeId: failedChild.worktreeId, - executionHostId: failedChild.executionHostId, - displayName: failedChild.displayName, - message: 'busy' - } - ] - }) - .mockResolvedValueOnce({ - removedIds: [unrelatedParent.worktreeId], - removedIdentities: [ - getWorkspaceCleanupHostIdentity('runtime:hub-b', unrelatedParent.worktreeId) - ], - failures: [] - }) - - startWorkspaceCleanupBackgroundRemoval({ - candidates: [unrelatedParent, failedChild], - removeCandidates, - onProgress: vi.fn() - }) - await settleBackgroundRemoval() - - expect(removeCandidates).toHaveBeenNthCalledWith(1, [failedChild.worktreeId], { - approvedCandidates: [failedChild] - }) - expect(removeCandidates).toHaveBeenNthCalledWith(2, [unrelatedParent.worktreeId], { - approvedCandidates: [unrelatedParent] - }) - }) }) diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal.ts similarity index 98% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal.ts rename to src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal.ts index b48d13f0f60..9f93749d1c3 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal.ts @@ -1,12 +1,13 @@ +// @protected-copy scope=file import { toast } from 'sonner' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../../shared/workspace-cleanup/workspace-cleanup' import type { WorkspaceCleanupFailure, WorkspaceCleanupRemoveOptions, WorkspaceCleanupRemoveResult } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' import { translate } from '@/i18n/i18n' -import { getWorkspaceCleanupCandidateHostId } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import { getWorkspaceCleanupCandidateHostId } from '../../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import { getSkippedAncestorMessage, isStrictWorkspaceCleanupDescendant, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-post-batch-late-settlement.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-post-batch-late-settlement.ts similarity index 98% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-post-batch-late-settlement.ts rename to src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-post-batch-late-settlement.ts index 40358d976d2..5d7b23cd21b 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-post-batch-late-settlement.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-post-batch-late-settlement.ts @@ -1,4 +1,4 @@ -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../../shared/workspace-cleanup/workspace-cleanup' import type { WorkspaceCleanupFailure, WorkspaceCleanupRemoveOptions, @@ -20,7 +20,7 @@ import { reclassifySkippedWorkspaceCleanupAncestors } from './workspace-cleanup- import { getWorkspaceCleanupCandidateHostId, getWorkspaceCleanupCandidateIdentity -} from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +} from '../../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' type WorkspaceCleanupRemoveCandidates = ( worktreeIds: readonly string[], diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-settlement.test.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.test.ts similarity index 95% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-settlement.test.ts rename to src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.test.ts index ccb408520ce..57d428bd218 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-settlement.test.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.test.ts @@ -3,7 +3,7 @@ import { trackWorkspaceCleanupLateSettlement, type WorkspaceCleanupRemovalSettlement } from './workspace-cleanup-removal-settlement' -import { makeCandidate } from './workspace-cleanup-presentation-fixtures' +import { makeCandidate } from '../../query/workspace-cleanup-candidate-state.test-fixture' describe('workspace cleanup late settlement tracking', () => { it('detaches the batch callback and reports with a compact candidate identity', async () => { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-settlement.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.ts similarity index 96% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-settlement.ts rename to src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.ts index 6d0b6ba0f63..c21be75a769 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-settlement.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.ts @@ -1,10 +1,10 @@ -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../../shared/workspace-cleanup/workspace-cleanup' import type { WorkspaceCleanupFailure, WorkspaceCleanupRemoveResult } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' import { translate } from '@/i18n/i18n' -import { getWorkspaceCleanupCandidateHostId } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import { getWorkspaceCleanupCandidateHostId } from '../../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' export type WorkspaceCleanupRemovalSettlement = | { status: 'fulfilled'; result: WorkspaceCleanupRemoveResult } diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-timeout-recovery.test.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-timeout-recovery.test.ts similarity index 99% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-timeout-recovery.test.ts rename to src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-timeout-recovery.test.ts index 4814a0cc9ea..7ef7ddb0974 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-timeout-recovery.test.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-timeout-recovery.test.ts @@ -4,7 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { toast } from 'sonner' import type { WorkspaceCleanupRemoveResult } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' import { startWorkspaceCleanupBackgroundRemoval } from './workspace-cleanup-background-removal' -import { makeCandidate } from './workspace-cleanup-presentation-fixtures' +import { makeCandidate } from '../../query/workspace-cleanup-candidate-state.test-fixture' vi.mock('sonner', () => ({ toast: { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-toasts.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-toasts.ts similarity index 92% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-toasts.ts rename to src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-toasts.ts index d14959c3b22..80c034b39ad 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-toasts.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-toasts.ts @@ -4,7 +4,7 @@ import type { WorkspaceCleanupRemoveResult } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' import { translate } from '@/i18n/i18n' -import { showPreservedBranchBatchToast } from '../sidebar/workspace-actions/deletion/preserved-branch-batch-toast' +import { showPreservedBranchBatchToast } from '../../../sidebar/workspace-actions/deletion/preserved-branch-batch-toast' export function showWorkspaceCleanupRemovalResultToasts( result: WorkspaceCleanupRemoveResult, diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-skipped-ancestor-reclassification.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-skipped-ancestor-reclassification.ts similarity index 95% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-skipped-ancestor-reclassification.ts rename to src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-skipped-ancestor-reclassification.ts index b562ab5ba62..aeccb981752 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-skipped-ancestor-reclassification.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-skipped-ancestor-reclassification.ts @@ -1,4 +1,4 @@ -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../../shared/workspace-cleanup/workspace-cleanup' import type { WorkspaceCleanupFailure } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' import { getSkippedAncestorMessage, diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-removal.agent-stop.test.tsx b/src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-removal.agent-stop.test.tsx similarity index 92% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-removal.agent-stop.test.tsx rename to src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-removal.agent-stop.test.tsx index 50603970058..3af8765d9a3 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-removal.agent-stop.test.tsx +++ b/src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-removal.agent-stop.test.tsx @@ -2,9 +2,9 @@ import { act, renderHook } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { AgentStatusEntry } from '../../../../shared/agent-status/contracts/agent-status-types' -import { makePaneKey } from '../../../../shared/workspace-layout/stable-pane-id' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { AgentStatusEntry } from '../../../../../shared/agent-status/contracts/agent-status-types' +import { makePaneKey } from '../../../../../shared/workspace-layout/stable-pane-id' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import type { WorkspaceCleanupRemoveOptions, WorkspaceCleanupRemoveResult @@ -16,9 +16,9 @@ vi.mock('sonner', () => ({ import { useAppStore } from '@/store' import { makeTab } from '@/store/terminals/terminal-tab.test-fixture' -import { makeCandidate } from './workspace-cleanup-presentation-fixtures' +import { makeCandidate } from '../query/workspace-cleanup-candidate-state.test-fixture' import { useWorkspaceCleanupRemoval } from './use-workspace-cleanup-removal' -import { useWorkspaceCleanupDialogLifecycle } from './use-workspace-cleanup-dialog-lifecycle' +import { useWorkspaceCleanupDialogLifecycle } from '../dialog/use-workspace-cleanup-dialog-lifecycle' const initialState = useAppStore.getInitialState() const LEAF_ID = '11111111-1111-4111-8111-111111111111' diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-removal.ts b/src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-removal.ts similarity index 97% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-removal.ts rename to src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-removal.ts index 20b34f2dbe5..86982ab67f3 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-removal.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-removal.ts @@ -1,25 +1,25 @@ import { useCallback, useRef, useState } from 'react' import { useAppStore } from '@/store' import { useMountedRef } from '@/hooks/useMountedRef' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import { getWorkspaceCleanupCandidateIdentity, getWorkspaceCleanupHostIdentity, resolveWorkspaceCleanupRemovalHostId -} from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import { getWorktreeDeleteStateKey } from '@/store/slices/worktrees/teardown/worktree-delete-state' import type { WorkspaceCleanupFailure } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' import { startWorkspaceCleanupBackgroundRemoval, type WorkspaceCleanupRemovalProgress -} from './workspace-cleanup-background-removal' +} from './settlement/workspace-cleanup-background-removal' import { filterWorkspaceCleanupRemovalCandidates } from './workspace-cleanup-removal-candidates' import { createWorkspaceCleanupSnapshotPruneBatch } from './workspace-cleanup-snapshot-prune-batch' import { useWorkspaceCleanupUnverifiedRemoval } from './use-workspace-cleanup-unverified-removal' import { useWorkspaceCleanupAgentStopGate, type WorkspaceCleanupAgentStopRequest -} from './use-workspace-cleanup-agent-stop-gate' +} from './agent-consent/use-workspace-cleanup-agent-stop-gate' export type WorkspaceCleanupRemovalController = { confirming: boolean diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-unverified-removal.ts b/src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-unverified-removal.ts similarity index 93% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-unverified-removal.ts rename to src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-unverified-removal.ts index d586c43157c..1505c1d2691 100644 --- a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-unverified-removal.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/use-workspace-cleanup-unverified-removal.ts @@ -1,18 +1,18 @@ import { useCallback, type Dispatch, type SetStateAction } from 'react' import { useMountedRef } from '@/hooks/useMountedRef' import { useAppStore } from '@/store' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import { getWorkspaceCleanupCandidateIdentity, getWorkspaceCleanupHostIdentity, resolveWorkspaceCleanupRemovalHostId -} from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import type { WorkspaceCleanupFailure, WorkspaceCleanupRemoveResult } from '@/store/slices/workspace-cleanup/workspace-cleanup-removal' -import { startWorkspaceCleanupBackgroundRemoval } from './workspace-cleanup-background-removal' -import type { WorkspaceCleanupDeletionPhase } from './workspace-cleanup-candidate-row' +import { startWorkspaceCleanupBackgroundRemoval } from './settlement/workspace-cleanup-background-removal' +import type { WorkspaceCleanupDeletionPhase } from '../candidate-list/workspace-cleanup-candidate-row' import { createWorkspaceCleanupSnapshotPruneBatch } from './workspace-cleanup-snapshot-prune-batch' type UnverifiedRemovalArgs = { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-confirm-remove.tsx b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-confirm-remove.tsx similarity index 91% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-confirm-remove.tsx rename to src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-confirm-remove.tsx index 2e87a8408d7..9e5d5bfd997 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-confirm-remove.tsx +++ b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-confirm-remove.tsx @@ -10,31 +10,31 @@ import { useTranslate, type Translate } from '@/i18n/use-translate' import { needsWorkspaceCleanupRemovalApproval, type WorkspaceCleanupCandidate -} from '../../../../shared/workspace-cleanup/workspace-cleanup' -import type { WorkspaceCleanupRemovalProgress } from './workspace-cleanup-background-removal' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupRemovalProgress } from './settlement/workspace-cleanup-background-removal' import { getCandidateFactStatuses, formatContextDetailLabels, getDirtyGitLabel, shouldShowGitMetadataChip -} from './workspace-cleanup-candidate-row-data' -import type { WorkspaceCleanupReviewInfo } from './workspace-cleanup-presentation' -import { formatWorkspaceCleanupRelativeTime } from './workspace-cleanup-relative-time' +} from '../candidate-list/workspace-cleanup-candidate-row-data' +import type { WorkspaceCleanupReviewInfo } from '../candidate-evidence/workspace-cleanup-review-lookup' +import { formatWorkspaceCleanupRelativeTime } from '../candidate-list/workspace-cleanup-relative-time' import { getReviewStateTone } from '@/components/github/review-state-presentation' -import { getReviewStateSrText } from './workspace-cleanup-row-labels' -import { StatusPill } from './workspace-cleanup-status-pill' -import { WorkspaceCleanupCandidateList } from './workspace-cleanup-candidate-list' -import './workspace-cleanup-host-identity' +import { getReviewStateSrText } from '../candidate-list/workspace-cleanup-review-labels' +import { StatusPill } from '../candidate-list/workspace-cleanup-status-pill' +import { WorkspaceCleanupCandidateList } from '../candidate-list/workspace-cleanup-candidate-list' +import '../candidate-evidence/workspace-cleanup-host-indexes' import { getWorkspaceCleanupCandidateHostId, getWorkspaceCleanupCandidateIdentity, getWorkspaceCleanupHostIdentity -} from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import { getWorkspaceCleanupCandidateAccessibleName, getWorkspaceCleanupCandidateHostLabel -} from './workspace-cleanup-host-label' -import { WorkspaceCleanupMetadataText } from './workspace-cleanup-metadata-chip' +} from '../candidate-list/workspace-cleanup-host-label' +import { WorkspaceCleanupMetadataText } from '../candidate-list/workspace-cleanup-metadata-chip' const CONFIRM_REMOVE_ROW_ESTIMATE_PX = 76 diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-deletion-phases.ts b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-deletion-phases.ts similarity index 84% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-deletion-phases.ts rename to src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-deletion-phases.ts index 5dd8ba2adb1..fe8b958c619 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-deletion-phases.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-deletion-phases.ts @@ -1,12 +1,12 @@ -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import type { WorktreeDeleteState } from '@/store/slices/worktrees/teardown/worktree-delete-state-types' import { getWorkspaceCleanupCandidateIdentity, getWorkspaceCleanupIdentityWorktreeId, resolveWorkspaceCleanupRemovalHostId -} from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' -import { composeWorktreeHostIdentity } from '../../../../shared/worktree/host-qualified-identity' -import type { WorkspaceCleanupDeletionPhase } from './workspace-cleanup-candidate-row' +} from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import { composeWorktreeHostIdentity } from '../../../../../shared/worktree/host-qualified-identity' +import type { WorkspaceCleanupDeletionPhase } from '../candidate-list/workspace-cleanup-candidate-row' export function getWorkspaceCleanupDeletionPhaseByIdentity( candidates: readonly WorkspaceCleanupCandidate[], diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-forget-locally-button.tsx b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-forget-locally-button.tsx similarity index 88% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-forget-locally-button.tsx rename to src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-forget-locally-button.tsx index 5e1c14fac61..ecffe668775 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-forget-locally-button.tsx +++ b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-forget-locally-button.tsx @@ -2,8 +2,8 @@ import { Trash2 } from 'lucide-react' import { Button } from '@/components/ui/button' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { useTranslate } from '@/i18n/use-translate' -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' -import { resolveWorkspaceCleanupRemovalHostId } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' +import { resolveWorkspaceCleanupRemovalHostId } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' import { runWorktreeDelete } from '@/components/sidebar/workspace-actions/deletion/delete-worktree-flow' export function openWorkspaceCleanupForgetLocally(candidate: WorkspaceCleanupCandidate): void { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-candidates.test.ts b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-removal-candidates.test.ts similarity index 82% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-candidates.test.ts rename to src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-removal-candidates.test.ts index 2731521393d..b086d32fdc3 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-candidates.test.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-removal-candidates.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest' -import { makeCandidate } from './workspace-cleanup-presentation-fixtures' +import { makeCandidate } from '../query/workspace-cleanup-candidate-state.test-fixture' import { filterWorkspaceCleanupRemovalCandidates } from './workspace-cleanup-removal-candidates' -import { composeWorktreeHostIdentity } from '../../../../shared/worktree/host-qualified-identity' +import { composeWorktreeHostIdentity } from '../../../../../shared/worktree/host-qualified-identity' describe('workspace cleanup removal candidates', () => { it('keeps a same-id sibling on another host removable', () => { diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-candidates.ts b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-removal-candidates.ts similarity index 80% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-candidates.ts rename to src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-removal-candidates.ts index 2d40590cffe..a1c81320df4 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-candidates.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-removal-candidates.ts @@ -1,7 +1,7 @@ -import type { WorkspaceCleanupCandidate } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupCandidate } from '../../../../../shared/workspace-cleanup/workspace-cleanup' import type { WorktreeDeleteState } from '@/store/slices/worktrees/teardown/worktree-delete-state-types' -import { composeWorktreeHostIdentity } from '../../../../shared/worktree/host-qualified-identity' -import { resolveWorkspaceCleanupRemovalHostId } from '../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' +import { composeWorktreeHostIdentity } from '../../../../../shared/worktree/host-qualified-identity' +import { resolveWorkspaceCleanupRemovalHostId } from '../../../../../shared/workspace-cleanup/workspace-cleanup-host-identity' type DeletionFlagState = Pick diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-snapshot-prune-batch.ts b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-snapshot-prune-batch.ts similarity index 81% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-snapshot-prune-batch.ts rename to src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-snapshot-prune-batch.ts index dc3fcb2bf30..981567c4886 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-snapshot-prune-batch.ts +++ b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-snapshot-prune-batch.ts @@ -1,5 +1,5 @@ -import type { WorkspaceCleanupBackgroundRemovalArgs } from './workspace-cleanup-background-removal' -import { createNonSecureContextUuid as createBrowserUuid } from '../../../../shared/non-secure-context-uuid' +import type { WorkspaceCleanupBackgroundRemovalArgs } from './settlement/workspace-cleanup-background-removal' +import { createNonSecureContextUuid as createBrowserUuid } from '../../../../../shared/non-secure-context-uuid' export function createWorkspaceCleanupSnapshotPruneBatch(): | WorkspaceCleanupBackgroundRemovalArgs['snapshotPruneBatch'] diff --git a/src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-scan-lifecycle.ts b/src/renderer/src/components/workspace-cleanup/scanning/use-workspace-cleanup-scan-lifecycle.ts similarity index 100% rename from src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-scan-lifecycle.ts rename to src/renderer/src/components/workspace-cleanup/scanning/use-workspace-cleanup-scan-lifecycle.ts diff --git a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-scan-notice.ts b/src/renderer/src/components/workspace-cleanup/scanning/workspace-cleanup-scan-notice.ts similarity index 96% rename from src/renderer/src/components/workspace-cleanup/workspace-cleanup-scan-notice.ts rename to src/renderer/src/components/workspace-cleanup/scanning/workspace-cleanup-scan-notice.ts index ae39ae0ed12..3fa5e354afd 100644 --- a/src/renderer/src/components/workspace-cleanup/workspace-cleanup-scan-notice.ts +++ b/src/renderer/src/components/workspace-cleanup/scanning/workspace-cleanup-scan-notice.ts @@ -1,6 +1,6 @@ import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import type { WorkspaceCleanupScanError } from '../../../../shared/workspace-cleanup/workspace-cleanup' +import type { WorkspaceCleanupScanError } from '../../../../../shared/workspace-cleanup/workspace-cleanup' function isDisconnectedRemoteScanError(message: string): boolean { return ( From 27ef651eefa282fc6a6f71691e0bf6e022bb2544 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:56:18 -0800 Subject: [PATCH 045/178] fix(dictation): stop only the skipped or timed-out cleanup's agent (STA-15095) (#41817) * fix(dictation): give each cleanup its own cancel lane (STA-15095) * test(dictation): one explicit case per overlapping-cleanup behavior * test(dictation): follow main's text-generation/execution moves (STA-15095) Main (#41838) moved the lanes, agent-launch, agent-environment and test harness modules under text-generation/execution/ and renamed the commit-message settings export to resolveCommitMessageSettings. Point the new dictation cleanup test's imports and mocks at the moved modules. --- .../opencode/text-tasks/opencode-text-task.ts | 2 +- .../speech/dictation-cleanup-service.test.ts | 146 ++++++++++++++++++ src/main/speech/dictation-cleanup-service.ts | 7 +- .../source-control-generation-lanes.ts | 18 ++- .../source-control-local-generation.ts | 5 +- .../execution/source-control-local-process.ts | 4 +- .../source-control-text-generation-types.ts | 3 +- 7 files changed, 173 insertions(+), 12 deletions(-) create mode 100644 src/main/speech/dictation-cleanup-service.test.ts diff --git a/src/main/opencode/text-tasks/opencode-text-task.ts b/src/main/opencode/text-tasks/opencode-text-task.ts index c7f970e4114..2deb65e70be 100644 --- a/src/main/opencode/text-tasks/opencode-text-task.ts +++ b/src/main/opencode/text-tasks/opencode-text-task.ts @@ -40,7 +40,7 @@ export async function runOpenCodeTextTask(input: { const deadline = AbortSignal.timeout(SOURCE_CONTROL_GENERATION_TIMEOUT_MS) const signal = AbortSignal.any([canceled.signal, deadline]) const cancel = () => canceled.abort() - const lane = localGenerationLaneKey(input.operation, input.target.cwd) + const lane = localGenerationLaneKey(input.operation, input.target.cwd, input.target.laneId) setLocalGenerationCancelToken(lane, cancel) try { if (input.shouldExecute?.() === false) { diff --git a/src/main/speech/dictation-cleanup-service.test.ts b/src/main/speech/dictation-cleanup-service.test.ts new file mode 100644 index 00000000000..af9b728e7eb --- /dev/null +++ b/src/main/speech/dictation-cleanup-service.test.ts @@ -0,0 +1,146 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { DICTATION_CLEANUP_TIMEOUT_MS } from '../../shared/dictation-cleanup' +import { + createMockDiscoveryChild, + type MockDiscoveryChild +} from '../text-generation/execution/commit-message-text-generation-test-harness' +import type * as DictationCleanup from '../../shared/dictation-cleanup' +import type * as GenerationLanes from '../text-generation/execution/source-control-generation-lanes' +import { cancelDictationCleanup, cleanUpDictatedText } from './dictation-cleanup-service' + +const children: MockDiscoveryChild[] = [] +const cancelRegistrations: string[] = [] +let agentId = 'claude' + +vi.mock('../../shared/dictation-cleanup', async (importOriginal) => ({ + ...(await importOriginal()), + isDictationCleanupOn: () => true +})) +vi.mock('../text-generation/commit-message-text-generation', () => ({ + resolveCommitMessageSettings: () => ({ ok: true, params: { agentId, model: 'default' } }) +})) +vi.mock('../text-generation/execution/commit-message-agent-environment', () => ({ + prepareLocalCommitMessageAgentEnv: async () => ({ ok: true }) +})) +vi.mock('../text-generation/execution/source-control-generation-lanes', async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + setLocalGenerationCancelToken: (lane: string, cancel: () => void) => { + cancelRegistrations.push(lane) + actual.setLocalGenerationCancelToken(lane, cancel) + } + } +}) +vi.mock('../text-generation/execution/source-control-agent-launch', () => ({ + spawnSourceControlAgent: ({ args }: { args: string[] }) => { + const child = createMockDiscoveryChild() + if (args.includes('mcp')) { + // Codex's read-only policy probe: no MCP servers configured. + queueMicrotask(() => { + child.stdout.emit('data', Buffer.from('[]')) + child.emit('close', 0) + }) + } else { + // A killed agent exits, as SIGKILL makes a real one do. + child.kill = vi.fn(() => { + queueMicrotask(() => child.emit('close', null, 'SIGKILL')) + return true + }) + children.push(child) + } + return child + } +})) + +const deps = { getSettings: () => ({}), getAgentEnvResolvers: () => undefined } + +afterEach(() => { + children.length = 0 + cancelRegistrations.length = 0 + agentId = 'claude' + vi.useRealTimers() +}) + +function startCleanup(requestId: string): ReturnType { + return cleanUpDictatedText({ text: requestId, terms: [], requestId }, deps) +} + +/** Starts a cleanup and returns once it can be canceled: its agent running, or queued for one. */ +async function startCancelableCleanup( + requestId: string +): Promise<{ cleanup: ReturnType }> { + const registeredBefore = cancelRegistrations.length + const cleanup = startCleanup(requestId) + await vi.waitFor(() => expect(cancelRegistrations.length).toBeGreaterThan(registeredBefore)) + return { cleanup } +} + +async function expectSecondAgentToFinish( + second: ReturnType +): Promise { + await vi.waitFor(() => expect(children).toHaveLength(2)) + const [firstAgent, secondAgent] = children + expect(firstAgent.kill).toHaveBeenCalled() + expect(secondAgent.kill).not.toHaveBeenCalled() + secondAgent.stdout.emit('data', Buffer.from('Second.')) + secondAgent.emit('close', 0) + await expect(second).resolves.toMatchObject({ status: 'cleaned', text: 'Second.' }) +} + +describe('overlapping dictation cleanups share a folder but stop independently', () => { + it('skipping the first stops only its agent while the second keeps running', async () => { + const first = startCleanup('a') + await vi.waitFor(() => expect(children).toHaveLength(1)) + const { cleanup: second } = await startCancelableCleanup('b') + expect(children).toHaveLength(2) + + cancelDictationCleanup('a') + + await expect(first).resolves.toEqual({ status: 'canceled' }) + await expectSecondAgentToFinish(second) + }) + + it('the first timing out stops only its agent while the second keeps running', async () => { + vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] }) + const first = startCleanup('a') + await vi.waitFor(() => expect(children).toHaveLength(1)) + await vi.advanceTimersByTimeAsync(DICTATION_CLEANUP_TIMEOUT_MS / 2) + const { cleanup: second } = await startCancelableCleanup('b') + expect(children).toHaveLength(2) + + await vi.advanceTimersByTimeAsync(DICTATION_CLEANUP_TIMEOUT_MS / 2) + + await expect(first).resolves.toEqual({ status: 'timed-out' }) + await expectSecondAgentToFinish(second) + }) + + // Codex runs one at a time under its home lock, so the second cleanup waits queued. + it('skipping a running Codex cleanup lets the queued one start and finish', async () => { + agentId = 'codex' + const first = startCleanup('a') + await vi.waitFor(() => expect(children).toHaveLength(1)) + const { cleanup: second } = await startCancelableCleanup('b') + expect(children).toHaveLength(1) + + cancelDictationCleanup('a') + + await expect(first).resolves.toEqual({ status: 'canceled' }) + await expectSecondAgentToFinish(second) + }) + + it('a running Codex cleanup timing out lets the queued one start and finish', async () => { + agentId = 'codex' + vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] }) + const first = startCleanup('a') + await vi.waitFor(() => expect(children).toHaveLength(1)) + await vi.advanceTimersByTimeAsync(DICTATION_CLEANUP_TIMEOUT_MS / 2) + const { cleanup: second } = await startCancelableCleanup('b') + expect(children).toHaveLength(1) + + await vi.advanceTimersByTimeAsync(DICTATION_CLEANUP_TIMEOUT_MS / 2) + + await expect(first).resolves.toEqual({ status: 'timed-out' }) + await expectSecondAgentToFinish(second) + }) +}) diff --git a/src/main/speech/dictation-cleanup-service.ts b/src/main/speech/dictation-cleanup-service.ts index 3d7a6c5325e..a053995aade 100644 --- a/src/main/speech/dictation-cleanup-service.ts +++ b/src/main/speech/dictation-cleanup-service.ts @@ -1,3 +1,4 @@ +import { randomUUID } from 'node:crypto' import { mkdir } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' @@ -48,6 +49,8 @@ export async function cleanUpDictatedText( } let stopped = false const cwd = cleanupFolder() + // Cleanups share the folder, so each gets its own cancel lane; a skip or timeout stops only its own agent. + const laneId = randomUUID() try { const resolved = resolveTextGenerationParams(settings, undefined, 'dictationCleanup') if (!resolved.ok) { @@ -70,7 +73,7 @@ export async function cleanUpDictatedText( voice: settings.voice, terms: input.terms, params, - target: { kind: 'local', cwd, ...(prepared.env ? { env: prepared.env } : {}) } + target: { kind: 'local', cwd, laneId, ...(prepared.env ? { env: prepared.env } : {}) } }) const outcome = await Promise.race([generation, stop.promise]) if (typeof outcome === 'string') { @@ -99,7 +102,7 @@ export async function cleanUpDictatedText( } // Stops the agent still running after a timeout or a skip. if (stopped) { - cancelLocalGeneration('dictation-cleanup', cwd) + cancelLocalGeneration('dictation-cleanup', cwd, laneId) } } } diff --git a/src/main/text-generation/execution/source-control-generation-lanes.ts b/src/main/text-generation/execution/source-control-generation-lanes.ts index 5dda38b67e6..60adb2f35de 100644 --- a/src/main/text-generation/execution/source-control-generation-lanes.ts +++ b/src/main/text-generation/execution/source-control-generation-lanes.ts @@ -2,12 +2,22 @@ import type { TextGenerationOperation } from '../source-control-text-generation- const cancelTokensByLane = new Map void>() -export function localGenerationLaneKey(operation: TextGenerationOperation, cwd: string): string { - return `${operation}:local:${cwd}` +/** `laneId` separates runs that share an operation and cwd, so canceling one never reaches another. */ +export function localGenerationLaneKey( + operation: TextGenerationOperation, + cwd: string, + laneId?: string +): string { + const key = `${operation}:local:${cwd}` + return laneId ? `${key}:${laneId}` : key } -export function cancelLocalGeneration(operation: TextGenerationOperation, cwd: string): void { - cancelTokensByLane.get(localGenerationLaneKey(operation, cwd))?.() +export function cancelLocalGeneration( + operation: TextGenerationOperation, + cwd: string, + laneId?: string +): void { + cancelTokensByLane.get(localGenerationLaneKey(operation, cwd, laneId))?.() } export function setLocalGenerationCancelToken(laneKey: string, cancel: () => void): void { diff --git a/src/main/text-generation/execution/source-control-local-generation.ts b/src/main/text-generation/execution/source-control-local-generation.ts index dcbd88392b0..19d517c604f 100644 --- a/src/main/text-generation/execution/source-control-local-generation.ts +++ b/src/main/text-generation/execution/source-control-local-generation.ts @@ -50,6 +50,7 @@ export function runLocalPlanForAgent(input: { cwd: input.target.cwd, env: wslDistro ? input.target.env : target.env, operation: input.operation, + laneId: input.target.laneId, wslDistro, spawnAgent: input.spawnAgent, timeoutMs @@ -60,7 +61,7 @@ export function runLocalPlanForAgent(input: { const deadlineAt = Date.now() + SOURCE_CONTROL_GENERATION_TIMEOUT_MS const deadline = AbortSignal.timeout(SOURCE_CONTROL_GENERATION_TIMEOUT_MS) const signal = AbortSignal.any([canceled.signal, deadline]) - const lane = localGenerationLaneKey(input.operation, input.target.cwd) + const lane = localGenerationLaneKey(input.operation, input.target.cwd, input.target.laneId) const failure = (): InternalTextGenerationResult => canceled.signal.aborted || input.shouldExecute?.() === false ? { success: false, error: 'Generation canceled.', canceled: true } @@ -149,7 +150,7 @@ function runCodexLocalPlanUnderHomeLock( commandEnv?: Record, awaitSettlement = false ): Promise { - const laneKey = localGenerationLaneKey(operation, target.cwd) + const laneKey = localGenerationLaneKey(operation, target.cwd, target.laneId) let canceledWhileQueued = false let publishResult!: (result: InternalTextGenerationResult) => void let rejectResult!: (error: unknown) => void diff --git a/src/main/text-generation/execution/source-control-local-process.ts b/src/main/text-generation/execution/source-control-local-process.ts index 15149b1ef4b..c2f9b18ff8c 100644 --- a/src/main/text-generation/execution/source-control-local-process.ts +++ b/src/main/text-generation/execution/source-control-local-process.ts @@ -32,7 +32,7 @@ export function runLocalSourceControlPlan( input: LocalSourceControlPlanInput ): LocalProcessExecution { let { plan } = input - const { cwd, operation } = input + const { cwd, operation, laneId } = input const distro = plan.promptFile || plan.taskFiles?.length ? wslDistroForCommand(cwd, input.wslDistro) : null const guest = distro ? prepareWslPrompt(plan, distro) : undefined @@ -108,7 +108,7 @@ export function runLocalSourceControlPlan( let outputLimitExceeded = false let settled = false let canceledByUser = false - const laneKey = localGenerationLaneKey(operation, cwd) + const laneKey = localGenerationLaneKey(operation, cwd, laneId) let timer: ReturnType | null = null let terminal = false let terminationComplete: Promise | null = null diff --git a/src/main/text-generation/source-control-text-generation-types.ts b/src/main/text-generation/source-control-text-generation-types.ts index 51ca97538e1..3d21553f4b8 100644 --- a/src/main/text-generation/source-control-text-generation-types.ts +++ b/src/main/text-generation/source-control-text-generation-types.ts @@ -53,7 +53,7 @@ export type TextGenerationOperation = | 'dictation-cleanup' export type CommitMessageGenerationTarget = - | { kind: 'local'; cwd: string; env?: NodeJS.ProcessEnv; wslDistro?: string } + | { kind: 'local'; cwd: string; env?: NodeJS.ProcessEnv; wslDistro?: string; laneId?: string } | { kind: 'remote' cwd: string @@ -127,6 +127,7 @@ export type LocalSourceControlPlanInput = { cwd: string env: NodeJS.ProcessEnv | undefined operation: TextGenerationOperation + laneId?: string wslDistro?: string spawnAgent: SpawnSourceControlAgent timeoutMs?: number From 6734880408c22db28807e6ec99494d8f2f909beb Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 00:08:35 -0800 Subject: [PATCH 046/178] test: remove synthetic portal loop and static WSL move suites (#41895) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 2 - ...rktree-scan-cache-annotation-reuse.test.ts | 19 -- ...-portal-publication-loop.react185.test.tsx | 58 ------ .../wsl-move/WslMoveDialog.test.tsx | 190 ------------------ 4 files changed, 269 deletions(-) delete mode 100644 src/renderer/src/components/activity/activity-terminal-portal-publication-loop.react185.test.tsx delete mode 100644 src/renderer/src/components/wsl-move/WslMoveDialog.test.tsx diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index ae79c365b7d..c48decce4df 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -4186,7 +4186,6 @@ "src/renderer/src/components/activity/activity-portal-readiness-subscription-churn.react185.test.tsx": 315, "src/renderer/src/components/activity/activity-scope-filter.test.ts": 734, "src/renderer/src/components/activity/activity-search-query.test.tsx": 2286, - "src/renderer/src/components/activity/activity-terminal-portal-publication-loop.react185.test.tsx": 222, "src/renderer/src/components/activity/activity-thread-list-pane.multi-select.test.tsx": 4699, "src/renderer/src/components/activity/activity-thread-list-pane.virtualization.test.tsx": 4863, "src/renderer/src/components/activity/useActivityUnreadCount.freshness.test.tsx": 1990, @@ -5410,7 +5409,6 @@ "src/renderer/src/components/worktree-jump/use-worktree-jump-palette-browser-ownership.test.ts": 2096, "src/renderer/src/components/worktree-jump/use-worktree-jump-palette-task-search.test.tsx": 2140, "src/renderer/src/components/worktree-jump/worktree-jump-palette-browser-navigation.test.tsx": 4470, - "src/renderer/src/components/wsl-move/WslMoveDialog.test.tsx": 524, "src/renderer/src/components/wsl-move/wsl-move-dialog-store.test.ts": 2011, "src/renderer/src/components/wsl-move/wsl-move-scope-rows.test.ts": 156, "src/renderer/src/hooks/agent-hook-completion-grok-notifications.test.ts": 679, diff --git a/src/main/git/worktrees/catalog/worktree-scan-cache-annotation-reuse.test.ts b/src/main/git/worktrees/catalog/worktree-scan-cache-annotation-reuse.test.ts index 44ebd1b6c0f..b3ba536a06c 100644 --- a/src/main/git/worktrees/catalog/worktree-scan-cache-annotation-reuse.test.ts +++ b/src/main/git/worktrees/catalog/worktree-scan-cache-annotation-reuse.test.ts @@ -50,25 +50,6 @@ describe('graph and annotated worktree scans', () => { __resetSparseCheckoutStateCacheForTests() }) - it('does not probe sparse state for a graph scan', async () => { - const rows = await listWorktreeGraph(REPO, { wslDistro: 'Ubuntu' }) - - expect(rows[0]?.path).toBe('C:\\wt\\x') - expect(rows[0]?.isSparse).toBeUndefined() - expect(detectSparseCheckoutMock).not.toHaveBeenCalled() - }) - - it('reads the git listing once for an overlapping graph and annotated scan', async () => { - const [graphRows, annotatedRows] = await Promise.all([ - listWorktreeGraph(REPO, { wslDistro: 'Ubuntu' }), - listWorktrees(REPO, { wslDistro: 'Ubuntu' }) - ]) - - expect(readTranslatedWorktreeGraphMock).toHaveBeenCalledTimes(1) - expect(graphRows[0]?.isSparse).toBeUndefined() - expect(annotatedRows[0]?.isSparse).toBe(true) - }) - it('resolves a graph scan while the annotated scan is still probing', async () => { let releaseProbe!: () => void detectSparseCheckoutMock.mockImplementation( diff --git a/src/renderer/src/components/activity/activity-terminal-portal-publication-loop.react185.test.tsx b/src/renderer/src/components/activity/activity-terminal-portal-publication-loop.react185.test.tsx deleted file mode 100644 index 130037efe4b..00000000000 --- a/src/renderer/src/components/activity/activity-terminal-portal-publication-loop.react185.test.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/** @vitest-environment happy-dom */ -import { act, useCallback, useLayoutEffect, useState } from 'react' -import { createRoot } from 'react-dom/client' -import { afterEach, describe, expect, it } from 'vitest' -import { - setActivityTerminalPortals, - useActivityTerminalPortals, - type ActivityTerminalPortalTarget -} from './activity-terminal-portal' - -;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true - -const target = document.createElement('div') -const descriptor: ActivityTerminalPortalTarget = { - slotId: 'primary', - requestToken: 'primary:tab-a:leaf-a', - target, - worktreeId: 'worktree-a', - tabId: 'tab-a', - paneKey: 'tab-a:leaf-a', - active: true -} - -afterEach(() => setActivityTerminalPortals([])) - -describe('Activity terminal portal publication loop', () => { - it('settles when Terminal feeds an identical descriptor back into Activity', () => { - let renders = 0 - - function ActivityPublisher(): null { - useLayoutEffect(() => setActivityTerminalPortals([{ ...descriptor }])) - return null - } - - function TerminalSubscriber({ invalidate }: { invalidate: () => void }): null { - const portals = useActivityTerminalPortals(true) - useLayoutEffect(invalidate, [invalidate, portals]) - return null - } - - function CrossSurfaceHost(): React.JSX.Element { - renders += 1 - const [, setRevision] = useState(0) - const invalidate = useCallback(() => setRevision((revision) => revision + 1), []) - return ( - <> - - - - ) - } - - const root = createRoot(document.createElement('div')) - expect(() => act(() => root.render())).not.toThrow() - expect(renders).toBeLessThan(10) - act(() => root.unmount()) - }) -}) diff --git a/src/renderer/src/components/wsl-move/WslMoveDialog.test.tsx b/src/renderer/src/components/wsl-move/WslMoveDialog.test.tsx deleted file mode 100644 index 1fff34b560e..00000000000 --- a/src/renderer/src/components/wsl-move/WslMoveDialog.test.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { renderToStaticMarkup } from 'react-dom/server' -import { describe, expect, it, vi } from 'vitest' -import type { WslMoveResult } from '../../../../shared/wsl-move/wsl-move-driver-types' - -import { WslMovePreviewBody, type WslMovePreviewBodyProps } from './WslMovePreviewBody' -import { WslMoveProgressBody, wslMoveCancellable } from './WslMoveProgressBody' -import { WslMoveResultBody, wslMoveResultTitle } from './WslMoveResultBody' - -const noop = (): void => undefined -const READY = { - outcome: 'ready' as const, - distro: 'Ubuntu', - repoIds: ['api'], - folderWorkspaceIds: ['notes'], - changed: [], - heldByHost: [], - refused: [], - blockers: [] -} - -function preview(overrides: Partial = {}): string { - return renderToStaticMarkup( - - ) -} - -function result(value: WslMoveResult, reload: 'idle' | 'checkpoint-refused' = 'idle'): string { - return renderToStaticMarkup( - - ) -} - -describe('preview', () => { - it('cannot start while checking, with nothing selected, or with blockers', () => { - expect(preview({ preview: null })).toContain('Checking the selected projects') - expect(preview({ preview: null })).toMatch(/disabled=""[^>]*>Move 1 project/) - expect(preview({ selected: [] })).toMatch(/disabled=""[^>]*>Move 0 projects/) - const blocked = preview({ - blockers: [ - { - blocker: { - kind: 'agent-working', - ids: ['t:1'], - actions: ['wait', 'end', 'skip-project'] - }, - projects: [{ kind: 'repo', id: 'api', name: 'api' }] - } - ] - }) - expect(blocked).toMatch(/disabled=""[^>]*>Move 1 project/) - expect(blocked).toContain('An agent is working or waiting for an answer') - expect(blocked).toContain('Leave api out of this move') - expect(blocked).toContain('Check again') - }) - - it('offers no skip where main does not allow it', () => { - const markup = preview({ - blockers: [ - { - blocker: { - kind: 'scrollback-too-large', - ids: ['t:1', 't:2'], - actions: ['clear-scrollback'] - }, - projects: [{ kind: 'repo', id: 'api', name: 'api' }] - } - ] - }) - expect(markup).toContain('2 terminals have too much output history to move') - expect(markup).not.toContain('out of this move') - }) - - it('lets a start settle an earlier unfinished move', () => { - const markup = preview({ preview: { outcome: 'refused', code: 'wsl_move_already_in_flight' } }) - expect(markup).toContain('A move of Ubuntu is still finishing.') - expect(markup).not.toMatch(/disabled=""[^>]*>Move 1 project/) - }) -}) - -describe('progress', () => { - it('disables cancel from the switch on, and says so', () => { - expect(wslMoveCancellable(null)).toBe(true) - expect(wslMoveCancellable({ distro: 'Ubuntu', step: 'copying' })).toBe(true) - expect(wslMoveCancellable({ distro: 'Ubuntu', step: 'switching' })).toBe(false) - expect(wslMoveCancellable({ distro: 'Ubuntu', step: 'finishing' })).toBe(false) - const markup = renderToStaticMarkup( - - ) - expect(markup).toMatch(/disabled=""[^>]*>Cancel move/) - expect(markup).toContain('Finishing') - expect(markup).toContain('cannot be cancelled') - }) -}) - -describe('result', () => { - it('asks for the reload, names automations still off, and points at the way back', () => { - const moved: WslMoveResult = { - outcome: 'moved', - migrationId: 'm1', - phase: 'retained', - reloadRequired: true, - automationsPending: ['a1', 'a2'], - browserLoginsNotCopiedReason: 'no-stable-partition' - } - const markup = result(moved) - expect(wslMoveResultTitle(moved, 'Ubuntu')).toBe('Moved to the Ubuntu host') - expect(markup).toContain('Reload now') - expect(markup).toContain('2 automations are off on the host for now') - expect(markup).toContain('could not identify the host’s browser profile') - expect(markup).not.toContain('no-stable-partition') - expect(markup).toContain('Use on this computer again') - expect(markup).not.toContain('Close') - }) - - it('says nothing was moved after a reset, and waits for the host when it may have committed', () => { - const reset = result({ outcome: 'reset', migrationId: 'm1', detail: 'stage failed' }) - expect(wslMoveResultTitle({ outcome: 'reset', migrationId: 'm', detail: '' }, 'U')).toBe( - 'Nothing was moved' - ) - expect(reset).toContain('everything was put back') - expect(reset).toContain('Back to projects') - const waiting = result({ outcome: 'waiting-for-host', migrationId: 'm1', detail: 'x' }) - expect(waiting).toContain('stay read-only here until it does') - expect(waiting).not.toContain('Reload now') - }) - - it('reloads, not closes, after an earlier move finished first', () => { - const markup = result({ outcome: 'refused', code: 'wsl_move_finished_earlier_move' }) - expect(markup).toContain('Reload now') - expect(markup).not.toContain('Back to projects') - }) -}) - -describe('entry points with the gate closed', () => { - it('render nothing', async () => { - vi.resetModules() - vi.doMock('../sidebar/use-wsl-hosts-enabled', () => ({ - useWslHostsEnabled: () => false, - useWslHostsAcceptNewWork: () => false, - useWslHostServerFilesMissing: () => false - })) - vi.doMock('@/store', () => ({ - useAppStore: (selector: (state: { runtimeEnvironments: unknown[] }) => unknown) => - selector({ - runtimeEnvironments: [ - { - id: 'wsl:ubuntu', - name: 'Ubuntu (WSL)', - orcadDeployment: { kind: 'wsl', distro: 'Ubuntu', localPort: 1, remotePort: 2 } - } - ] - }) - })) - const { WslMoveProjectMenuItem } = await import('./WslMoveProjectMenuItem') - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the item reads only path and the host fields. - const repo = { id: 'api', path: '\\\\wsl.localhost\\Ubuntu\\home\\dev\\api' } as never - expect(renderToStaticMarkup()).toBe('') - vi.doUnmock('../sidebar/use-wsl-hosts-enabled') - vi.doUnmock('@/store') - }) -}) From fd8c3da20a5754a5a868cf33a421b6d51807fc09 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 00:12:38 -0800 Subject: [PATCH 047/178] Simplify delete dialogs and make button states visible (#41870) * fix: simplify delete dialogs and clarify control interaction states * fix: preserve bulk delete context and update dialog e2e flows --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- docs/styleguide/components.md | 2 + mobile/src/automations/AutomationManager.tsx | 4 +- .../components/pr-sidebar/PRCommentCard.tsx | 2 +- .../src/components/pr-sidebar/PrThreadRow.tsx | 2 +- .../src/host-screen/host-screen-overlays.tsx | 6 +-- mobile/src/i18n/locales/de.json | 4 -- mobile/src/i18n/locales/en.json | 7 +-- mobile/src/i18n/locales/es.json | 4 -- mobile/src/i18n/locales/fr.json | 4 -- mobile/src/i18n/locales/ja.json | 4 -- mobile/src/i18n/locales/ko.json | 4 -- mobile/src/i18n/locales/pt.json | 7 +-- mobile/src/i18n/locales/ru.json | 4 -- mobile/src/i18n/locales/tr.json | 4 -- mobile/src/i18n/locales/zh-Hant.json | 4 -- mobile/src/i18n/locales/zh.json | 4 -- .../quick-commands/QuickCommandsSheet.tsx | 15 +++--- src/renderer/src/assets/main.css | 19 +++++++ .../components/artifacts/ArtifactsPage.tsx | 2 +- .../authoring/AutomationDeleteDialogs.tsx | 11 +--- .../ai-vault-session-delete-action.ts | 5 +- .../use-checks-panel-comment-mutations.tsx | 2 +- .../file-explorer-delete-classification.ts | 27 +++++----- .../mutations/file-explorer-trash-fallback.ts | 29 +++++----- .../useFileDeletion.trash-failure.test.tsx | 17 +----- .../mutations/useFileDeletion.ts | 40 ++++++++------ .../commit/discard-confirmation.ts | 17 ++---- .../passwords/BrowserPasswordsSection.tsx | 25 ++++----- .../quick-commands/QuickCommandsPane.tsx | 15 +++--- .../DeleteNestedWorktreesDialog.test.tsx | 10 ++-- .../deletion/DeleteNestedWorktreesDialog.tsx | 6 +-- .../deletion/DeleteWorktreeDialog.tsx | 10 +--- .../deletion/DeleteWorktreeDialogFooter.tsx | 20 +------ .../PreservedBranchBatchReviewDialog.tsx | 15 ++---- .../preserved-branch-batch-toast.test.tsx | 4 +- .../rows/ProjectGroupDeleteDialog.tsx | 9 +--- .../skills/deletion/use-skill-delete-flow.ts | 5 +- .../sharing/SkillSharedLinkRow.test.tsx | 2 +- .../skills/sharing/SkillSharedLinkRow.tsx | 2 +- .../tab-bar/TabBarQuickCommandsButton.tsx | 17 +++--- .../src/components/ui/button-group.tsx | 2 +- .../src/components/ui/button.fixture.tsx | 22 ++++++++ src/renderer/src/components/ui/button.tsx | 5 +- .../ui/control-interaction.fixture.tsx | 54 +++++++++++++++++++ src/renderer/src/components/ui/select.tsx | 2 +- src/renderer/src/components/ui/toggle.tsx | 2 +- .../workspace-cleanup-dialog-header.tsx | 7 +-- .../workspace-cleanup-confirm-stop-agents.tsx | 19 +++---- .../workspace-cleanup-confirm-remove.tsx | 20 +++---- .../src/i18n/en-runtime-required.json | 9 ---- src/renderer/src/i18n/locales/de.json | 41 +++----------- src/renderer/src/i18n/locales/en.json | 49 +++-------------- src/renderer/src/i18n/locales/es.json | 42 ++------------- src/renderer/src/i18n/locales/fr.json | 44 +++------------ src/renderer/src/i18n/locales/ja.json | 44 +++------------ src/renderer/src/i18n/locales/ko.json | 44 +++------------ src/renderer/src/i18n/locales/pt.json | 51 +++--------------- src/renderer/src/i18n/locales/ru.json | 47 +++------------- src/renderer/src/i18n/locales/tr.json | 41 +++----------- src/renderer/src/i18n/locales/zh-Hant.json | 41 +++----------- src/renderer/src/i18n/locales/zh.json | 44 +++------------ .../languages/protected-copy-keys.json | 23 +------- tests/e2e/modal-scene-blur.spec.ts | 12 +++-- ...ource-control-discard-confirmation.spec.ts | 12 ++--- tests/e2e/worktree-delete-shortcut.spec.ts | 2 +- .../worktree-lineage-toggle-dialog.spec.ts | 4 +- 66 files changed, 336 insertions(+), 737 deletions(-) create mode 100644 src/renderer/src/components/ui/control-interaction.fixture.tsx diff --git a/docs/styleguide/components.md b/docs/styleguide/components.md index 46dbd89f118..7cdde19d66a 100644 --- a/docs/styleguide/components.md +++ b/docs/styleguide/components.md @@ -26,6 +26,8 @@ Variants in priority order: Sizes are defined in `button.tsx` (`default`, `sm`, `xs`, `lg`, plus `icon`, `icon-xs`, `icon-sm`, `icon-lg`). Match the size to the surrounding row height — don't drop a `default` button into a 28px toolbar. +Buttons, toggles and select triggers use `control-interaction`: a thin outline on hover, a solid `ring` outline on focus (including the initially focused dialog action), and a closer outline while pressed. These cues do not change layout. Preserve them in every variant, including destructive and split buttons; use the same utility for custom button controls. + ## Picking the right primitive | You want… | Reach for | Don't use | diff --git a/mobile/src/automations/AutomationManager.tsx b/mobile/src/automations/AutomationManager.tsx index 78e7c87b6e7..f35c1ff8541 100644 --- a/mobile/src/automations/AutomationManager.tsx +++ b/mobile/src/automations/AutomationManager.tsx @@ -389,13 +389,13 @@ export function AutomationManager(props: { setDeleting(null)} onConfirm={() => deleting && remove(deleting)} diff --git a/mobile/src/components/pr-sidebar/PRCommentCard.tsx b/mobile/src/components/pr-sidebar/PRCommentCard.tsx index 8bf6e839b5c..f7837163433 100644 --- a/mobile/src/components/pr-sidebar/PRCommentCard.tsx +++ b/mobile/src/components/pr-sidebar/PRCommentCard.tsx @@ -308,7 +308,7 @@ export const PRCommentCard = memo(function PRCommentCard({ {actions ? ( - - {t('hostScreen.hostScreenOverlays.deleteWorktree', 'Delete Worktree')} - + {t('common.delete', 'Delete')} {t( 'hostScreen.hostScreenOverlays.deleteConfirmMessage', @@ -225,7 +223,7 @@ export function HostScreenOverlays({ controller }: { controller: HostScreenContr actions.archiveHookFailure?.item.displayName || actions.archiveHookFailure?.item.repo })} message={actions.archiveHookFailure?.message} - confirmLabel={t('hostScreen.hostScreenOverlays.deleteAnyway', 'Delete Anyway')} + confirmLabel={t('common.delete', 'Delete')} destructive onConfirm={actions.deleteDespiteArchiveHookFailure} onCancel={actions.dismissArchiveHookFailure} diff --git a/mobile/src/i18n/locales/de.json b/mobile/src/i18n/locales/de.json index 080ef99262d..28cd19206c7 100644 --- a/mobile/src/i18n/locales/de.json +++ b/mobile/src/i18n/locales/de.json @@ -1593,7 +1593,6 @@ "deleteConfirmMessage": "„{{name}}“ ({{branch}}) löschen?", "deleteDestroysEnvironment": "Dadurch wird auch die Umgebung „{{environment}}“ gelöscht, die Orca dafür erstellt hat, samt nicht committeter Änderungen und laufender Terminals.", "deleteKeepsEnvironment": "Die Umgebung „{{environment}}“, die Orca dafür erstellt hat, läuft weiter: Ihr Rezept hat keinen Löschschritt.", - "deleteWorktree": "Worktree löschen", "pin": "Anheften", "primaryWorktreeDeleteHint": "Primärer Worktree — kann nicht gelöscht werden. Stattdessen das Projekt entfernen.", "removeHost": "Host entfernen", @@ -1765,7 +1764,6 @@ "daysAgo_one": "vor {{count}} Tag", "daysAgo_other": "vor {{count}} Tagen", "deleteConfirmMessage": "Dadurch wird der Kommentar auf GitHub dauerhaft gelöscht.", - "deleteConfirmTitle": "Kommentar löschen?", "editPlaceholder": "Kommentar bearbeiten…", "hoursAgo_one": "vor {{count}} Std.", "hoursAgo_other": "vor {{count}} Std.", @@ -2386,7 +2384,6 @@ "addTitle": "Schnellbefehl hinzufügen", "chooseAgentTitle": "Agenten auswählen", "deleteConfirmMessage": "Dieser Schnellbefehl wird aus der Liste gespeicherter Befehle entfernt.", - "deleteConfirmTitle": "„{{label}}“ löschen?", "editTitle": "Schnellbefehl bearbeiten", "editorDescription": "Terminal-Befehle oder Agenten-Prompts für den Schnellzugriff speichern.", "title": "Schnellbefehle", @@ -4109,7 +4106,6 @@ "outcome": "Ergebnis", "allOutcomes": "Alle Ergebnisse", "couldNotStart": "Start nicht möglich", - "deleteAutomationTitle": "Automatisierung löschen?", "deleteRetained": "„{{name}}“ und die gespeicherten Ausführungen löschen?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "Das ursprüngliche Agentenfenster konnte nicht überprüft werden. Verbinde dich erneut und versuche es noch einmal.", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "Das ursprüngliche Agentenfenster kann nicht mehr geöffnet werden. Gespeicherte Ausgaben bleiben hier.", diff --git a/mobile/src/i18n/locales/en.json b/mobile/src/i18n/locales/en.json index ee677e5aca6..fd4c8278ef6 100644 --- a/mobile/src/i18n/locales/en.json +++ b/mobile/src/i18n/locales/en.json @@ -703,7 +703,6 @@ "automations": "Automations" }, "hostScreenOverlays": { - "deleteWorktree": "Delete Worktree", "deleteConfirmMessage": "Delete \"{{name}}\" ({{branch}})?", "deleteDestroysEnvironment": "This also destroys the environment \"{{environment}}\" that Orca created for it, with any uncommitted work and running terminals on it.", "deleteKeepsEnvironment": "The environment \"{{environment}}\" that Orca created for it keeps running: its recipe has no destroy step.", @@ -714,8 +713,7 @@ "primaryWorktreeDeleteHint": "Primary worktree — can't be deleted. Remove the project instead.", "removeHost": "Remove Host", "removeHostMessage": "Remove \"{{name}}\"? You can re-pair later.", - "deleteFailed": "Failed to delete \"{{name}}\"", - "deleteAnyway": "Delete Anyway" + "deleteFailed": "Failed to delete \"{{name}}\"" }, "workspaceRenameSheet": { "title": "Rename Workspace", @@ -1565,7 +1563,6 @@ "unresolve": "Unresolve", "resolve": "Resolve", "replyPlaceholder": "Write a reply…", - "deleteConfirmTitle": "Delete comment?", "deleteConfirmMessage": "This permanently deletes the comment on GitHub." }, "commentsSection": { @@ -2235,7 +2232,6 @@ "newCommand": "New quick command" }, "quickCommandsSheet": { - "deleteConfirmTitle": "Delete \"{{label}}\"?", "untitled": "Untitled", "deleteConfirmMessage": "This quick command will be removed from your saved list.", "editTitle": "Edit Quick Command", @@ -4772,7 +4768,6 @@ "completed": "Completed", "couldNotStart": "Could not start", "skipped": "Skipped", - "deleteAutomationTitle": "Delete automation?", "deleteRetained": "Delete “{{name}}” and its retained runs?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "Could not verify the original agent pane. Reconnect and try again.", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "The original agent pane is no longer available to open. Saved output remains here.", diff --git a/mobile/src/i18n/locales/es.json b/mobile/src/i18n/locales/es.json index 9ae48aea8df..424eff6c507 100644 --- a/mobile/src/i18n/locales/es.json +++ b/mobile/src/i18n/locales/es.json @@ -1634,7 +1634,6 @@ "deleteConfirmMessage": "¿Eliminar \"{{name}}\" ({{branch}})?", "deleteDestroysEnvironment": "También se destruye el entorno «{{environment}}» que Orca creó para él, con los cambios sin confirmar y los terminales en ejecución que haya en él.", "deleteKeepsEnvironment": "El entorno «{{environment}}» que Orca creó para él sigue en ejecución: su receta no tiene un paso de destrucción.", - "deleteWorktree": "Eliminar worktree", "pin": "Fijar", "primaryWorktreeDeleteHint": "El worktree principal no se puede eliminar. Quita el proyecto en su lugar.", "removeHost": "Eliminar host", @@ -1807,7 +1806,6 @@ "daysAgo_one": "hace {{count}} día", "daysAgo_other": "hace {{count}} días", "deleteConfirmMessage": "Esto elimina permanentemente el comentario en GitHub.", - "deleteConfirmTitle": "¿Eliminar comentario?", "editPlaceholder": "Edita el comentario…", "hoursAgo_many": "hace {{count}} h", "hoursAgo_one": "hace {{count}} h", @@ -2456,7 +2454,6 @@ "addTitle": "Agregar comando rápido", "chooseAgentTitle": "Elegir agente", "deleteConfirmMessage": "Este comando rápido se eliminará de tu lista guardada.", - "deleteConfirmTitle": "¿Eliminar \"{{label}}\"?", "editTitle": "Editar comando rápido", "editorDescription": "Guarda comandos de terminal o prompts de agente para acceder a ellos rápidamente.", "title": "Comandos rápidos", @@ -4210,7 +4207,6 @@ "outcome": "Resultado", "allOutcomes": "Todos los resultados", "couldNotStart": "No se pudo iniciar", - "deleteAutomationTitle": "¿Eliminar automatización?", "deleteRetained": "¿Eliminar «{{name}}» y sus ejecuciones conservadas?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "No se pudo verificar el panel original del agente. Vuelve a conectarte e inténtalo de nuevo.", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "El panel original del agente ya no se puede abrir. La salida guardada permanece aquí.", diff --git a/mobile/src/i18n/locales/fr.json b/mobile/src/i18n/locales/fr.json index eea40512fb7..04177b25722 100644 --- a/mobile/src/i18n/locales/fr.json +++ b/mobile/src/i18n/locales/fr.json @@ -1634,7 +1634,6 @@ "deleteConfirmMessage": "Supprimer « {{name}} » ({{branch}}) ?", "deleteDestroysEnvironment": "Cela détruit aussi l’environnement « {{environment}} » qu’Orca a créé pour lui, avec les modifications non validées et les terminaux en cours qui s’y trouvent.", "deleteKeepsEnvironment": "L’environnement « {{environment}} » qu’Orca a créé pour lui continue de tourner : sa recette n’a pas d’étape de destruction.", - "deleteWorktree": "Supprimer le worktree", "pin": "Épingler", "primaryWorktreeDeleteHint": "Worktree principal — impossible à supprimer. Retirez plutôt le projet.", "removeHost": "Supprimer l'hôte", @@ -1807,7 +1806,6 @@ "daysAgo_one": "il y a {{count}} j", "daysAgo_other": "il y a {{count}} j", "deleteConfirmMessage": "Cette action supprime définitivement le commentaire sur GitHub.", - "deleteConfirmTitle": "Supprimer le commentaire ?", "editPlaceholder": "Modifier le commentaire…", "hoursAgo_many": "il y a {{count}} h", "hoursAgo_one": "il y a {{count}} h", @@ -2456,7 +2454,6 @@ "addTitle": "Ajouter une commande rapide", "chooseAgentTitle": "Choisir un agent", "deleteConfirmMessage": "Cette commande rapide sera retirée de votre liste enregistrée.", - "deleteConfirmTitle": "Supprimer « {{label}} » ?", "editTitle": "Modifier la commande rapide", "editorDescription": "Enregistrez des commandes de terminal ou des prompts d'agent pour y accéder rapidement.", "title": "Commandes rapides", @@ -4209,7 +4206,6 @@ "outcome": "Résultat", "allOutcomes": "Tous les résultats", "couldNotStart": "Démarrage impossible", - "deleteAutomationTitle": "Supprimer l’automatisation ?", "deleteRetained": "Supprimer « {{name}} » et ses exécutions conservées ?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "Impossible de vérifier le panneau d’origine de l’agent. Reconnectez-vous et réessayez.", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "Le panneau d’origine de l’agent ne peut plus être ouvert. La sortie enregistrée reste ici.", diff --git a/mobile/src/i18n/locales/ja.json b/mobile/src/i18n/locales/ja.json index 36f4c2aff3b..a808530c675 100644 --- a/mobile/src/i18n/locales/ja.json +++ b/mobile/src/i18n/locales/ja.json @@ -1589,7 +1589,6 @@ "deleteConfirmMessage": "「{{name}}」({{branch}})を削除しますか?", "deleteDestroysEnvironment": "Orca がこのワークツリー用に作成した環境「{{environment}}」も、コミットされていない変更や実行中のターミナルごと破棄されます。", "deleteKeepsEnvironment": "Orca がこのワークツリー用に作成した環境「{{environment}}」は動作し続けます。レシピに破棄ステップがありません。", - "deleteWorktree": "ワークツリーを削除", "pin": "ピン", "primaryWorktreeDeleteHint": "プライマリワークツリーは削除できません。代わりにプロジェクトを削除してください。", "removeHost": "ホストを削除", @@ -1761,7 +1760,6 @@ "daysAgo_one": "{{count}} 日前", "daysAgo_other": "{{count}} 日前", "deleteConfirmMessage": "GitHub 上のコメントを完全に削除します。", - "deleteConfirmTitle": "コメントを削除しますか?", "editPlaceholder": "コメントを編集…", "hoursAgo_one": "{{count}} 時間前", "hoursAgo_other": "{{count}} 時間前", @@ -2382,7 +2380,6 @@ "addTitle": "クイックコマンドを追加", "chooseAgentTitle": "Agent を選択", "deleteConfirmMessage": "このクイックコマンドは保存されたリストから削除されます。", - "deleteConfirmTitle": "「{{label}}」を削除しますか?", "editTitle": "クイックコマンドの編集", "editorDescription": "ターミナルコマンドや Agent プロンプトを保存して、すぐに使えるようにします。", "title": "クイックコマンド", @@ -4106,7 +4103,6 @@ "outcome": "結果", "allOutcomes": "すべての結果", "couldNotStart": "開始できませんでした", - "deleteAutomationTitle": "自動化を削除しますか?", "deleteRetained": "「{{name}}」と保持されている実行を削除しますか?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "元のAgentペインを確認できませんでした。再接続して再試行してください。", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "元のAgentペインは開けなくなりました。保存済みの出力はここに残ります。", diff --git a/mobile/src/i18n/locales/ko.json b/mobile/src/i18n/locales/ko.json index 3b478623298..0ab35071a9e 100644 --- a/mobile/src/i18n/locales/ko.json +++ b/mobile/src/i18n/locales/ko.json @@ -1589,7 +1589,6 @@ "deleteConfirmMessage": "\"{{name}}\" ({{branch}})을(를) 삭제하시겠습니까?", "deleteDestroysEnvironment": "Orca가 이 작업 트리용으로 만든 환경 \"{{environment}}\"도 커밋되지 않은 작업과 실행 중인 터미널과 함께 삭제됩니다.", "deleteKeepsEnvironment": "Orca가 이 작업 트리용으로 만든 환경 \"{{environment}}\"은(는) 계속 실행됩니다. 레시피에 삭제 단계가 없습니다.", - "deleteWorktree": "작업 트리 삭제", "pin": "고정", "primaryWorktreeDeleteHint": "기본 작업 트리는 삭제할 수 없습니다. 대신 프로젝트를 제거하세요.", "removeHost": "호스트 제거", @@ -1761,7 +1760,6 @@ "daysAgo_one": "{{count}}일 전", "daysAgo_other": "{{count}}일 전", "deleteConfirmMessage": "GitHub에서 댓글이 영구적으로 삭제됩니다.", - "deleteConfirmTitle": "댓글을 삭제하시겠습니까?", "editPlaceholder": "댓글 수정…", "hoursAgo_one": "{{count}}시간 전", "hoursAgo_other": "{{count}}시간 전", @@ -2382,7 +2380,6 @@ "addTitle": "빠른 명령 추가", "chooseAgentTitle": "에이전트 선택", "deleteConfirmMessage": "이 빠른 명령은 저장된 목록에서 제거됩니다.", - "deleteConfirmTitle": "\"{{label}}\"을(를) 삭제하시겠습니까?", "editTitle": "빠른 명령 편집", "editorDescription": "터미널 명령이나 에이전트 프롬프트를 저장하여 빠르게 사용하세요.", "title": "빠른 명령어", @@ -4106,7 +4103,6 @@ "outcome": "결과", "allOutcomes": "모든 결과", "couldNotStart": "시작하지 못함", - "deleteAutomationTitle": "자동화를 삭제할까요?", "deleteRetained": "“{{name}}” 및 보관된 실행을 삭제할까요?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "원래 에이전트 창을 확인하지 못했습니다. 다시 연결한 후 시도하세요.", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "원래 에이전트 창을 더 이상 열 수 없습니다. 저장된 출력은 여기에 유지됩니다.", diff --git a/mobile/src/i18n/locales/pt.json b/mobile/src/i18n/locales/pt.json index d7f11c1a401..b6b7ebfb990 100644 --- a/mobile/src/i18n/locales/pt.json +++ b/mobile/src/i18n/locales/pt.json @@ -1778,7 +1778,6 @@ "deleteConfirmMessage": "Excluir \"{{name}}\" ({{branch}})?", "deleteDestroysEnvironment": "Isto também destrói o ambiente \"{{environment}}\" que o Orca criou para ele, com as alterações não confirmadas e os terminais em execução nele.", "deleteKeepsEnvironment": "O ambiente \"{{environment}}\" que o Orca criou para ele continua em execução: a receita não tem um passo de destruição.", - "deleteWorktree": "Excluir Worktree", "pin": "Fixar", "primaryWorktreeDeleteHint": "Worktree primária — não pode ser excluída. Remova o projeto em vez disso.", "removeHost": "Remover host", @@ -1786,8 +1785,7 @@ "sleep": "Suspender", "unpin": "Desafixar", "rename": "Renomear", - "deleteFailed": "Falha ao excluir \"{{name}}\"", - "deleteAnyway": "Excluir Mesmo Assim" + "deleteFailed": "Falha ao excluir \"{{name}}\"" }, "workspaceDetailsSheet": { "checksFailing": "Verificações: com falha", @@ -1968,7 +1966,6 @@ "daysAgo_one": "há {{count}} d", "daysAgo_other": "há {{count}} d", "deleteConfirmMessage": "Isso exclui permanentemente o comentário no GitHub.", - "deleteConfirmTitle": "Excluir comentário?", "editPlaceholder": "Edite o comentário…", "hoursAgo_many": "há {{count}} h", "hoursAgo_one": "há {{count}} h", @@ -2828,7 +2825,6 @@ "addTitle": "Adicionar Comando Rápido", "chooseAgentTitle": "Escolher Agente", "deleteConfirmMessage": "Este comando rápido será removido da sua lista salva.", - "deleteConfirmTitle": "Excluir \"{{label}}\"?", "editTitle": "Editar Comando Rápido", "editorDescription": "Salve comandos de terminal ou prompts de agente para acesso rápido.", "title": "Comandos Rápidos", @@ -4796,7 +4792,6 @@ "outcome": "Resultado", "allOutcomes": "Todos os resultados", "couldNotStart": "Não foi possível iniciar", - "deleteAutomationTitle": "Excluir automação?", "deleteRetained": "Excluir “{{name}}” e suas execuções retidas?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "Não foi possível verificar o painel original do agente. Reconecte e tente novamente.", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "O painel original do agente não pode mais ser aberto. A saída salva permanece aqui.", diff --git a/mobile/src/i18n/locales/ru.json b/mobile/src/i18n/locales/ru.json index a48953f4f52..ffa4c7b6ea5 100644 --- a/mobile/src/i18n/locales/ru.json +++ b/mobile/src/i18n/locales/ru.json @@ -1675,7 +1675,6 @@ "deleteConfirmMessage": "Удалить «{{name}}» ({{branch}})?", "deleteDestroysEnvironment": "Также будет уничтожено окружение «{{environment}}», созданное для него Orca, вместе с незакоммиченными изменениями и запущенными терминалами.", "deleteKeepsEnvironment": "Окружение «{{environment}}», созданное для него Orca, продолжит работать: в его рецепте нет шага уничтожения.", - "deleteWorktree": "Удалить рабочее дерево", "pin": "Закрепить", "primaryWorktreeDeleteHint": "Основное рабочее дерево нельзя удалить. Вместо этого уберите проект.", "removeHost": "Удалить сервер", @@ -1849,7 +1848,6 @@ "daysAgo_one": "{{count}} дн. назад", "daysAgo_other": "{{count}} дн. назад", "deleteConfirmMessage": "Комментарий на GitHub будет удалён безвозвратно.", - "deleteConfirmTitle": "Удалить комментарий?", "editPlaceholder": "Измените комментарий…", "hoursAgo_few": "{{count}} ч назад", "hoursAgo_many": "{{count}} ч назад", @@ -2526,7 +2524,6 @@ "addTitle": "Добавить быструю команду", "chooseAgentTitle": "Выбрать агента", "deleteConfirmMessage": "Эта быстрая команда будет удалена из сохранённого списка.", - "deleteConfirmTitle": "Удалить «{{label}}»?", "editTitle": "Изменить быструю команду", "editorDescription": "Сохраняйте команды терминала или запросы агентам для быстрого доступа.", "title": "Быстрые команды", @@ -4310,7 +4307,6 @@ "outcome": "Результат", "allOutcomes": "Все результаты", "couldNotStart": "Не удалось запустить", - "deleteAutomationTitle": "Удалить автоматизацию?", "deleteRetained": "Удалить «{{name}}» и сохранённые запуски?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "Не удалось проверить исходную панель агента. Подключитесь заново и повторите попытку.", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "Исходную панель агента больше нельзя открыть. Сохранённый вывод остаётся здесь.", diff --git a/mobile/src/i18n/locales/tr.json b/mobile/src/i18n/locales/tr.json index 3986dd06aff..0b5b40e2849 100644 --- a/mobile/src/i18n/locales/tr.json +++ b/mobile/src/i18n/locales/tr.json @@ -1593,7 +1593,6 @@ "deleteConfirmMessage": "“{{name}}” ({{branch}}) silinsin mi?", "deleteDestroysEnvironment": "Orca'nın bunun için oluşturduğu \"{{environment}}\" ortamı da, içindeki commit edilmemiş değişiklikler ve çalışan terminallerle birlikte yok edilir.", "deleteKeepsEnvironment": "Orca'nın bunun için oluşturduğu \"{{environment}}\" ortamı çalışmaya devam eder: tarifinde yok etme adımı yok.", - "deleteWorktree": "Worktree'yi Sil", "pin": "Sabitle", "primaryWorktreeDeleteHint": "Birincil worktree, silinemez. Bunun yerine projeyi kaldırın.", "removeHost": "Sunucuyu Kaldır", @@ -1765,7 +1764,6 @@ "daysAgo_one": "{{count}} gün önce", "daysAgo_other": "{{count}} gün önce", "deleteConfirmMessage": "Bu işlem GitHub'daki yorumu kalıcı olarak siler.", - "deleteConfirmTitle": "Yorum silinsin mi?", "editPlaceholder": "Yorumu düzenleyin…", "hoursAgo_one": "{{count}} saat önce", "hoursAgo_other": "{{count}} saat önce", @@ -2386,7 +2384,6 @@ "addTitle": "Hızlı Komut Ekle", "chooseAgentTitle": "Ajan Seç", "deleteConfirmMessage": "Bu hızlı komut kayıtlı listenizden kaldırılacak.", - "deleteConfirmTitle": "“{{label}}” silinsin mi?", "editTitle": "Hızlı Komutu Düzenle", "editorDescription": "Hızlı erişim için terminal komutlarını veya ajan istemlerini kaydedin.", "title": "Hızlı Komutlar", @@ -4110,7 +4107,6 @@ "outcome": "Sonuç", "allOutcomes": "Tüm sonuçlar", "couldNotStart": "Başlatılamadı", - "deleteAutomationTitle": "Otomasyon silinsin mi?", "deleteRetained": "“{{name}}” ve saklanan çalıştırmaları silinsin mi?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "Özgün aracı bölmesi doğrulanamadı. Yeniden bağlanıp tekrar deneyin.", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "Özgün aracı bölmesi artık açılamıyor. Kaydedilen çıktı burada kalır.", diff --git a/mobile/src/i18n/locales/zh-Hant.json b/mobile/src/i18n/locales/zh-Hant.json index 5d5a9d41f7b..d74a5bcfb1f 100644 --- a/mobile/src/i18n/locales/zh-Hant.json +++ b/mobile/src/i18n/locales/zh-Hant.json @@ -1589,7 +1589,6 @@ "deleteConfirmMessage": "要刪除「{{name}}」({{branch}})嗎?", "deleteDestroysEnvironment": "這也會銷毀 Orca 為其建立的環境「{{environment}}」,包括其中未提交的變更和正在執行的終端機。", "deleteKeepsEnvironment": "Orca 為其建立的環境「{{environment}}」會繼續執行:其配方沒有銷毀步驟。", - "deleteWorktree": "刪除工作樹", "pin": "固定", "primaryWorktreeDeleteHint": "主工作樹不能刪除。請改為移除專案。", "removeHost": "移除主機", @@ -1761,7 +1760,6 @@ "daysAgo_one": "{{count}} 天前", "daysAgo_other": "{{count}} 天前", "deleteConfirmMessage": "這會永久刪除 GitHub 上的留言。", - "deleteConfirmTitle": "刪除留言?", "editPlaceholder": "編輯留言…", "hoursAgo_one": "{{count}} 小時前", "hoursAgo_other": "{{count}} 小時前", @@ -2382,7 +2380,6 @@ "addTitle": "新增快速命令", "chooseAgentTitle": "選擇代理程式", "deleteConfirmMessage": "該快速命令將從您儲存的清單中刪除。", - "deleteConfirmTitle": "要刪除「{{label}}」嗎?", "editTitle": "編輯快速命令", "editorDescription": "儲存終端機命令或代理程式提示詞,方便快速取用。", "title": "快速命令", @@ -4106,7 +4103,6 @@ "outcome": "結果", "allOutcomes": "所有結果", "couldNotStart": "無法啟動", - "deleteAutomationTitle": "刪除自動化?", "deleteRetained": "刪除「{{name}}」及其保留的執行?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "無法驗證原始代理程式窗格。請重新連線後再試。", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "原始代理程式窗格已無法開啟。已儲存的輸出仍保留在此處。", diff --git a/mobile/src/i18n/locales/zh.json b/mobile/src/i18n/locales/zh.json index 99baccd066a..cb130226828 100644 --- a/mobile/src/i18n/locales/zh.json +++ b/mobile/src/i18n/locales/zh.json @@ -1589,7 +1589,6 @@ "deleteConfirmMessage": "要删除“{{name}}”({{branch}})吗?", "deleteDestroysEnvironment": "这也会销毁 Orca 为其创建的环境“{{environment}}”,包括其中未提交的更改和正在运行的终端。", "deleteKeepsEnvironment": "Orca 为其创建的环境“{{environment}}”会继续运行:其配方没有销毁步骤。", - "deleteWorktree": "删除工作树", "pin": "固定", "primaryWorktreeDeleteHint": "主工作树不能删除。请改为移除项目", "removeHost": "移除主机", @@ -1761,7 +1760,6 @@ "daysAgo_one": "{{count}} 天前", "daysAgo_other": "{{count}} 天前", "deleteConfirmMessage": "这将永久删除 GitHub 上的这条评论", - "deleteConfirmTitle": "删除评论?", "editPlaceholder": "编辑评论…", "hoursAgo_one": "{{count}} 小时前", "hoursAgo_other": "{{count}} 小时前", @@ -2388,7 +2386,6 @@ "addTitle": "添加快捷命令", "chooseAgentTitle": "选择智能体", "deleteConfirmMessage": "该快捷命令将从您保存的列表中删除", - "deleteConfirmTitle": "删除“{{label}}”?", "editTitle": "编辑快捷命令", "editorDescription": "保存终端命令或智能体提示词,便于快速调用", "title": "快捷命令", @@ -4115,7 +4112,6 @@ "outcome": "结果", "allOutcomes": "所有结果", "couldNotStart": "无法启动", - "deleteAutomationTitle": "删除自动化?", "deleteRetained": "删除“{{name}}”及其保留的运行?", "couldNotVerifyTheOriginalAgentPaneReconnectAndTryAgain": "无法验证原始代理窗格。请重新连接后重试。", "theOriginalAgentPaneIsNoLongerAvailableToOpenSavedOutputRemainsHere": "原始代理窗格已无法打开。已保存的输出仍保留在此处。", diff --git a/mobile/src/terminal/quick-commands/QuickCommandsSheet.tsx b/mobile/src/terminal/quick-commands/QuickCommandsSheet.tsx index 8a870dd9868..b1f4d7c60dc 100644 --- a/mobile/src/terminal/quick-commands/QuickCommandsSheet.tsx +++ b/mobile/src/terminal/quick-commands/QuickCommandsSheet.tsx @@ -117,13 +117,14 @@ export function QuickCommandsSheet({ // Why: quick commands sync with desktop, so an accidental one-tap delete // removes shared data rather than only dismissing a local row. Alert.alert( - t('session.quickCommandsSheet.deleteConfirmTitle', 'Delete "{{label}}"?', { - label: command.label || t('session.quickCommandsSheet.untitled', 'Untitled') - }), - t( - 'session.quickCommandsSheet.deleteConfirmMessage', - 'This quick command will be removed from your saved list.' - ), + t('common.delete', 'Delete'), + [ + command.label || t('session.quickCommandsSheet.untitled', 'Untitled'), + t( + 'session.quickCommandsSheet.deleteConfirmMessage', + 'This quick command will be removed from your saved list.' + ) + ].join('\n'), [ { text: t('common.cancel', 'Cancel'), style: 'cancel' }, { diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index 4b11b0e9edb..6100df6a3fb 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -870,6 +870,25 @@ body[data-modal-overlay-open] } } +@utility control-interaction { + @media (hover: hover) { + &:not(:disabled, [aria-disabled='true']):hover { + outline: 1px solid var(--ring); + outline-offset: 1px; + } + } + + &:not(:disabled, [aria-disabled='true']):focus { + outline: 2px solid var(--ring); + outline-offset: 2px; + } + + &:not(:disabled, [aria-disabled='true']):active { + outline: 2px solid var(--ring); + outline-offset: 0; + } +} + /* Tailwind must generate this utility; a plain class silently emits no scrollbar-none CSS. */ @utility scrollbar-none { -ms-overflow-style: none; diff --git a/src/renderer/src/components/artifacts/ArtifactsPage.tsx b/src/renderer/src/components/artifacts/ArtifactsPage.tsx index 27fd63a96eb..a6376613463 100644 --- a/src/renderer/src/components/artifacts/ArtifactsPage.tsx +++ b/src/renderer/src/components/artifacts/ArtifactsPage.tsx @@ -101,7 +101,7 @@ export default function ArtifactsPage(): React.JSX.Element { const name = item.artifact.title || item.artifact.originalFileName || item.artifact.slug if (!settings?.skipDeleteArtifactConfirm) { const accepted = await confirm({ - title: t('auto.components.artifacts.ArtifactsPage.deleteTitle', 'Delete artifact?'), + title: t('common.delete', 'Delete'), description: t( 'auto.components.artifacts.ArtifactsPage.deleteDescription', '“{{name}}” will no longer be available at its public link.', diff --git a/src/renderer/src/components/automations/authoring/AutomationDeleteDialogs.tsx b/src/renderer/src/components/automations/authoring/AutomationDeleteDialogs.tsx index f5f46a51520..b51a643fc67 100644 --- a/src/renderer/src/components/automations/authoring/AutomationDeleteDialogs.tsx +++ b/src/renderer/src/components/automations/authoring/AutomationDeleteDialogs.tsx @@ -48,9 +48,7 @@ export function AutomationDeleteDialog({ }} > - - {t('auto.components.automations.AutomationsPage.080dcb5fbb', 'Delete Automation')} - + {t('common.delete', 'Delete')} - - {t( - 'auto.components.automations.AutomationsPage.9adfab2596', - 'Delete External Automation' - )} - + {t('common.delete', 'Delete')} { return confirm({ - title: translate( - 'auto.components.right.sidebar.useFileDeletion.d979a4fbb5', - "Permanently delete '{{value0}}'?", - { value0: node.name } - ), - description: node.isDirectory - ? translate( - 'auto.components.right.sidebar.useFileDeletion.7fb9435c86', - 'This permanently deletes the directory and its contents on the remote host. This cannot be undone.' - ) - : translate( - 'auto.components.right.sidebar.useFileDeletion.23e98f192f', - 'This permanently deletes the file on the remote host. This cannot be undone.' - ), + title: translate('common.delete', 'Delete'), + description: [ + node.name, + node.isDirectory + ? translate( + 'auto.components.right.sidebar.useFileDeletion.7fb9435c86', + 'This permanently deletes the directory and its contents on the remote host. This cannot be undone.' + ) + : translate( + 'auto.components.right.sidebar.useFileDeletion.23e98f192f', + 'This permanently deletes the file on the remote host. This cannot be undone.' + ) + ].join('\n'), confirmLabel: translate('auto.components.right.sidebar.useFileDeletion.92276aceb7', 'Delete'), confirmVariant: 'destructive' }) diff --git a/src/renderer/src/components/right-sidebar/file-explorer/mutations/file-explorer-trash-fallback.ts b/src/renderer/src/components/right-sidebar/file-explorer/mutations/file-explorer-trash-fallback.ts index f9dcffdb7ea..f7e426d96c6 100644 --- a/src/renderer/src/components/right-sidebar/file-explorer/mutations/file-explorer-trash-fallback.ts +++ b/src/renderer/src/components/right-sidebar/file-explorer/mutations/file-explorer-trash-fallback.ts @@ -29,19 +29,22 @@ export async function deleteWithPermanentFallback({ } } const confirmed = await confirm({ - title: isWindows - ? translate( - 'fileExplorer.trashFailed.titleRecycleBin', - "Couldn't move '{{name}}' to the Recycle Bin. Delete it permanently?", - { name } - ) - : translate( - 'fileExplorer.trashFailed.titleTrash', - "Couldn't move '{{name}}' to the Trash. Delete it permanently?", - { name } - ), - description: translate('fileExplorer.trashFailed.description', 'This cannot be undone.'), - confirmLabel: translate('fileExplorer.trashFailed.confirm', 'Delete Permanently'), + title: translate('common.delete', 'Delete'), + description: [ + isWindows + ? translate( + 'fileExplorer.trashFailed.titleRecycleBin', + "Couldn't move '{{name}}' to the Recycle Bin. Delete it permanently?", + { name } + ) + : translate( + 'fileExplorer.trashFailed.titleTrash', + "Couldn't move '{{name}}' to the Trash. Delete it permanently?", + { name } + ), + translate('fileExplorer.trashFailed.description', 'This cannot be undone.') + ].join('\n'), + confirmLabel: translate('common.delete', 'Delete'), confirmVariant: 'destructive', cancelVariant: 'ghost', surface: 'security' diff --git a/src/renderer/src/components/right-sidebar/file-explorer/mutations/useFileDeletion.trash-failure.test.tsx b/src/renderer/src/components/right-sidebar/file-explorer/mutations/useFileDeletion.trash-failure.test.tsx index b82ad8ee642..72375566aea 100644 --- a/src/renderer/src/components/right-sidebar/file-explorer/mutations/useFileDeletion.trash-failure.test.tsx +++ b/src/renderer/src/components/right-sidebar/file-explorer/mutations/useFileDeletion.trash-failure.test.tsx @@ -97,16 +97,7 @@ it('permanently deletes after the user confirms a Trash failure', async () => { await requestDelete(result) await vi.waitFor(() => expect(closeFile).toHaveBeenCalledWith('tab-1')) - expect(mocks.confirm).toHaveBeenCalledWith( - expect.objectContaining({ - title: "Couldn't move 'file.ts' to the Recycle Bin. Delete it permanently?", - description: 'This cannot be undone.', - confirmLabel: 'Delete Permanently', - confirmVariant: 'destructive', - cancelVariant: 'ghost', - surface: 'security' - }) - ) + expect(mocks.deleteRuntimePath).toHaveBeenLastCalledWith( expect.anything(), 'C:\\repo\\file.ts', @@ -134,11 +125,7 @@ it('keeps the file and stays quiet when the user cancels', async () => { await requestDelete(result) await vi.waitFor(() => expect(mocks.confirm).toHaveBeenCalled()) - expect(mocks.confirm).toHaveBeenCalledWith( - expect.objectContaining({ - title: "Couldn't move 'file.ts' to the Trash. Delete it permanently?" - }) - ) + expect(mocks.deleteRuntimePath).toHaveBeenCalledTimes(1) expect(closeFile).not.toHaveBeenCalled() expect(refreshDir).not.toHaveBeenCalled() diff --git a/src/renderer/src/components/right-sidebar/file-explorer/mutations/useFileDeletion.ts b/src/renderer/src/components/right-sidebar/file-explorer/mutations/useFileDeletion.ts index d560593ddae..577114fe834 100644 --- a/src/renderer/src/components/right-sidebar/file-explorer/mutations/useFileDeletion.ts +++ b/src/renderer/src/components/right-sidebar/file-explorer/mutations/useFileDeletion.ts @@ -263,24 +263,30 @@ export function useFileDeletion({ needsConfirmation: roots.some(needsRemoteDeleteConfirmation), confirmBatch: () => confirm({ + title: translate('common.delete', 'Delete'), // Count selected children even when ancestor roots subsume their deletes. - title: hasLocalDelete - ? translate( - 'auto.components.right.sidebar.useFileDeletion.deleteItems', - 'Delete {{count}} items?', - { count: nodes.length, defaultValue_one: 'Delete {{count}} item?' } - ) - : translate( - 'auto.components.right.sidebar.useFileDeletion.permanentlyDeleteItems', - 'Permanently delete {{count}} items?', - { count: nodes.length, defaultValue_one: 'Permanently delete {{count}} item?' } - ), - description: !hasLocalDelete - ? translate( - 'auto.components.right.sidebar.useFileDeletion.dd029aa5cd', - 'This permanently deletes the selected items and any directory contents on the remote host. This cannot be undone.' - ) - : mixedBatchDeleteDescription(isWindows), + description: [ + hasLocalDelete + ? translate( + 'auto.components.right.sidebar.useFileDeletion.deleteItems', + 'Delete {{count}} items?', + { count: nodes.length, defaultValue_one: 'Delete {{count}} item?' } + ) + : translate( + 'auto.components.right.sidebar.useFileDeletion.permanentlyDeleteItems', + 'Permanently delete {{count}} items?', + { + count: nodes.length, + defaultValue_one: 'Permanently delete {{count}} item?' + } + ), + !hasLocalDelete + ? translate( + 'auto.components.right.sidebar.useFileDeletion.dd029aa5cd', + 'This permanently deletes the selected items and any directory contents on the remote host. This cannot be undone.' + ) + : mixedBatchDeleteDescription(isWindows) + ].join('\n'), confirmLabel: translate( 'auto.components.right.sidebar.useFileDeletion.92276aceb7', 'Delete' diff --git a/src/renderer/src/components/right-sidebar/source-control/commit/discard-confirmation.ts b/src/renderer/src/components/right-sidebar/source-control/commit/discard-confirmation.ts index dcab6403d0a..47a9b55b0fc 100644 --- a/src/renderer/src/components/right-sidebar/source-control/commit/discard-confirmation.ts +++ b/src/renderer/src/components/right-sidebar/source-control/commit/discard-confirmation.ts @@ -24,11 +24,7 @@ export function getDiscardEntryConfirmationCopy( if (discardDeletesEntryFile(entry)) { return { - title: t( - 'auto.components.right.sidebar.source.control.discard.confirmation.96c772bee9', - 'Delete "{{value0}}"?', - { value0: name } - ), + title: t('common.delete', 'Delete'), description: t( 'auto.components.right.sidebar.source.control.discard.confirmation.d97bf697c9', 'This will permanently delete this file. This cannot be undone.' @@ -77,11 +73,7 @@ export function getDiscardAreaConfirmationCopy( switch (area) { case 'untracked': return { - title: t( - 'sourceControl.discard.deleteUntrackedTitle', - 'Delete {{count}} untracked files?', - { count, defaultValue_one: 'Delete {{count}} untracked file?' } - ), + title: t('common.delete', 'Delete'), description: count === 1 ? t( @@ -98,10 +90,7 @@ export function getDiscardAreaConfirmationCopy( } ), deleteAction: true, - confirmLabel: - count === 1 - ? t('sourceControl.discard.delete', 'Delete') - : t('sourceControl.discard.deleteCount', 'Delete {{count}}', { count }) + confirmLabel: t('common.delete', 'Delete') } case 'staged': return { diff --git a/src/renderer/src/components/settings/browser/passwords/BrowserPasswordsSection.tsx b/src/renderer/src/components/settings/browser/passwords/BrowserPasswordsSection.tsx index 999fcd4edab..4051fd93052 100644 --- a/src/renderer/src/components/settings/browser/passwords/BrowserPasswordsSection.tsx +++ b/src/renderer/src/components/settings/browser/passwords/BrowserPasswordsSection.tsx @@ -169,18 +169,15 @@ function BrowserPasswordsProfile({ const remove = async (item: BrowserPasswordEntrySummary): Promise => { const confirmed = await confirm({ - title: t( - 'settings.browser.passwords.deleteConfirmTitle', - 'Delete the {{site}} password for {{username}}?', - { - site: browserPasswordHostOf(item.origin), - username: item.username || t('settings.browser.passwords.noUsername', '(no username)') - } - ), - description: t( - 'settings.browser.passwords.deleteConfirmDescription', - 'Orca won’t fill it anymore. This can’t be undone.' - ), + title: t('common.delete', 'Delete'), + description: [ + browserPasswordHostOf(item.origin), + item.username || t('settings.browser.passwords.noUsername', '(no username)'), + t( + 'settings.browser.passwords.deleteConfirmDescription', + 'Orca won’t fill it anymore. This can’t be undone.' + ) + ].join('\n'), confirmLabel: t('settings.browser.passwords.deleteConfirm', 'Delete'), confirmVariant: 'destructive' }) @@ -197,13 +194,13 @@ function BrowserPasswordsProfile({ const deleteAll = async (): Promise => { const confirmed = await confirm({ - title: t('settings.browser.passwords.deleteAllConfirmTitle', 'Delete all password data?'), + title: t('common.delete', 'Delete'), description: t( 'settings.browser.passwords.deleteAllConfirmDescription', 'Every saved password in the {{profile}} profile is permanently deleted. This can’t be undone.', { profile: profileLabel } ), - confirmLabel: t('settings.browser.passwords.deleteAll', 'Delete data'), + confirmLabel: t('common.delete', 'Delete'), confirmVariant: 'destructive' }) if (confirmed && (await api.deleteAll({ browserProfileId }))) { diff --git a/src/renderer/src/components/settings/quick-commands/QuickCommandsPane.tsx b/src/renderer/src/components/settings/quick-commands/QuickCommandsPane.tsx index ca0161265ef..8aeaa2e92b4 100644 --- a/src/renderer/src/components/settings/quick-commands/QuickCommandsPane.tsx +++ b/src/renderer/src/components/settings/quick-commands/QuickCommandsPane.tsx @@ -252,13 +252,14 @@ export function QuickCommandsPane({ const removeCommand = async (command: TerminalQuickCommand): Promise => { const confirmed = await confirm({ - title: t('auto.components.settings.QuickCommandsPane.3edf3deaf8', 'Delete "{{value0}}"?', { - value0: command.label || 'Untitled' - }), - description: t( - 'auto.components.settings.QuickCommandsPane.3d9dc558e8', - 'This quick command will be removed from your saved list.' - ), + title: t('common.delete', 'Delete'), + description: [ + command.label || 'Untitled', + t( + 'auto.components.settings.QuickCommandsPane.3d9dc558e8', + 'This quick command will be removed from your saved list.' + ) + ].join('\n'), confirmLabel: t('auto.components.settings.QuickCommandsPane.ec1ed99e70', 'Delete'), confirmVariant: 'destructive' }) diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteNestedWorktreesDialog.test.tsx b/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteNestedWorktreesDialog.test.tsx index 7070ced8f01..a3036c07bf0 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteNestedWorktreesDialog.test.tsx +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteNestedWorktreesDialog.test.tsx @@ -83,7 +83,7 @@ describe('nested deletion confirmation', () => { /> ) await review() - fireEvent.click(screen.getByRole('button', { name: 'Delete all listed worktrees' })) + fireEvent.click(screen.getByRole('button', { name: 'Delete' })) await vi.waitFor(() => expect(mocks.refresh).toHaveBeenCalled()) expect(mocks.remove).toHaveBeenCalledWith(target, true, { approvedNestedWorktrees: plan, @@ -111,7 +111,7 @@ describe('nested deletion confirmation', () => { /> ) await review() - fireEvent.click(screen.getByRole('button', { name: 'Delete all listed worktrees' })) + fireEvent.click(screen.getByRole('button', { name: 'Delete' })) expect(screen.queryByRole('dialog')).not.toBeInTheDocument() expect(screen.getByRole('button', { name: 'Deleting…' })).toBeDisabled() expect(mocks.refresh).not.toHaveBeenCalled() @@ -137,7 +137,7 @@ describe('nested deletion confirmation', () => { ) await review() - fireEvent.click(screen.getByRole('button', { name: 'Delete all listed worktrees' })) + fireEvent.click(screen.getByRole('button', { name: 'Delete' })) await vi.waitFor(() => expect(toast.error).toHaveBeenCalledWith('Failed to delete workspace', { description: 'Child archive hook failed' @@ -145,9 +145,7 @@ describe('nested deletion confirmation', () => { ) expect(screen.queryByRole('dialog')).not.toBeInTheDocument() expect(mocks.refresh).toHaveBeenCalledOnce() - expect( - screen.queryByRole('button', { name: 'Delete all listed worktrees' }) - ).not.toBeInTheDocument() + expect(screen.queryByRole('button', { name: 'Delete' })).not.toBeInTheDocument() await review() await vi.waitFor(() => expect(mocks.preview).toHaveBeenCalledTimes(2)) } diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteNestedWorktreesDialog.tsx b/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteNestedWorktreesDialog.tsx index 0c0301f04e0..76a67fe129a 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteNestedWorktreesDialog.tsx +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteNestedWorktreesDialog.tsx @@ -171,9 +171,7 @@ export function DeleteNestedWorktreesDialog({ > - - {t('worktree.nestedRemoval.title', 'Delete workspace and nested worktrees?')} - + {t('common.delete', 'Delete')} {t( 'worktree.nestedRemoval.description', @@ -244,7 +242,7 @@ export function DeleteNestedWorktreesDialog({ {deleting ? : null} {deleting ? t('worktree.nestedRemoval.deleting', 'Deleting…') - : t('worktree.nestedRemoval.confirm', 'Delete all listed worktrees')} + : t('common.delete', 'Delete')} )} diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteWorktreeDialog.tsx b/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteWorktreeDialog.tsx index 12ae8afa370..d1d54f3da7b 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteWorktreeDialog.tsx +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteWorktreeDialog.tsx @@ -330,11 +330,7 @@ const DeleteWorktreeDialog = React.memo(function DeleteWorktreeDialog() { }} > - - {isBatchDelete - ? t('auto.components.sidebar.DeleteWorktreeDialog.86f0ae1257', 'Delete Workspaces') - : t('auto.components.sidebar.DeleteWorktreeDialog.fc23c4cbdf', 'Delete Workspace')} - + {t('common.delete', 'Delete')} handleOpenChange(false)} onForceDelete={ preserveChildrenSelected ? handleDetachAndDelete : () => handleDelete(true) diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteWorktreeDialogFooter.tsx b/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteWorktreeDialogFooter.tsx index 050ac614e87..a06ca1106c4 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteWorktreeDialogFooter.tsx +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/DeleteWorktreeDialogFooter.tsx @@ -5,7 +5,6 @@ import { Button } from '@/components/ui/button' import { useTranslate } from '@/i18n/use-translate' import { useAppStore } from '@/store' import { WorktreeForceDeleteButton } from './WorktreeForceDeleteButton' -import { deleteWorkspacesCountLabel } from '../context-menu/workspace-batch-action-labels' export function DeleteWorktreeDialogFooter({ isMainWorktree, @@ -13,10 +12,6 @@ export function DeleteWorktreeDialogFooter({ isDetachingChildren = false, deleteDisabled = false, canForceDelete, - isBatchDelete, - worktreeCount, - canDeleteAllLineage, - lineageDeleteTargetCount, onCancel, onForceDelete, onSavingChange, @@ -29,10 +24,6 @@ export function DeleteWorktreeDialogFooter({ isDetachingChildren?: boolean deleteDisabled?: boolean canForceDelete: boolean - isBatchDelete: boolean - worktreeCount: number - canDeleteAllLineage: boolean - lineageDeleteTargetCount: number onCancel: () => void onForceDelete: () => void onSavingChange: (saving: boolean) => void @@ -48,16 +39,7 @@ export function DeleteWorktreeDialogFooter({ ? canForceDelete ? t('auto.components.sidebar.DeleteWorktreeDialogFooter.forceDeleting', 'Force Deleting...') : t('auto.components.sidebar.DeleteWorktreeDialogFooter.deleting', 'Deleting...') - : isBatchDelete - ? deleteWorkspacesCountLabel(worktreeCount, t) - : canDeleteAllLineage - ? deleteWorkspacesCountLabel(lineageDeleteTargetCount, t) - : canForceDelete - ? t('auto.components.sidebar.DeleteWorktreeDialogFooter.forceDelete', 'Force Delete') - : t( - 'auto.components.sidebar.DeleteWorktreeDialogFooter.deleteWorkspace', - 'Delete Workspace' - ) + : t('common.delete', 'Delete') return ( <> diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/PreservedBranchBatchReviewDialog.tsx b/src/renderer/src/components/sidebar/workspace-actions/deletion/PreservedBranchBatchReviewDialog.tsx index db415048889..abb8d36c624 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/PreservedBranchBatchReviewDialog.tsx +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/PreservedBranchBatchReviewDialog.tsx @@ -74,14 +74,9 @@ export function PreservedBranchBatchReviewDialog({ return ( - + - - {t( - 'auto.components.sidebar.PreservedBranchBatchReviewDialog.c4bf8e7eaf', - 'Review kept branches' - )} - + {t('common.delete', 'Delete')} {t( 'auto.components.sidebar.PreservedBranchBatchReviewDialog.f21976c9a8', @@ -173,11 +168,7 @@ export function PreservedBranchBatchReviewDialog({ onClick={() => onForceDelete(selectedBranches)} > - {t( - 'auto.components.sidebar.PreservedBranchBatchReviewDialog.a0f9863597', - 'Force Delete {{count}} Branches', - { count: selectedBranches.length, defaultValue_one: 'Force Delete {{count}} Branch' } - )} + {t('common.delete', 'Delete')} diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/preserved-branch-batch-toast.test.tsx b/src/renderer/src/components/sidebar/workspace-actions/deletion/preserved-branch-batch-toast.test.tsx index 8315af02d16..ef5a21f77cb 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/preserved-branch-batch-toast.test.tsx +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/preserved-branch-batch-toast.test.tsx @@ -97,7 +97,7 @@ describe('showPreservedBranchBatchToast', () => { ]) await clickCheckbox('preserved-branch-1') - await clickButton(document.body, 'Force Delete 1 Branch') + await clickButton(document.body, 'Delete') expect(mocks.state.closeModal).toHaveBeenCalledOnce() expect(mocks.state.forceDeletePreservedBranch).toHaveBeenCalledOnce() @@ -137,7 +137,7 @@ describe('showPreservedBranchBatchToast', () => { ]) expect((document.getElementById('preserved-branch-1') as HTMLButtonElement).disabled).toBe(true) - await clickButton(document.body, 'Force Delete 1 Branch') + await clickButton(document.body, 'Delete') expect(mocks.state.forceDeletePreservedBranch).toHaveBeenCalledOnce() expect(mocks.state.forceDeletePreservedBranch).toHaveBeenCalledWith( diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/ProjectGroupDeleteDialog.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/ProjectGroupDeleteDialog.tsx index e84ce744275..0b660e112fa 100644 --- a/src/renderer/src/components/sidebar/worktree-list/rows/ProjectGroupDeleteDialog.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/ProjectGroupDeleteDialog.tsx @@ -110,12 +110,7 @@ export function ProjectGroupDeleteDialog({ }} > - - {t( - 'auto.components.sidebar.ProjectGroupDeleteDialog.591f330288', - 'Delete Project Group' - )} - + {t('common.delete', 'Delete')} {deleting ? t('auto.components.sidebar.ProjectGroupDeleteDialog.2c14ce677a', 'Deleting...') - : t('auto.components.sidebar.ProjectGroupDeleteDialog.fec7e9c8ae', 'Delete Group')} + : t('common.delete', 'Delete')} diff --git a/src/renderer/src/components/skills/deletion/use-skill-delete-flow.ts b/src/renderer/src/components/skills/deletion/use-skill-delete-flow.ts index adcec8011f6..e79b8dd0d37 100644 --- a/src/renderer/src/components/skills/deletion/use-skill-delete-flow.ts +++ b/src/renderer/src/components/skills/deletion/use-skill-delete-flow.ts @@ -107,8 +107,9 @@ export function useSkillDeleteFlow( return false } const confirmed = await confirm({ - title: skillDeleteActionLabel(actionable.length), + title: translate('common.delete', 'Delete'), description: [ + skillDeleteActionLabel(actionable.length), hostLabel ? translate('auto.components.skills.SkillDelete.confirmHost', 'On {{host}}.', { host: hostLabel @@ -128,7 +129,7 @@ export function useSkillDeleteFlow( ] .filter((line): line is string => Boolean(line)) .join('\n'), - confirmLabel: skillDeleteActionLabel(actionable.length), + confirmLabel: translate('common.delete', 'Delete'), confirmVariant: 'destructive' }) if (!confirmed) { diff --git a/src/renderer/src/components/skills/sharing/SkillSharedLinkRow.test.tsx b/src/renderer/src/components/skills/sharing/SkillSharedLinkRow.test.tsx index 03ac9ef808f..89d38ee1685 100644 --- a/src/renderer/src/components/skills/sharing/SkillSharedLinkRow.test.tsx +++ b/src/renderer/src/components/skills/sharing/SkillSharedLinkRow.test.tsx @@ -58,7 +58,7 @@ describe('SkillSharedLinkRow', () => { await openDeleteMenu() expect(deletePackage).not.toHaveBeenCalled() - fireEvent.click(screen.getByRole('button', { name: 'Confirm delete' })) + fireEvent.click(screen.getByRole('button', { name: 'Delete' })) expect(deletePackage).toHaveBeenCalledWith('pkg_1') await vi.waitFor(() => expect(onDeleted).toHaveBeenCalledOnce()) diff --git a/src/renderer/src/components/skills/sharing/SkillSharedLinkRow.tsx b/src/renderer/src/components/skills/sharing/SkillSharedLinkRow.tsx index 5be9838636c..65762d2be80 100644 --- a/src/renderer/src/components/skills/sharing/SkillSharedLinkRow.tsx +++ b/src/renderer/src/components/skills/sharing/SkillSharedLinkRow.tsx @@ -156,7 +156,7 @@ export function SkillSharedLinkRow({ {busy || deleting ? : null} {confirming === 'revoke' ? t('auto.components.settings.shareSkills.confirmUnshare', 'Confirm unshare') - : t('auto.components.skills.SkillSharedLinkRow.confirmDelete', 'Confirm delete')} + : t('common.delete', 'Delete')} ) : ( <> diff --git a/src/renderer/src/components/tab-bar/TabBarQuickCommandsButton.tsx b/src/renderer/src/components/tab-bar/TabBarQuickCommandsButton.tsx index 3b2041e6833..1a54628a4af 100644 --- a/src/renderer/src/components/tab-bar/TabBarQuickCommandsButton.tsx +++ b/src/renderer/src/components/tab-bar/TabBarQuickCommandsButton.tsx @@ -122,15 +122,14 @@ export function TabBarQuickCommandsButton({ const handleDeleteCommand = async (entry: HostedTerminalQuickCommand): Promise => { const { command } = entry const confirmed = await confirm({ - title: t( - 'auto.components.tab.bar.TabBarQuickCommandsButton.e8e1a52edb', - 'Delete "{{value0}}"?', - { value0: command.label } - ), - description: t( - 'auto.components.tab.bar.TabBarQuickCommandsButton.3220e2da27', - 'This quick command will be removed from your saved list.' - ), + title: t('common.delete', 'Delete'), + description: [ + command.label, + t( + 'auto.components.tab.bar.TabBarQuickCommandsButton.3220e2da27', + 'This quick command will be removed from your saved list.' + ) + ].join('\n'), confirmLabel: t('auto.components.tab.bar.TabBarQuickCommandsButton.be8f0ff166', 'Delete'), confirmVariant: 'destructive' }) diff --git a/src/renderer/src/components/ui/button-group.tsx b/src/renderer/src/components/ui/button-group.tsx index ee38c9d991f..ed5cc6ae26b 100644 --- a/src/renderer/src/components/ui/button-group.tsx +++ b/src/renderer/src/components/ui/button-group.tsx @@ -4,7 +4,7 @@ import { cva, type VariantProps } from 'class-variance-authority' import { cn } from '@/lib/class-names' const buttonGroupVariants = cva( - "flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1", + "flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:hover:relative [&>*]:hover:z-10 [&>*]:focus:relative [&>*]:focus:z-20 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1", { variants: { orientation: { diff --git a/src/renderer/src/components/ui/button.fixture.tsx b/src/renderer/src/components/ui/button.fixture.tsx index cb8bcaa4a95..aaf5bea9c9e 100644 --- a/src/renderer/src/components/ui/button.fixture.tsx +++ b/src/renderer/src/components/ui/button.fixture.tsx @@ -55,3 +55,25 @@ export const sizes = defineFixture({
) }) + +function focusedButtonFixture(variant: (typeof VARIANTS)[number]) { + return defineFixture({ + width: 320, + render: () => ( +
+ + +
+ ), + interact: (frame) => frame.querySelector('[data-fixture-focus]')?.focus() + }) +} + +export const focusedPrimary = focusedButtonFixture('default') + +export const focusedDestructive = focusedButtonFixture('destructive') + +export const focusedOutline = focusedButtonFixture('outline') diff --git a/src/renderer/src/components/ui/button.tsx b/src/renderer/src/components/ui/button.tsx index 2c901f9c878..e5d7bbf8ddc 100644 --- a/src/renderer/src/components/ui/button.tsx +++ b/src/renderer/src/components/ui/button.tsx @@ -8,13 +8,12 @@ const outlineButtonClasses = 'border border-border bg-background text-foreground shadow-xs hover:border-muted-foreground/35 hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50' const buttonVariants = cva( - "inline-flex shrink-0 items-center justify-center gap-2 rounded-md cursor-pointer text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + "control-interaction inline-flex shrink-0 items-center justify-center gap-2 rounded-md cursor-pointer text-sm font-medium whitespace-nowrap transition-[color,background-color,border-color,box-shadow] outline-none disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", { variants: { variant: { default: 'bg-primary text-primary-foreground hover:bg-primary/90', - destructive: - 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40', + destructive: 'bg-destructive text-white hover:bg-destructive/90 dark:bg-destructive/60', outline: outlineButtonClasses, 'orchestration-tab': `${outlineButtonClasses} border-(--crew-border) bg-(--crew-surface) text-card-foreground shadow-floating dark:border-(--crew-border) dark:bg-(--crew-surface) dark:hover:bg-accent dark:hover:text-accent-foreground`, secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', diff --git a/src/renderer/src/components/ui/control-interaction.fixture.tsx b/src/renderer/src/components/ui/control-interaction.fixture.tsx new file mode 100644 index 00000000000..a4cc6cbf900 --- /dev/null +++ b/src/renderer/src/components/ui/control-interaction.fixture.tsx @@ -0,0 +1,54 @@ +import { defineFixture } from '../../component-fixtures/component-fixture' +import { Select, SelectTrigger, SelectValue } from './select' +import { Toggle } from './toggle' +import { ToggleGroup, ToggleGroupItem } from './toggle-group' + +function selectionControlsFixture(focusedSlot?: string) { + return defineFixture({ + width: 640, + render: () => ( +
+
+ Toggle + Wrap lines + + Disabled + +
+
+ Select + + +
+
+ Group + + Files + Changes + + Disabled + + +
+
+ ), + interact: (frame) => + frame.querySelector(`[data-slot="${focusedSlot}"]`)?.focus() + }) +} + +export const selectionControls = selectionControlsFixture() + +export const focusedToggle = selectionControlsFixture('toggle') + +export const focusedSelect = selectionControlsFixture('select-trigger') + +export const focusedToggleGroup = selectionControlsFixture('toggle-group-item') diff --git a/src/renderer/src/components/ui/select.tsx b/src/renderer/src/components/ui/select.tsx index 9efd1267e28..e4033d990d3 100644 --- a/src/renderer/src/components/ui/select.tsx +++ b/src/renderer/src/components/ui/select.tsx @@ -56,7 +56,7 @@ function SelectTrigger({ data-slot="select-trigger" data-size={size} className={cn( - "flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground", + "control-interaction flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground", selectTriggerVariants({ density }), className )} diff --git a/src/renderer/src/components/ui/toggle.tsx b/src/renderer/src/components/ui/toggle.tsx index 1843a638884..a33d7d5f8a3 100644 --- a/src/renderer/src/components/ui/toggle.tsx +++ b/src/renderer/src/components/ui/toggle.tsx @@ -5,7 +5,7 @@ import * as TogglePrimitive from 'radix-ui/toggle' import { cn } from '@/lib/class-names' const toggleVariants = cva( - "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + "control-interaction inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", { variants: { variant: { diff --git a/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-header.tsx b/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-header.tsx index 47a895eb5ab..9e37c3c3e1a 100644 --- a/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-header.tsx +++ b/src/renderer/src/components/workspace-cleanup/dialog/workspace-cleanup-dialog-header.tsx @@ -30,11 +30,8 @@ export function WorkspaceCleanupDialogHeader({
- - {t( - 'auto.components.workspace.cleanup.WorkspaceCleanupDialog.b2c1331844', - 'Delete Inactive Workspaces' - )} + + {t('common.delete', 'Delete')}
- - {count === 1 - ? t( - 'auto.components.terminal.pane.CloseTerminalDialog.stop_agent_title', - 'Stop this agent?' - ) - : t('components.workspace.cleanup.stopAgents.titleMany', 'Stop these agents?')} - + {t('common.delete', 'Delete')} + + {t('components.workspace.cleanup.browse.workspaceCount', '{{count}} workspaces', { + count, + defaultValue_one: '{{count}} workspace' + })} + {count === 1 ? t( 'components.workspace.cleanup.stopAgents.descriptionOne', @@ -97,9 +96,7 @@ export function WorkspaceCleanupConfirmStopAgents({
diff --git a/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-confirm-remove.tsx b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-confirm-remove.tsx index 9e5d5bfd997..826ec78e364 100644 --- a/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-confirm-remove.tsx +++ b/src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-confirm-remove.tsx @@ -84,13 +84,17 @@ export function WorkspaceCleanupConfirmRemove({ 'Deleting {{count}} workspaces', { count, defaultValue_one: 'Deleting {{count}} workspace' } ) - : t( - 'components.workspace.cleanup.confirm.deleteTitle', - 'Delete {{count}} workspaces?', - { count, defaultValue_one: 'Delete {{count}} workspace?' } - )} + : t('common.delete', 'Delete')}
+ {!deleting && ( + + {t('components.workspace.cleanup.browse.workspaceCount', '{{count}} workspaces', { + count, + defaultValue_one: '{{count}} workspace' + })} + + )} {deleting ? t( 'auto.components.workspace.cleanup.WorkspaceCleanupDialog.1d3503357d', @@ -176,11 +180,7 @@ export function WorkspaceCleanupConfirmRemove({ {!deleting ? ( ) : null} diff --git a/src/renderer/src/i18n/en-runtime-required.json b/src/renderer/src/i18n/en-runtime-required.json index de2a463f586..f2556d3ecfd 100644 --- a/src/renderer/src/i18n/en-runtime-required.json +++ b/src/renderer/src/i18n/en-runtime-required.json @@ -926,11 +926,6 @@ "reviewHiddenWorktrees_one": "Review {{count}} hidden worktree in {{scope}}", "reviewHiddenWorktrees_other": "Review {{count}} hidden worktrees in {{scope}}" }, - "PreservedBranchBatchReviewDialog": { - "a0f9863597": "Force Delete {{count}} Branches", - "a0f9863597_one": "Force Delete {{count}} Branch", - "a0f9863597_other": "Force Delete {{count}} Branches" - }, "ProjectGroupDeleteDialog": { "moreProjects_one": "+{{count}} more", "moreProjects_other": "+{{count}} more", @@ -2143,8 +2138,6 @@ "workspaceCount_other": "{{count}} workspaces" }, "confirm": { - "deleteTitle_one": "Delete {{count}} workspace?", - "deleteTitle_other": "Delete {{count}} workspaces?", "deletingTitle_one": "Deleting {{count}} workspace", "deletingTitle_other": "Deleting {{count}} workspaces" }, @@ -2581,8 +2574,6 @@ "discard": { "deleteUntrackedFilesDescription_one": "This will permanently delete {{count}} untracked file. This cannot be undone.", "deleteUntrackedFilesDescription_other": "This will permanently delete these {{count}} untracked files. This cannot be undone.", - "deleteUntrackedTitle_one": "Delete {{count}} untracked file?", - "deleteUntrackedTitle_other": "Delete {{count}} untracked files?", "unstagedFilesIncludingUntrackedDescription_one": "This will revert unstaged changes and permanently delete any untracked files among {{count}} file. This cannot be undone.", "unstagedFilesIncludingUntrackedDescription_other": "This will revert unstaged changes and permanently delete any untracked files among these {{count}} files. This cannot be undone." }, diff --git a/src/renderer/src/i18n/locales/de.json b/src/renderer/src/i18n/locales/de.json index 396f1cb7af8..6901410c75b 100644 --- a/src/renderer/src/i18n/locales/de.json +++ b/src/renderer/src/i18n/locales/de.json @@ -3813,7 +3813,6 @@ "7eee951968": "Arbeitsbereiche werden durchsucht", "191f0bc98e": "Schließen", "7ae2ad30f4": "Aktualisieren", - "b2c1331844": "Inaktive Arbeitsbereiche löschen", "7f451a3e2c": "Bereinigungsvorschlag konnte nicht ignoriert werden", "662b8ec3f8": "Suche nach zu bereinigenden Arbeitsbereichen fehlgeschlagen", "9cc26c019d": "Entfernen", @@ -3823,7 +3822,6 @@ "1d3503357d": "Dieses Fenster kann geschlossen und später wieder geöffnet werden, während das Löschen fortgesetzt wird.", "4c2990886e": "{{value0}}/{{value1}} gelöscht", "86ba852118": "{{value0}}, {{value1}} fehlgeschlagen", - "deleteButtonCount": "{{value0}} löschen", "archivedStatus": "Archiviert", "74f6c16279": "Zurück" }, @@ -4414,7 +4412,6 @@ "1d411fb6a5": "Schnellbefehl für dieses Repo speichern", "8f1e971966": "Schnellbefehl hinzufügen", "3220e2da27": "Dieser Schnellbefehl wird aus der Liste gespeicherter Befehle entfernt.", - "e8e1a52edb": "„{{value0}}“ löschen?", "37e1bb90ce": "Ausführen: {{value0}}", "77ac113df0": "{{value0}} starten: {{value1}}", "7b1c9d6ae1": "Ausführen", @@ -5809,7 +5806,6 @@ "loading": "Inhalt wird geladen…", "deleteFailed": "Orca konnte dieses Element nicht aus der Cloud löschen.", "deleted": "Aus der Cloud gelöscht", - "confirmDelete": "Löschen bestätigen", "moreActions": "Weitere Aktionen für {{name}}", "deletePackage": "Aus der Cloud löschen" }, @@ -6076,8 +6072,6 @@ "5cc1a6701c": "Einstellungen öffnen", "2b56b35f53": "Dies lässt sich in den Einstellungen ändern.", "dd3a45bbbd": "Diese Bestätigung wird beim nächsten Mal übersprungen.", - "fc23c4cbdf": "Arbeitsbereich löschen", - "86f0ae1257": "Arbeitsbereiche löschen", "removeFolderProjectInstead": "Das Ordnerprojekt entfernen, statt diesen Arbeitsbereich zu löschen.", "gitBlocksMainWorktree": "Git erlaubt das Entfernen des Haupt-Worktrees nicht.", "removeBatchFolder_one": "{{count}} Arbeitsbereich aus Orca entfernen. Projektordner auf dem Datenträger werden nicht gelöscht.", @@ -6183,11 +6177,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "Abbrechen", - "591f330288": "Projektgruppe löschen", "2c14ce677a": "Wird gelöscht…", "0e0e6764af": "Enthaltene Projekte", "55f75628c0": "Projektordner auf dem Datenträger werden nicht gelöscht.", - "fec7e9c8ae": "Gruppe löschen", "removeContainedProjects_one": "{{count}} enthaltenes Projekt entfernen", "removeContainedProjects_other": "{{count}} enthaltene Projekte entfernen", "deleteGroupDescription": "{{name}} löschen.", @@ -6914,9 +6906,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "Abbrechen", "cf95e3b5bb": "Schließen", - "deleteWorkspace": "Arbeitsbereich löschen", "deleting": "Wird gelöscht…", - "forceDelete": "Löschen erzwingen", "forceDeleting": "Löschen wird erzwungen…" }, "HostRemoveDialog": { @@ -7182,15 +7172,12 @@ } }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "Beibehaltene Branches prüfen", "f21976c9a8": "Lokale Branches auswählen, deren Löschung erzwungen werden soll. Nicht ausgewählte Branches bleiben in ihren Repositorys.", "38c947f7c5": "Alle auswählen", "9602129d38": "{{value0}} von {{value1}} ausgewählt", "ee39e872d5": "Möglicherweise nicht gemergt", "676db406fd": "HEAD nicht verfügbar", - "285e1e4882": "Abbrechen", - "a0f9863597_one": "Löschen von {{count}} Branch erzwingen", - "a0f9863597_other": "Löschen von {{count}} Branches erzwingen" + "285e1e4882": "Abbrechen" }, "WorktreeListScrollToTopButton": { "jumpToTop": "Nach oben springen" @@ -8572,7 +8559,6 @@ "c6b155911b": "Alle Befehle", "c36912efd5": "Über die Schaltfläche „Schnellbefehle“ in der Tab-Leiste oder per Rechtsklick in einem beliebigen Terminal ausführen.", "3d9dc558e8": "Dieser Schnellbefehl wird aus der Liste gespeicherter Befehle entfernt.", - "3edf3deaf8": "„{{value0}}“ löschen?", "9fcfc29519": "Einfügen", "9b3e338d62": "Enter", "4ccc63da87": "Agent", @@ -13495,7 +13481,6 @@ "abf59262fb": "Vor dem Start eines Agenten die vollständige Befehlseingabe prüfen und bearbeiten.", "4ede779461": "Review-Konflikte mit KI lösen", "3b203c62f8": "Der Kommentar wird dauerhaft aus dem PR entfernt.", - "ea9b649ce3": "Kommentar löschen?", "5788d1059d": "Der Review-Thread konnte nicht aktualisiert werden. Das GitHub-API-Kontingent prüfen.", "07871c0589": "Weiteren PR verknüpfen", "7f4489f370": "Aktualisieren", @@ -14377,8 +14362,7 @@ "5ddd8cac7f": "Alle vorgemerkten Änderungen verwerfen?", "d4df3a61df": "Änderungen an „{{value0}}“ verwerfen?", "5c0bdbc4cb": "„{{value0}}“ wiederherstellen?", - "d97bf697c9": "Dadurch wird diese Datei dauerhaft gelöscht. Dies kann nicht rückgängig gemacht werden.", - "96c772bee9": "„{{value0}}“ löschen?" + "d97bf697c9": "Dadurch wird diese Datei dauerhaft gelöscht. Dies kann nicht rückgängig gemacht werden." }, "dialog": { "3bc61dc989": "Abbrechen", @@ -14615,7 +14599,6 @@ "unresolved": "Der zuständige Host für diesen Arbeitsbereich konnte nicht ermittelt werden. Verbindung prüfen und erneut versuchen." }, "useFileDeletion": { - "d979a4fbb5": "„{{value0}}“ dauerhaft löschen?", "92276aceb7": "Löschen", "7fb9435c86": "Das Verzeichnis und sein Inhalt werden auf dem Remote-Host dauerhaft gelöscht. Dies kann nicht rückgängig gemacht werden.", "23e98f192f": "Die Datei wird auf dem Remote-Host dauerhaft gelöscht. Dies kann nicht rückgängig gemacht werden.", @@ -14804,7 +14787,6 @@ "resumeInNewCliTooltipChat": "Dieser Chat bleibt unverändert." }, "AiVaultSessionDeleteDialog": { - "title": "Diese Sitzung löschen?", "description": "„{{value0}}“ wird gelöscht. Danach kann die Sitzung auch über die eigene Befehlszeile von {{value1}} nicht mehr fortgesetzt werden.", "remoteDescription": "„{{value0}}“ wird von {{value2}} gelöscht. Danach kann die Sitzung auch über die eigene Befehlszeile von {{value1}} nicht mehr fortgesetzt werden.", "confirm": "Löschen" @@ -17714,9 +17696,7 @@ "2faecab10b": "Jetzt ausführen", "13118faadf": "Unbekanntes Projekt", "73f630b49d": "Abbrechen", - "9adfab2596": "Externe Automatisierung löschen", "1e2e41392f": "Nicht erneut fragen", - "080dcb5fbb": "Automatisierung löschen", "19a6e30eae": "Automatisierungen aktualisieren", "77c2778945": "Automatisierungen", "e1bf9b1512": "Der Arbeitsbereich ist nicht verfügbar.", @@ -18636,7 +18616,6 @@ "ArtifactsPage": { "signInAgain": "Erneut bei Orca anmelden, um Artefakte zu laden.", "loadFailed": "Artefakte konnten nicht geladen werden.", - "deleteTitle": "Artefakt löschen?", "deleteDescription": "„{{name}}“ wird unter seinem öffentlichen Link nicht mehr verfügbar sein.", "delete": "Löschen", "deleteFailed": "Das Artefakt konnte nicht gelöscht werden.", @@ -20540,11 +20519,8 @@ "candidateName": "{{value0}} auf {{value1}}" }, "stopAgents": { - "titleMany": "Diese Agenten stoppen?", "descriptionOne": "Beim Löschen wird die aktuelle Arbeit des Agenten gestoppt.", - "descriptionMany": "Beim Löschen wird die aktuelle Arbeit dieser Agenten gestoppt.", - "confirm": "Agenten stoppen und löschen", - "confirmOne": "Den Agenten stoppen und löschen" + "descriptionMany": "Beim Löschen wird die aktuelle Arbeit dieser Agenten gestoppt." }, "candidateRow": { "gitChip": { @@ -21233,10 +21209,7 @@ "unstagedSelectionIncludingUntrackedDescription": "Nicht vorgemerkte Änderungen werden zurückgesetzt und alle nicht versionierten Dateien in dieser Auswahl dauerhaft gelöscht. Dies kann nicht rückgängig gemacht werden.", "unstagedFilesIncludingUntrackedDescription_other": "Nicht vorgemerkte Änderungen werden zurückgesetzt und alle nicht versionierten Dateien unter diesen {{count}} Dateien dauerhaft gelöscht. Dies kann nicht rückgängig gemacht werden.", "delete": "Löschen", - "deleteCount": "{{count}} löschen", "deleteUntrackedFilesDescription_one": "{{count}} nicht versionierte Datei wird dauerhaft gelöscht. Dies kann nicht rückgängig gemacht werden.", - "deleteUntrackedTitle_one": "{{count}} nicht versionierte Datei löschen?", - "deleteUntrackedTitle_other": "{{count}} nicht versionierte Dateien löschen?", "discard": "Verwerfen", "discardAll": "Alle verwerfen", "restore": "Wiederherstellen", @@ -21712,7 +21685,8 @@ }, "common": { "retry": "Erneut versuchen", - "cancel": "Abbrechen" + "cancel": "Abbrechen", + "delete": "Löschen" }, "terminal": { "sessionSaveFailure": { @@ -21837,8 +21811,7 @@ "trashFailed": { "titleRecycleBin": "„{{name}}“ konnte nicht in den Papierkorb verschoben werden. Endgültig löschen?", "titleTrash": "„{{name}}“ konnte nicht in den Papierkorb verschoben werden. Endgültig löschen?", - "description": "Dies kann nicht rückgängig gemacht werden.", - "confirm": "Endgültig löschen" + "description": "Dies kann nicht rückgängig gemacht werden." } }, "sparsePreset": { @@ -22047,7 +22020,6 @@ "nestedRemoval": { "refreshFailed": "Die Liste der Arbeitsbereiche konnte nicht aktualisiert werden", "review": "Mit verschachtelten Worktrees löschen…", - "title": "Arbeitsbereich und verschachtelte Worktrees löschen?", "description": "Dadurch werden „{{name}}“, alle unten aufgeführten verschachtelten Worktrees und alle darin enthaltenen Dateien einschließlich nicht committeter Änderungen endgültig gelöscht. Ihre Terminals und Agenten werden gestoppt.", "detached": "Losgelöster HEAD", "loading": "Verschachtelte Worktrees werden überprüft…", @@ -22055,7 +22027,6 @@ "cancel": "Abbrechen", "retry": "Erneut überprüfen", "deleting": "Wird gelöscht…", - "confirm": "Alle aufgeführten Worktrees löschen", "preservedBranches": "Einige Branches verschachtelter Worktrees wurden beibehalten" }, "setupTerminalTitle": "Einrichtung", diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 2d0c1501784..0b43fa80a27 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -926,9 +926,7 @@ "importInvalid": "{{count}} unreadable rows", "importUndecryptable": "{{count}} protected by the browser; export a CSV from it instead", "imported": "Imported {{count}} passwords", - "deleteAllConfirmTitle": "Delete all password data?", "deleteAllConfirmDescription": "Every saved password in the {{profile}} profile is permanently deleted. This can’t be undone.", - "deleteAll": "Delete data", "deletedAll": "Password data deleted", "form": { "invalidSite": "Enter a website address, like example.com.", @@ -968,7 +966,6 @@ "editFor": "Edit {{account}}", "moreActions": "More actions for {{account}}", "accountCount": "{{count}} accounts", - "deleteConfirmTitle": "Delete the {{site}} password for {{username}}?", "deleteConfirmDescription": "Orca won’t fill it anymore. This can’t be undone.", "deleteConfirm": "Delete", "profileDescription": "Saved logins for the {{profile}} profile. Orca fills one only when you pick it under a sign-in field.", @@ -4392,7 +4389,6 @@ "7eee951968": "Scanning workspaces", "191f0bc98e": "Close", "7ae2ad30f4": "Refresh", - "b2c1331844": "Delete Inactive Workspaces", "7f451a3e2c": "Could not ignore cleanup suggestion", "662b8ec3f8": "Workspace cleanup scan failed", "9cc26c019d": "Remove", @@ -4402,7 +4398,6 @@ "1d3503357d": "You can close this and come back while deletion continues.", "4c2990886e": "{{value0}}/{{value1}} deleted", "86ba852118": "{{value0}}, {{value1}} failed", - "deleteButtonCount": "Delete {{value0}}", "archivedStatus": "Archived", "mergedStatus": "Merged", "unchangedStatus": "No new commits", @@ -5005,7 +5000,6 @@ "1d411fb6a5": "Save a quick command for this repo", "8f1e971966": "Add quick command", "3220e2da27": "This quick command will be removed from your saved list.", - "e8e1a52edb": "Delete \"{{value0}}\"?", "37e1bb90ce": "Run: {{value0}}", "77ac113df0": "Start {{value0}}: {{value1}}", "7b1c9d6ae1": "Run", @@ -6416,7 +6410,6 @@ "loading": "Loading contents…", "deleteFailed": "Orca could not delete this from the Cloud.", "deleted": "Deleted from the Cloud", - "confirmDelete": "Confirm delete", "moreActions": "More actions for {{name}}", "deletePackage": "Delete from the Cloud" }, @@ -6695,8 +6688,6 @@ "5cc1a6701c": "Open Settings", "2b56b35f53": "You can change this in Settings.", "dd3a45bbbd": "We'll skip this confirmation next time.", - "fc23c4cbdf": "Delete Workspace", - "86f0ae1257": "Delete Workspaces", "removeFolderProjectInstead": "Remove the folder project instead of deleting this workspace.", "gitBlocksMainWorktree": "Git does not allow removing the main worktree.", "removeBatchFolder_one": "Remove {{count}} workspace from Orca. Project folders on disk will not be deleted.", @@ -6803,11 +6794,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "Cancel", - "591f330288": "Delete Project Group", "2c14ce677a": "Deleting...", "0e0e6764af": "Contained projects", "55f75628c0": "Project folders on disk are not deleted.", - "fec7e9c8ae": "Delete Group", "removeContainedProjects_one": "Remove {{count}} contained project", "removeContainedProjects_other": "Remove {{count}} contained projects", "deleteGroupDescription": "Delete {{name}}.", @@ -7560,9 +7549,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "Cancel", "cf95e3b5bb": "Close", - "deleteWorkspace": "Delete Workspace", "deleting": "Deleting...", - "forceDelete": "Force Delete", "forceDeleting": "Force Deleting..." }, "HostRemoveDialog": { @@ -7836,16 +7823,12 @@ } }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "Review kept branches", "f21976c9a8": "Select the local branches you want to force delete. Unselected branches stay in their repositories.", "38c947f7c5": "Select all", "9602129d38": "{{value0}} of {{value1}} selected", "ee39e872d5": "May be unmerged", "676db406fd": "Head unavailable", - "285e1e4882": "Cancel", - "a0f9863597_one": "Force Delete {{count}} Branch", - "a0f9863597_other": "Force Delete {{count}} Branches", - "a0f9863597": "Force Delete {{count}} Branches" + "285e1e4882": "Cancel" }, "WorktreeListScrollToTopButton": { "jumpToTop": "Jump to top" @@ -9254,7 +9237,6 @@ "c6b155911b": "All commands", "c36912efd5": "Run them from the Quick Commands button in the tab bar, or right-click inside any terminal.", "3d9dc558e8": "This quick command will be removed from your saved list.", - "3edf3deaf8": "Delete \"{{value0}}\"?", "9fcfc29519": "Insert", "9b3e338d62": "Enter", "4ccc63da87": "Agent", @@ -14340,7 +14322,6 @@ "abf59262fb": "Review and edit the full command input before starting an agent.", "4ede779461": "Resolve Review Conflicts With AI", "3b203c62f8": "This will permanently remove the comment from the PR.", - "ea9b649ce3": "Delete comment?", "5788d1059d": "Could not update review thread. Check the GitHub API budget.", "07871c0589": "Link another PR", "7f4489f370": "Refresh", @@ -15229,8 +15210,7 @@ "5ddd8cac7f": "Discard all staged changes?", "d4df3a61df": "Discard changes to \"{{value0}}\"?", "5c0bdbc4cb": "Restore \"{{value0}}\"?", - "d97bf697c9": "This will permanently delete this file. This cannot be undone.", - "96c772bee9": "Delete \"{{value0}}\"?" + "d97bf697c9": "This will permanently delete this file. This cannot be undone." }, "dialog": { "3bc61dc989": "Cancel", @@ -15488,7 +15468,6 @@ "unresolved": "Couldn't determine which host owns this workspace. Check the connection and try again." }, "useFileDeletion": { - "d979a4fbb5": "Permanently delete '{{value0}}'?", "92276aceb7": "Delete", "7fb9435c86": "This permanently deletes the directory and its contents on the remote host. This cannot be undone.", "23e98f192f": "This permanently deletes the file on the remote host. This cannot be undone.", @@ -15677,7 +15656,6 @@ "resumeInNewCliTooltipChat": "This chat stays unchanged." }, "AiVaultSessionDeleteDialog": { - "title": "Delete this session?", "description": "\"{{value0}}\" will be deleted. Once deleted, it will no longer be resumable from {{value1}}'s own command line either.", "remoteDescription": "\"{{value0}}\" will be deleted from {{value2}}. Once deleted, it will no longer be resumable from {{value1}}'s own command line either.", "confirm": "Delete" @@ -18673,9 +18651,7 @@ "2faecab10b": "Run Now", "13118faadf": "Unknown project", "73f630b49d": "Cancel", - "9adfab2596": "Delete External Automation", "1e2e41392f": "Don't ask again", - "080dcb5fbb": "Delete Automation", "19a6e30eae": "Refresh automations", "77c2778945": "Automations", "e1bf9b1512": "Workspace is not available.", @@ -19618,7 +19594,6 @@ "ArtifactsPage": { "signInAgain": "Sign in to Orca again to load artifacts.", "loadFailed": "Could not load artifacts.", - "deleteTitle": "Delete artifact?", "deleteDescription": "“{{name}}” will no longer be available at its public link.", "delete": "Delete", "deleteFailed": "Could not delete the artifact.", @@ -21800,11 +21775,8 @@ "candidateName": "{{value0}} on {{value1}}" }, "stopAgents": { - "titleMany": "Stop these agents?", "descriptionOne": "Deleting will stop the agent's current work.", - "descriptionMany": "Deleting will stop these agents' current work.", - "confirm": "Stop agents and delete", - "confirmOne": "Stop agent and delete" + "descriptionMany": "Deleting will stop these agents' current work." }, "candidateRow": { "gitChip": { @@ -21819,9 +21791,7 @@ }, "confirm": { "deletingTitle_one": "Deleting {{count}} workspace", - "deletingTitle_other": "Deleting {{count}} workspaces", - "deleteTitle_one": "Delete {{count}} workspace?", - "deleteTitle_other": "Delete {{count}} workspaces?" + "deletingTitle_other": "Deleting {{count}} workspaces" } }, "creation": { @@ -22719,9 +22689,6 @@ "restore": "Restore", "discard": "Discard", "discardAll": "Discard all", - "deleteUntrackedTitle_one": "Delete {{count}} untracked file?", - "deleteUntrackedTitle_other": "Delete {{count}} untracked files?", - "deleteCount": "Delete {{count}}", "deleteUntrackedFilesDescription_one": "This will permanently delete {{count}} untracked file. This cannot be undone.", "unstagedFilesIncludingUntrackedDescription_one": "This will revert unstaged changes and permanently delete any untracked files among {{count}} file. This cannot be undone." }, @@ -23202,7 +23169,8 @@ "common": { "retry": "Retry", "cancel": "Cancel", - "unassigned": "Unassigned" + "unassigned": "Unassigned", + "delete": "Delete" }, "terminal": { "sessionSaveFailure": { @@ -23346,8 +23314,7 @@ "trashFailed": { "titleRecycleBin": "Couldn't move '{{name}}' to the Recycle Bin. Delete it permanently?", "titleTrash": "Couldn't move '{{name}}' to the Trash. Delete it permanently?", - "description": "This cannot be undone.", - "confirm": "Delete Permanently" + "description": "This cannot be undone." } }, "sparsePreset": { @@ -23599,7 +23566,6 @@ "nestedRemoval": { "refreshFailed": "Could not refresh the workspace list", "review": "Delete with nested worktrees…", - "title": "Delete workspace and nested worktrees?", "description": "This permanently deletes \"{{name}}\", every nested worktree listed below, and all files inside them, including uncommitted changes. Their terminals and agents will be stopped.", "detached": "Detached HEAD", "loading": "Checking nested worktrees…", @@ -23607,7 +23573,6 @@ "cancel": "Cancel", "retry": "Review again", "deleting": "Deleting…", - "confirm": "Delete all listed worktrees", "preservedBranches": "Some nested worktree branches were kept" }, "setupTerminalTitle": "Setup", diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json index 123288c7a1c..b5e69c8f6db 100644 --- a/src/renderer/src/i18n/locales/es.json +++ b/src/renderer/src/i18n/locales/es.json @@ -3303,7 +3303,6 @@ "7eee951968": "Verificando seguridad del espacio de trabajo", "191f0bc98e": "Cerrar", "7ae2ad30f4": "Actualizar", - "b2c1331844": "Eliminar espacios de trabajo inactivos", "7f451a3e2c": "No se pudo ignorar la sugerencia de limpieza", "662b8ec3f8": "Falló el escaneo de limpieza del espacio de trabajo", "9cc26c019d": "Eliminar", @@ -3313,7 +3312,6 @@ "1d3503357d": "Puedes cerrar esto y volver mientras la eliminación continúa.", "4c2990886e": "{{value0}}/{{value1}} eliminados", "86ba852118": "{{value0}}, {{value1}} fallidos", - "deleteButtonCount": "Eliminar {{value0}}", "archivedStatus": "Archivado", "74f6c16279": "Atrás" }, @@ -3906,7 +3904,6 @@ "1d411fb6a5": "Guarda un comando rápido para este repositorio", "8f1e971966": "Agregar comando rápido", "3220e2da27": "Este comando rápido se eliminará de tu lista guardada.", - "e8e1a52edb": "¿Eliminar \"{{value0}}\"?", "37e1bb90ce": "Ejecutar: {{value0}}", "77ac113df0": "Iniciar {{value0}}: {{value1}}", "7b1c9d6ae1": "Ejecutar", @@ -5365,7 +5362,6 @@ "loading": "Cargando contenidos…", "deleteFailed": "Orca no pudo eliminar esto de la nube.", "deleted": "Eliminado de la nube", - "confirmDelete": "Confirmar eliminación", "moreActions": "Otras acciones para {{name}}", "deletePackage": "Eliminar de la nube" }, @@ -5621,8 +5617,6 @@ "5cc1a6701c": "Abrir Ajustes", "2b56b35f53": "Puedes cambiar esto en Ajustes.", "dd3a45bbbd": "Omitiremos esta confirmación la próxima vez.", - "fc23c4cbdf": "Eliminar espacio de trabajo", - "86f0ae1257": "Eliminar espacios de trabajo", "removeFolderProjectInstead": "Quita el proyecto de carpeta en lugar de eliminar este espacio de trabajo.", "gitBlocksMainWorktree": "Git no permite quitar el worktree principal.", "removeBatchFolder_one": "Eliminar {{count}} espacio de trabajo de Orca. Las carpetas de los proyectos en disco no se borrarán.", @@ -5742,11 +5736,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "Cancelar", - "591f330288": "Eliminar grupo de proyectos", "2c14ce677a": "Eliminando...", "0e0e6764af": "Proyectos contenidos", "55f75628c0": "Las carpetas de los proyectos en el disco no se eliminan.", - "fec7e9c8ae": "Eliminar grupo", "removeContainedProjects_one": "Quitar {{count}} proyecto del grupo", "removeContainedProjects_other": "Quitar {{count}} proyectos del grupo", "removeContainedProjects_many": "Quitar {{count}} proyectos del grupo", @@ -6492,9 +6484,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "Cancelar", "cf95e3b5bb": "Cerrar", - "deleteWorkspace": "Eliminar espacio de trabajo", "deleting": "Eliminando...", - "forceDelete": "Forzar eliminación", "forceDeleting": "Forzando eliminación..." }, "LinearAgentSkillSetupPrompt": { @@ -6813,17 +6803,12 @@ } }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "Revisar sucursales mantenidas", "f21976c9a8": "Seleccione las sucursales locales que desea forzar la eliminación. Las sucursales no seleccionadas permanecen en sus repositorios.", "38c947f7c5": "Seleccionar todo", "9602129d38": "{{value0}} de {{value1}} seleccionado", "ee39e872d5": "Puede estar desfusionado", "676db406fd": "Cabeza no disponible", - "285e1e4882": "Cancelar", - "a0f9863597_one": "Forzar eliminación de rama {{count}}", - "a0f9863597_other": "Forzar eliminación de ramas {{count}}", - "a0f9863597_many": "Forzar eliminación de ramas {{count}}", - "a0f9863597": "Forzar eliminación de ramas {{count}}" + "285e1e4882": "Cancelar" }, "WorktreeListScrollToTopButton": { "jumpToTop": "Saltar hacia arriba" @@ -8143,7 +8128,6 @@ "c6b155911b": "Todos los comandos", "c36912efd5": "Ejecútalos desde el botón Comandos rápidos en la barra de pestañas o haz clic derecho dentro de cualquier terminal.", "3d9dc558e8": "Este comando rápido se eliminará de tu lista guardada.", - "3edf3deaf8": "¿Eliminar \"{{value0}}\"?", "9fcfc29519": "Insertar", "9b3e338d62": "Enter", "4ccc63da87": "Agente", @@ -13144,7 +13128,6 @@ "abf59262fb": "Revisa y edita la entrada de comando completa antes de iniciar un agente.", "4ede779461": "Resolver conflictos de revisión con IA", "3b203c62f8": "Esto eliminará permanentemente el comentario del PR.", - "ea9b649ce3": "¿Eliminar comentario?", "5788d1059d": "No se pudo actualizar el hilo de revisión. Revisa el presupuesto de la API de GitHub.", "07871c0589": "Vincular otro PR", "7f4489f370": "Actualizar", @@ -14047,8 +14030,7 @@ "5ddd8cac7f": "¿Descartar todos los cambios staged?", "d4df3a61df": "¿Descartar cambios en \"{{value0}}\"?", "5c0bdbc4cb": "¿Restaurar \"{{value0}}\"?", - "d97bf697c9": "Esto eliminará permanentemente este archivo. Esto no se puede deshacer.", - "96c772bee9": "¿Eliminar \"{{value0}}\"?" + "d97bf697c9": "Esto eliminará permanentemente este archivo. Esto no se puede deshacer." }, "dialog": { "3bc61dc989": "Cancelar", @@ -14294,7 +14276,6 @@ "unresolved": "No se pudo determinar qué host posee este espacio de trabajo. Comprueba la conexión e inténtalo de nuevo." }, "useFileDeletion": { - "d979a4fbb5": "¿Eliminar '{{value0}}' permanentemente?", "92276aceb7": "Eliminar", "7fb9435c86": "Esto elimina permanentemente el directorio y su contenido en el host remoto. Esto no se puede deshacer.", "23e98f192f": "Esto elimina permanentemente el archivo en el host remoto. Esto no se puede deshacer.", @@ -14491,7 +14472,6 @@ "resumeInNewCliTooltipChat": "Este chat permanece sin cambios." }, "AiVaultSessionDeleteDialog": { - "title": "¿Eliminar esta sesión?", "description": "\"{{value0}}\" se eliminará. Una vez eliminada, tampoco podrá reanudarse desde la línea de comandos propia de {{value1}}.", "remoteDescription": "\"{{value0}}\" se eliminará de {{value2}}. Una vez eliminada, tampoco se podrá reanudar desde la línea de comandos de {{value1}}.", "confirm": "Eliminar" @@ -17447,9 +17427,7 @@ "2faecab10b": "Ejecutar ahora", "13118faadf": "Proyecto desconocido", "73f630b49d": "Cancelar", - "9adfab2596": "Eliminar automatización externa", "1e2e41392f": "no vuelvas a preguntar", - "080dcb5fbb": "Eliminar automatización", "19a6e30eae": "Actualizar automatizaciones", "77c2778945": "Automatizaciones", "e1bf9b1512": "El espacio de trabajo no está disponible.", @@ -18340,7 +18318,6 @@ }, "artifacts": { "ArtifactsPage": { - "deleteTitle": "¿Eliminar el artefacto?", "deleteDescription": "“{{name}}” ya no estará disponible en su enlace público.", "delete": "Eliminar", "title": "Artefactos", @@ -20348,9 +20325,6 @@ }, "stopAgents": { "descriptionOne": "La eliminación detendrá el trabajo actual del agente.", - "titleMany": "¿Detener a estos agentes?", - "confirm": "Detener agentes y eliminar", - "confirmOne": "Detener agente y eliminar", "descriptionMany": "La eliminación detendrá el trabajo actual de estos agentes." }, "candidateRow": { @@ -21401,8 +21375,7 @@ "trashFailed": { "titleRecycleBin": "No se pudo mover '{{name}}' a la Papelera de reciclaje. ¿Eliminarlo permanentemente?", "titleTrash": "No se pudo mover '{{name}}' a la Papelera. ¿Eliminarlo permanentemente?", - "description": "Esto no se puede deshacer.", - "confirm": "Eliminar permanentemente" + "description": "Esto no se puede deshacer." } }, "sparsePreset": { @@ -21910,7 +21883,8 @@ }, "common": { "retry": "Reintentar", - "cancel": "Cancelar" + "cancel": "Cancelar", + "delete": "Borrar" }, "terminal": { "codexTerminalServerIsolationNotice": { @@ -22054,10 +22028,6 @@ "restore": "Restaurar", "discard": "Descartar", "discardAll": "Descartar todo", - "deleteUntrackedTitle_one": "¿Eliminar {{count}} archivo sin seguimiento?", - "deleteUntrackedTitle_other": "¿Eliminar {{count}} archivos sin seguimiento?", - "deleteUntrackedTitle_many": "¿Eliminar {{count}} archivos sin seguimiento?", - "deleteCount": "Eliminar {{count}}", "deleteUntrackedFilesDescription_one": "Esto eliminará permanentemente {{count}} archivo sin seguimiento. No se puede deshacer.", "unstagedFilesIncludingUntrackedDescription_one": "Esto revertirá los cambios no preparados de {{count}} archivo y lo eliminará permanentemente si no tiene seguimiento. No se puede deshacer." }, @@ -22507,7 +22477,6 @@ "nestedRemoval": { "refreshFailed": "No se pudo actualizar la lista de espacios de trabajo", "review": "Eliminar con worktrees anidados…", - "title": "¿Eliminar el espacio de trabajo y los worktrees anidados?", "description": "Esto elimina de forma permanente \"{{name}}\", todos los worktrees anidados que se indican a continuación y todos los archivos que contienen, incluidos los cambios sin confirmar. Se detendrán sus terminales y agentes.", "detached": "HEAD desacoplado", "loading": "Comprobando worktrees anidados…", @@ -22515,7 +22484,6 @@ "cancel": "Cancelar", "retry": "Revisar de nuevo", "deleting": "Eliminando…", - "confirm": "Eliminar todos los worktrees indicados", "preservedBranches": "Se conservaron algunas ramas de worktrees anidados" }, "setupTerminalTitle": "Configuración", diff --git a/src/renderer/src/i18n/locales/fr.json b/src/renderer/src/i18n/locales/fr.json index 2e6d846e090..adbc27e17f3 100644 --- a/src/renderer/src/i18n/locales/fr.json +++ b/src/renderer/src/i18n/locales/fr.json @@ -3574,7 +3574,6 @@ "7eee951968": "Analyse des espaces de travail", "191f0bc98e": "Fermer", "7ae2ad30f4": "Actualiser", - "b2c1331844": "Supprimer les espaces de travail inactifs", "7f451a3e2c": "Impossible d'ignorer la suggestion de nettoyage", "662b8ec3f8": "Échec du scan de nettoyage des espaces de travail", "9cc26c019d": "Supprimer", @@ -3584,7 +3583,6 @@ "1d3503357d": "Vous pouvez fermer cette fenêtre et revenir pendant que la suppression continue.", "4c2990886e": "{{value0}}/{{value1}} supprimés", "86ba852118": "{{value0}}, {{value1}} en échec", - "deleteButtonCount": "Supprimer {{value0}}", "archivedStatus": "Archivé", "74f6c16279": "Retour" }, @@ -4177,7 +4175,6 @@ "1d411fb6a5": "Enregistrer une commande rapide pour ce dépôt", "8f1e971966": "Ajouter une commande rapide", "3220e2da27": "Cette commande rapide sera retirée de votre liste enregistrée.", - "e8e1a52edb": "Supprimer « {{value0}} » ?", "37e1bb90ce": "Exécuter : {{value0}}", "77ac113df0": "Démarrer {{value0}} : {{value1}}", "7b1c9d6ae1": "Exécution", @@ -5636,7 +5633,6 @@ "loading": "Chargement du contenu…", "deleteFailed": "Orca n'a pas pu supprimer cet élément du Cloud.", "deleted": "Supprimé du Cloud", - "confirmDelete": "Confirmer la suppression", "moreActions": "Autres actions pour {{name}}", "deletePackage": "Supprimer du Cloud" }, @@ -5907,8 +5903,6 @@ "5cc1a6701c": "Ouvrir les paramètres", "2b56b35f53": "Vous pouvez changer cela dans les paramètres.", "dd3a45bbbd": "Cette confirmation sera ignorée la prochaine fois.", - "fc23c4cbdf": "Supprimer l'espace de travail", - "86f0ae1257": "Supprimer les espaces de travail", "removeFolderProjectInstead": "Retirez le projet de dossier au lieu de supprimer cet espace de travail.", "gitBlocksMainWorktree": "Git ne permet pas de retirer le worktree principal.", "removeBatchFolder_one": "Retirer {{count}} espace de travail d'Orca. Les dossiers des projets sur le disque ne seront pas supprimés.", @@ -6028,11 +6022,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "Annuler", - "591f330288": "Supprimer le groupe de projets", "2c14ce677a": "Suppression...", "0e0e6764af": "Projets contenus", "55f75628c0": "Les dossiers de projets sur le disque ne sont pas supprimés.", - "fec7e9c8ae": "Supprimer le groupe", "removeContainedProjects_one": "Retirer {{count}} projet du groupe", "removeContainedProjects_other": "Retirer {{count}} projets du groupe", "removeContainedProjects_many": "Retirer {{count}} projets du groupe", @@ -6759,9 +6751,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "Annuler", "cf95e3b5bb": "Fermer", - "deleteWorkspace": "Supprimer l'espace de travail", "deleting": "Suppression...", - "forceDelete": "Forcer la suppression", "forceDeleting": "Suppression forcée..." }, "HostRemoveDialog": { @@ -7044,17 +7034,12 @@ } }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "Examiner les branches conservées", "f21976c9a8": "Sélectionnez les branches locales à supprimer de force. Les branches non sélectionnées restent dans leurs dépôts.", "38c947f7c5": "Tout sélectionner", "9602129d38": "{{value0}} sur {{value1}} sélectionnées", "ee39e872d5": "Possiblement non fusionnée", "676db406fd": "Head indisponible", - "285e1e4882": "Annuler", - "a0f9863597_one": "Forcer la suppression de {{count}} branche", - "a0f9863597_other": "Forcer la suppression de {{count}} branches", - "a0f9863597_many": "Forcer la suppression de {{count}} branches", - "a0f9863597": "Forcer la suppression de {{count}} branches" + "285e1e4882": "Annuler" }, "WorktreeListScrollToTopButton": { "jumpToTop": "Remonter en haut" @@ -8456,7 +8441,6 @@ "c6b155911b": "Toutes les commandes", "c36912efd5": "Exécutez-les depuis le bouton Commandes rapides de la barre d'onglets, ou faites un clic droit dans n'importe quel terminal.", "3d9dc558e8": "Cette commande rapide sera retirée de votre liste enregistrée.", - "3edf3deaf8": "Supprimer « {{value0}} » ?", "9fcfc29519": "Insérer", "9b3e338d62": "Enter", "4ccc63da87": "Agent", @@ -13415,7 +13399,6 @@ "abf59262fb": "Vérifiez et modifiez l'entrée de commande complète avant de démarrer un agent.", "4ede779461": "Résoudre les conflits de revue avec l'IA", "3b203c62f8": "Le commentaire sera définitivement retiré de la PR.", - "ea9b649ce3": "Supprimer le commentaire ?", "5788d1059d": "Impossible de mettre à jour le fil de revue. Vérifiez le budget de l'API GitHub.", "07871c0589": "Lier une autre PR", "7f4489f370": "Actualiser", @@ -14318,8 +14301,7 @@ "5ddd8cac7f": "Abandonner toutes les modifications indexées ?", "d4df3a61df": "Abandonner les modifications de « {{value0}} » ?", "5c0bdbc4cb": "Restaurer « {{value0}} » ?", - "d97bf697c9": "Cela supprimera définitivement ce fichier. Cette action est irréversible.", - "96c772bee9": "Supprimer « {{value0}} » ?" + "d97bf697c9": "Cela supprimera définitivement ce fichier. Cette action est irréversible." }, "dialog": { "3bc61dc989": "Annuler", @@ -14565,7 +14547,6 @@ "unresolved": "Impossible de déterminer quel hôte possède cet espace de travail. Vérifiez la connexion et réessayez." }, "useFileDeletion": { - "d979a4fbb5": "Supprimer définitivement « {{value0}} » ?", "92276aceb7": "Supprimer", "7fb9435c86": "Cette action supprime définitivement le répertoire et son contenu sur l'hôte distant. Elle est irréversible.", "23e98f192f": "Cette action supprime définitivement le fichier sur l'hôte distant. Elle est irréversible.", @@ -14762,7 +14743,6 @@ "resumeInNewCliTooltipChat": "Ce chat reste inchangé." }, "AiVaultSessionDeleteDialog": { - "title": "Supprimer cette session ?", "description": "« {{value0}} » sera supprimée. Une fois supprimée, elle ne pourra plus être reprise depuis la ligne de commande de {{value1}} non plus.", "remoteDescription": "« {{value0}} » sera supprimée de {{value2}}. Une fois supprimée, elle ne pourra plus être reprise depuis la ligne de commande de {{value1}} non plus.", "confirm": "Supprimer" @@ -17691,9 +17671,7 @@ "2faecab10b": "Exécuter maintenant", "13118faadf": "Projet inconnu", "73f630b49d": "Annuler", - "9adfab2596": "Supprimer l'automatisation externe", "1e2e41392f": "Ne plus demander", - "080dcb5fbb": "Supprimer l'automatisation", "19a6e30eae": "Actualiser les automatisations", "77c2778945": "Automatisations", "e1bf9b1512": "L'espace de travail n'est pas disponible.", @@ -18636,7 +18614,6 @@ "ArtifactsPage": { "signInAgain": "Reconnectez-vous à Orca pour charger les artifacts.", "loadFailed": "Impossible de charger les artifacts.", - "deleteTitle": "Supprimer l'artifact ?", "deleteDescription": "« {{name}} » ne sera plus disponible via son lien public.", "delete": "Supprimer", "deleteFailed": "Impossible de supprimer l'artifact.", @@ -20620,11 +20597,8 @@ "candidateName": "{{value0}} sur {{value1}}" }, "stopAgents": { - "titleMany": "Arrêter ces agents ?", "descriptionOne": "La suppression interrompra le travail en cours de l'agent.", - "descriptionMany": "La suppression interrompra le travail en cours de ces agents.", - "confirm": "Arrêter les agents et supprimer", - "confirmOne": "Arrêter l'agent et supprimer" + "descriptionMany": "La suppression interrompra le travail en cours de ces agents." }, "candidateRow": { "gitChip": { @@ -21634,10 +21608,6 @@ "restore": "Restaurer", "discard": "Abandonner", "discardAll": "Tout abandonner", - "deleteUntrackedTitle_one": "Supprimer {{count}} fichier non suivi ?", - "deleteUntrackedTitle_other": "Supprimer {{count}} fichiers non suivis ?", - "deleteUntrackedTitle_many": "Supprimer {{count}} fichiers non suivis ?", - "deleteCount": "Supprimer {{count}}", "deleteUntrackedFilesDescription_one": "Cela supprimera définitivement {{count}} fichier non suivi. Cette action est irréversible.", "unstagedFilesIncludingUntrackedDescription_one": "Cela annulera les modifications non indexées apportées à {{count}} fichier et le supprimera définitivement s'il n'est pas suivi. Cette action est irréversible." }, @@ -22079,7 +22049,8 @@ }, "common": { "retry": "Réessayer", - "cancel": "Annuler" + "cancel": "Annuler", + "delete": "Supprimer" }, "fileExplorer": { "root": { @@ -22099,8 +22070,7 @@ "trashFailed": { "titleRecycleBin": "Impossible de déplacer « {{name}} » vers la Corbeille. Le supprimer définitivement ?", "titleTrash": "Impossible de déplacer « {{name}} » vers la Corbeille. Le supprimer définitivement ?", - "description": "Cette action est irréversible.", - "confirm": "Supprimer définitivement" + "description": "Cette action est irréversible." } }, "sparsePreset": { @@ -22530,7 +22500,6 @@ "nestedRemoval": { "refreshFailed": "Impossible d'actualiser la liste des espaces de travail", "review": "Supprimer avec les worktrees imbriqués…", - "title": "Supprimer l'espace de travail et les worktrees imbriqués ?", "description": "Cela supprime définitivement « {{name}} », chaque worktree imbriqué listé ci-dessous et tous les fichiers qu'il contient, y compris les modifications non validées. Leurs terminaux et agents seront arrêtés.", "detached": "HEAD détaché", "loading": "Vérification des worktrees imbriqués…", @@ -22538,7 +22507,6 @@ "cancel": "Annuler", "retry": "Vérifier à nouveau", "deleting": "Suppression…", - "confirm": "Supprimer tous les worktrees listés", "preservedBranches": "Certaines branches de worktrees imbriqués ont été conservées" }, "setupTerminalTitle": "Configuration", diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json index 8118800757c..c3f93d47ebf 100644 --- a/src/renderer/src/i18n/locales/ja.json +++ b/src/renderer/src/i18n/locales/ja.json @@ -3245,7 +3245,6 @@ "7eee951968": "作業場の安全性の確認", "191f0bc98e": "閉じる", "7ae2ad30f4": "更新", - "b2c1331844": "非アクティブなワークスペースの削除", "7f451a3e2c": "クリーンアップの提案を無視できませんでした", "662b8ec3f8": "ワークスペースのクリーンアップスキャンに失敗しました", "9cc26c019d": "削除", @@ -3255,7 +3254,6 @@ "1d3503357d": "削除の続行中にこれを閉じて後で戻れます。", "4c2990886e": "{{value0}}/{{value1}} 件を削除済み", "86ba852118": "{{value0}}、{{value1}} 件が失敗", - "deleteButtonCount": "{{value0}} を削除", "archivedStatus": "アーカイブ済み", "74f6c16279": "戻る" }, @@ -3841,7 +3839,6 @@ "1d411fb6a5": "このリポジトリのクイックコマンドを保存します", "8f1e971966": "クイックコマンドを追加", "3220e2da27": "このクイックコマンドは保存されたリストから削除されます。", - "e8e1a52edb": "「{{value0}}」を削除しますか?", "37e1bb90ce": "実行: {{value0}}", "77ac113df0": "{{value0}} を開始: {{value1}}", "7b1c9d6ae1": "実行", @@ -5177,7 +5174,6 @@ "loading": "コンテンツを読み込み中…", "deleteFailed": "Orca はこれをクラウドから削除できませんでした。", "deleted": "クラウドから削除されました", - "confirmDelete": "削除の確認", "moreActions": "{{name}} のその他の操作", "deletePackage": "クラウドから削除" }, @@ -5409,8 +5405,6 @@ "5cc1a6701c": "設定を開く", "2b56b35f53": "これは設定で変更できます。", "dd3a45bbbd": "次回からはこの確認は省略させていただきます。", - "fc23c4cbdf": "ワークスペースの削除", - "86f0ae1257": "ワークスペースの削除", "removeFolderProjectInstead": "このワークスペースを削除する代わりに、フォルダープロジェクトを削除してください。", "gitBlocksMainWorktree": "Git ではメインワークツリーを削除できません。", "removeBatchFolder_other": "Orca から{{count}} 個のワークスペースを削除します。ディスク上のプロジェクトフォルダーは削除されません。", @@ -5507,11 +5501,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "キャンセル", - "591f330288": "プロジェクトグループの削除", "2c14ce677a": "削除中…", "0e0e6764af": "含まれるプロジェクト", "55f75628c0": "ディスク上のプロジェクトフォルダーは削除されません。", - "fec7e9c8ae": "グループを削除", "removeContainedProjects_other": "含まれる {{count}} 件のプロジェクトを削除", "deleteGroupDescription": "{{name}} を削除します。", "moreProjects_other": "ほか {{count}} 件" @@ -6245,9 +6237,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "キャンセル", "cf95e3b5bb": "閉じる", - "deleteWorkspace": "ワークスペースを削除", "deleting": "削除中…", - "forceDelete": "強制削除", "forceDeleting": "強制削除中…" }, "local": { @@ -6549,16 +6539,12 @@ } }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "保持されているブランチを確認する", "f21976c9a8": "強制的に削除するローカルブランチを選択します。選択されていないブランチはリポジトリに残ります。", "38c947f7c5": "すべて選択", "9602129d38": "{{value0}}/{{value1}} が選択されました", "ee39e872d5": "統合されていない可能性があります", "676db406fd": "ヘッドが使用不可", - "285e1e4882": "キャンセル", - "a0f9863597_one": "{{count}} ブランチを強制削除", - "a0f9863597_other": "{{count}} ブランチを強制的に削除", - "a0f9863597": "{{count}} ブランチを強制的に削除" + "285e1e4882": "キャンセル" }, "WorktreeListScrollToTopButton": { "jumpToTop": "先頭へジャンプ" @@ -7890,7 +7876,6 @@ "c6b155911b": "すべてのコマンド", "c36912efd5": "タブバーの [クイックコマンド] ボタンから実行するか、ターミナル内で右クリックします。", "3d9dc558e8": "このクイックコマンドは保存されたリストから削除されます。", - "3edf3deaf8": "「{{value0}}」を削除しますか?", "9fcfc29519": "入れる", "9b3e338d62": "入力", "4ccc63da87": "Agent", @@ -12841,7 +12826,6 @@ "abf59262fb": "Agent を開始する前に、コマンド入力全体を確認して編集してください。", "4ede779461": "AI を使用してレビューの競合を解決する", "3b203c62f8": "これにより、PR からコメントが完全に削除されます。", - "ea9b649ce3": "コメントを削除しますか?", "5788d1059d": "レビュースレッドを更新できませんでした。 GitHub API の予算を確認してください。", "07871c0589": "別の PR をリンクする", "7f4489f370": "更新", @@ -13709,8 +13693,7 @@ "5ddd8cac7f": "ステージ済みの変更をすべて破棄しますか?", "d4df3a61df": "「{{value0}}」への変更を破棄しますか?", "5c0bdbc4cb": "「{{value0}}」を復元しますか?", - "d97bf697c9": "これにより、このファイルは完全に削除されます。これを元に戻すことはできません。", - "96c772bee9": "「{{value0}}」を削除しますか?" + "d97bf697c9": "これにより、このファイルは完全に削除されます。これを元に戻すことはできません。" }, "dialog": { "3bc61dc989": "キャンセル", @@ -13946,7 +13929,6 @@ "unresolved": "このワークスペースを所有しているホストを特定できませんでした。接続を確認して、もう一度お試しください。" }, "useFileDeletion": { - "d979a4fbb5": "「{{value0}}」を完全に削除しますか?", "92276aceb7": "削除", "7fb9435c86": "リモートホスト上のディレクトリとその内容を完全に削除します。この操作は取り消せません。", "23e98f192f": "リモートホスト上のファイルを完全に削除します。この操作は取り消せません。", @@ -14127,7 +14109,6 @@ "resumeInNewCliTooltipChat": "このチャットは変更されません。" }, "AiVaultSessionDeleteDialog": { - "title": "このセッションを削除しますか?", "description": "「{{value0}}」を削除します。削除すると、{{value1}} 自体のコマンドラインからも再開できなくなります。", "remoteDescription": "「{{value0}}」は {{value2}} から削除されます。削除後は {{value1}} 自身のコマンドラインからも再開できなくなります。", "confirm": "削除" @@ -17034,9 +17015,7 @@ "2faecab10b": "今すぐ実行", "13118faadf": "不明なプロジェクト", "73f630b49d": "キャンセル", - "9adfab2596": "外部オートメーションの削除", "1e2e41392f": "今後表示しない", - "080dcb5fbb": "オートメーションの削除", "19a6e30eae": "自動更新", "77c2778945": "自動化", "e1bf9b1512": "ワークスペースが利用できません。", @@ -17882,7 +17861,6 @@ }, "artifacts": { "ArtifactsPage": { - "deleteTitle": "成果物を削除しますか?", "deleteDescription": "「{{name}}」は公開リンクで利用できなくなります。", "delete": "削除", "signInAgain": "再度 Orca にサインインして、アーティファクトをロードします。", @@ -19824,11 +19802,8 @@ "candidateName": "{{value0}}、{{value1}}" }, "stopAgents": { - "titleMany": "これらの Agent を停止しますか?", "descriptionOne": "削除すると、Agent の現在の作業が停止します。", - "descriptionMany": "削除すると、これらの Agent の現在の作業が停止します。", - "confirm": "Agent を停止して削除", - "confirmOne": "Agent を停止して削除" + "descriptionMany": "削除すると、これらの Agent の現在の作業が停止します。" }, "candidateRow": { "gitChip": { @@ -20983,10 +20958,7 @@ "delete": "削除", "restore": "復元", "discard": "破棄", - "discardAll": "すべて破棄", - "deleteUntrackedTitle_one": "未追跡のファイル {{count}} 個を削除しますか?", - "deleteUntrackedTitle_other": "未追跡のファイル {{count}} 個を削除しますか?", - "deleteCount": "{{count}} 個を削除" + "discardAll": "すべて破棄" }, "createReviewFailure": { "authRequiredCommand": "{{providerName}} が認証されていません。次の手順: この環境で {{command}} を実行してください。", @@ -21410,7 +21382,8 @@ }, "common": { "retry": "再試行", - "cancel": "キャンセル" + "cancel": "キャンセル", + "delete": "削除" }, "fileExplorer": { "root": { @@ -21430,8 +21403,7 @@ "trashFailed": { "titleRecycleBin": "「{{name}}」をごみ箱に移動できませんでした。完全に削除しますか?", "titleTrash": "「{{name}}」をごみ箱に移動できませんでした。完全に削除しますか?", - "description": "この操作は取り消せません。", - "confirm": "完全に削除" + "description": "この操作は取り消せません。" } }, "sparsePreset": { @@ -21798,7 +21770,6 @@ "nestedRemoval": { "refreshFailed": "ワークスペースリストを更新できませんでした", "review": "ネストされたワークツリーと共に削除…", - "title": "ワークスペースとネストされたワークツリーを削除しますか?", "description": "これにより「{{name}}」、以下に表示されるすべてのネストされたワークツリー、およびそれら内のすべてのファイル(コミットされていない変更を含む)が完全に削除されます。それらのターミナルと Agent は停止されます。", "detached": "分離 HEAD", "loading": "ネストされたワークツリーを確認中…", @@ -21806,7 +21777,6 @@ "cancel": "キャンセル", "retry": "再確認", "deleting": "削除中…", - "confirm": "一覧表示されたすべてのワークツリーを削除", "preservedBranches": "一部のネストされたワークツリーブランチは保持されました" }, "setupTerminalTitle": "セットアップ" diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json index 3d5d02e435e..2e312739034 100644 --- a/src/renderer/src/i18n/locales/ko.json +++ b/src/renderer/src/i18n/locales/ko.json @@ -3245,7 +3245,6 @@ "7eee951968": "워크스페이스 안전성 확인 중", "191f0bc98e": "닫기", "7ae2ad30f4": "새로고침", - "b2c1331844": "비활성 워크스페이스 삭제", "7f451a3e2c": "정리 제안을 무시할 수 없습니다.", "662b8ec3f8": "워크스페이스 정리 검사 실패", "9cc26c019d": "제거", @@ -3255,7 +3254,6 @@ "1d3503357d": "삭제가 계속되는 동안 이 창을 닫고 나중에 돌아올 수 있습니다.", "4c2990886e": "{{value0}}/{{value1}}개 삭제됨", "86ba852118": "{{value0}}, {{value1}}개 실패", - "deleteButtonCount": "{{value0}}개 삭제", "archivedStatus": "보관됨", "74f6c16279": "뒤로" }, @@ -3838,7 +3836,6 @@ "1d411fb6a5": "이 repo에 대한 빠른 명령 저장", "8f1e971966": "빠른 명령 추가", "3220e2da27": "이 빠른 명령은 저장된 목록에서 제거됩니다.", - "e8e1a52edb": "'{{value0}}'을 삭제하시겠습니까?", "37e1bb90ce": "실행: {{value0}}", "77ac113df0": "{{value0}} 시작: {{value1}}", "7b1c9d6ae1": "실행", @@ -5174,7 +5171,6 @@ "loading": "콘텐츠 로드 중…", "deleteFailed": "Orca가 클라우드에서 이 항목을 삭제할 수 없습니다.", "deleted": "클라우드에서 삭제됨", - "confirmDelete": "삭제 확인", "moreActions": "{{name}}에 대한 추가 작업", "deletePackage": "클라우드에서 삭제" }, @@ -5406,8 +5402,6 @@ "5cc1a6701c": "설정 열기", "2b56b35f53": "설정에서 이를 변경할 수 있습니다.", "dd3a45bbbd": "다음번에는 이 확인을 건너뛰겠습니다.", - "fc23c4cbdf": "워크스페이스 삭제", - "86f0ae1257": "워크스페이스 삭제", "removeFolderProjectInstead": "이 워크스페이스를 삭제하는 대신 폴더 프로젝트를 제거하세요.", "gitBlocksMainWorktree": "Git에서는 메인 워크트리를 제거할 수 없습니다.", "removeBatchFolder_other": "Orca에서 워크스페이스 {{count}}개를 제거합니다. 디스크의 프로젝트 폴더는 삭제되지 않습니다.", @@ -5504,11 +5498,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "취소", - "591f330288": "프로젝트 그룹 삭제", "2c14ce677a": "삭제 중...", "0e0e6764af": "포함된 프로젝트", "55f75628c0": "디스크의 프로젝트 폴더는 삭제되지 않습니다.", - "fec7e9c8ae": "그룹 삭제", "removeContainedProjects_other": "포함된 프로젝트 {{count}}개 제거", "deleteGroupDescription": "{{name}} 그룹을 삭제합니다.", "moreProjects_other": "+{{count}}개 더" @@ -6242,9 +6234,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "취소", "cf95e3b5bb": "닫기", - "deleteWorkspace": "워크스페이스 삭제", "deleting": "삭제 중...", - "forceDelete": "강제 삭제", "forceDeleting": "강제 삭제 중..." }, "local": { @@ -6546,16 +6536,12 @@ } }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "유지된 브랜치 검토", "f21976c9a8": "강제 삭제하려는 로컬 브랜치을 선택하세요. 선택되지 않은 브랜치는 저장소에 유지됩니다.", "38c947f7c5": "모두 선택", "9602129d38": "{{value1}} 중 {{value0}}이(가) 선택됨", "ee39e872d5": "병합되지 않을 수 있음", "676db406fd": "헤드를 사용할 수 없음", - "285e1e4882": "취소", - "a0f9863597_one": "강제 삭제 {{count}} 브랜치", - "a0f9863597_other": "{{count}} 브랜치 강제 삭제", - "a0f9863597": "{{count}} 브랜치 강제 삭제" + "285e1e4882": "취소" }, "WorktreeListScrollToTopButton": { "jumpToTop": "맨 위로 이동" @@ -7850,7 +7836,6 @@ "c6b155911b": "모든 명령", "c36912efd5": "탭 표시줄의 빠른 명령 버튼을 사용하여 실행하거나 터미널 내부를 마우스 오른쪽 버튼으로 클릭하세요.", "3d9dc558e8": "이 빠른 명령은 저장된 목록에서 제거됩니다.", - "3edf3deaf8": "'{{value0}}'을 삭제하시겠습니까?", "9fcfc29519": "삽입", "9b3e338d62": "Enter로 실행", "4ccc63da87": "에이전트", @@ -12838,7 +12823,6 @@ "abf59262fb": "agent를 시작하기 전에 전체 명령 입력을 리뷰하고 편집하세요.", "4ede779461": "AI를 통해 리뷰 충돌 해결", "3b203c62f8": "이렇게 하면 PR에서 해당 댓글이 영구적으로 제거됩니다.", - "ea9b649ce3": "댓글을 삭제하시겠습니까?", "5788d1059d": "리뷰 스레드를 업데이트할 수 없습니다. GitHub API 예산을 확인하세요.", "07871c0589": "다른 PR 연결", "7f4489f370": "새로고침", @@ -13706,8 +13690,7 @@ "5ddd8cac7f": "스테이징된 변경 사항을 모두 삭제하시겠습니까?", "d4df3a61df": "'{{value0}}' 변경사항을 취소하시겠습니까?", "5c0bdbc4cb": "'{{value0}}'을(를) 복원하시겠습니까?", - "d97bf697c9": "이 파일이 영구적으로 삭제됩니다. 이 작업은 취소할 수 없습니다.", - "96c772bee9": "'{{value0}}'을 삭제하시겠습니까?" + "d97bf697c9": "이 파일이 영구적으로 삭제됩니다. 이 작업은 취소할 수 없습니다." }, "dialog": { "3bc61dc989": "취소", @@ -13943,7 +13926,6 @@ "unresolved": "이 워크스페이스를 소유한 호스트를 확인할 수 없습니다. 연결을 확인한 후 다시 시도하세요." }, "useFileDeletion": { - "d979a4fbb5": "'{{value0}}'을(를) 영구적으로 삭제하시겠습니까?", "92276aceb7": "삭제", "7fb9435c86": "원격 호스트의 디렉터리와 그 안의 내용을 영구적으로 삭제합니다. 이 작업은 되돌릴 수 없습니다.", "23e98f192f": "원격 호스트의 파일을 영구적으로 삭제합니다. 이 작업은 되돌릴 수 없습니다.", @@ -14124,7 +14106,6 @@ "resumeInNewCliTooltipChat": "이 채팅은 그대로 유지됩니다." }, "AiVaultSessionDeleteDialog": { - "title": "이 세션을 삭제할까요?", "description": "\"{{value0}}\"이(가) 삭제됩니다. 삭제하면 {{value1}}의 자체 명령줄에서도 더 이상 재개할 수 없습니다.", "remoteDescription": "\"{{value0}}\"이(가) {{value2}}에서 삭제됩니다. 삭제하면 {{value1}}의 자체 명령줄에서도 더 이상 재개할 수 없습니다.", "confirm": "삭제" @@ -17031,9 +17012,7 @@ "2faecab10b": "지금 실행", "13118faadf": "알 수 없는 프로젝트", "73f630b49d": "취소", - "9adfab2596": "외부 자동화 삭제", "1e2e41392f": "다시 묻지 마세요", - "080dcb5fbb": "자동화 삭제", "19a6e30eae": "자동화 새로 고침", "77c2778945": "자동화", "e1bf9b1512": "워크스페이스를 사용할 수 없습니다.", @@ -17879,7 +17858,6 @@ }, "artifacts": { "ArtifactsPage": { - "deleteTitle": "아티팩트를 삭제할까요?", "deleteDescription": "“{{name}}”은(는) 공개 링크에서 더 이상 사용할 수 없습니다.", "delete": "삭제", "signInAgain": "아티팩트를 로드하려면 Orca에 다시 로그인하세요.", @@ -19821,11 +19799,8 @@ "candidateName": "{{value1}}의 {{value0}}" }, "stopAgents": { - "titleMany": "이 agent들을 중지할까요?", "descriptionOne": "삭제하면 agent의 현재 작업이 중지됩니다.", - "descriptionMany": "삭제하면 이 agent들의 현재 작업이 중지됩니다.", - "confirm": "agent 중지 후 삭제", - "confirmOne": "agent 중지 후 삭제" + "descriptionMany": "삭제하면 이 agent들의 현재 작업이 중지됩니다." }, "candidateRow": { "gitChip": { @@ -20975,10 +20950,7 @@ "delete": "삭제", "restore": "복원", "discard": "버리기", - "discardAll": "모두 폐기", - "deleteUntrackedTitle_one": "추적되지 않은 파일 {{count}}개를 삭제할까요?", - "deleteUntrackedTitle_other": "추적되지 않은 파일 {{count}}개를 삭제할까요?", - "deleteCount": "{{count}}개 삭제" + "discardAll": "모두 폐기" }, "createReviewFailure": { "authRequiredCommand": "{{providerName}}이(가) 인증되지 않았습니다. 다음 단계: 이 환경에서 {{command}}을(를) 실행하세요.", @@ -21402,7 +21374,8 @@ }, "common": { "retry": "다시 시도", - "cancel": "취소" + "cancel": "취소", + "delete": "삭제" }, "fileExplorer": { "root": { @@ -21422,8 +21395,7 @@ "trashFailed": { "titleRecycleBin": "'{{name}}'을(를) 휴지통으로 이동하지 못했습니다. 영구적으로 삭제하시겠습니까?", "titleTrash": "'{{name}}'을(를) 휴지통으로 이동하지 못했습니다. 영구적으로 삭제하시겠습니까?", - "description": "이 작업은 되돌릴 수 없습니다.", - "confirm": "영구 삭제" + "description": "이 작업은 되돌릴 수 없습니다." } }, "sparsePreset": { @@ -21790,7 +21762,6 @@ "nestedRemoval": { "refreshFailed": "워크스페이스 목록을 새로고침하지 못했습니다", "review": "중첩 워크트리와 함께 삭제…", - "title": "워크스페이스와 중첩 워크트리를 삭제할까요?", "description": "\"{{name}}\", 아래에 나열된 모든 중첩 워크트리, 그리고 커밋되지 않은 변경 사항을 포함한 그 안의 모든 파일이 영구적으로 삭제됩니다. 해당 터미널과 에이전트는 중지됩니다.", "detached": "분리된 HEAD", "loading": "중첩 워크트리 확인 중…", @@ -21798,7 +21769,6 @@ "cancel": "취소", "retry": "다시 검토", "deleting": "삭제 중…", - "confirm": "나열된 모든 워크트리 삭제", "preservedBranches": "일부 중첩 워크트리 브랜치가 유지되었습니다" }, "setupTerminalTitle": "초기 설정" diff --git a/src/renderer/src/i18n/locales/pt.json b/src/renderer/src/i18n/locales/pt.json index d82a01fb373..3e62a8c2f91 100644 --- a/src/renderer/src/i18n/locales/pt.json +++ b/src/renderer/src/i18n/locales/pt.json @@ -16,7 +16,8 @@ }, "common": { "retry": "Tentar novamente", - "cancel": "Cancelar" + "cancel": "Cancelar", + "delete": "Excluir" }, "quickOpen": { "moreMatchesAvailable": "Mais resultados podem estar disponíveis. Refine sua busca para reduzir os resultados.", @@ -42,10 +43,6 @@ "restore": "Restaurar", "discard": "Descartar", "discardAll": "Descartar tudo", - "deleteUntrackedTitle_one": "Excluir {{count}} arquivo não rastreado?", - "deleteUntrackedTitle_other": "Excluir {{count}} arquivos não rastreados?", - "deleteUntrackedTitle_many": "Excluir {{count}} arquivos não rastreados?", - "deleteCount": "Excluir {{count}}", "deleteUntrackedFilesDescription_one": "Isso excluirá permanentemente {{count}} arquivo não rastreado. Essa ação não pode ser desfeita.", "unstagedFilesIncludingUntrackedDescription_one": "Isso reverterá as alterações não preparadas em {{count}} arquivo e o excluirá permanentemente se ele não for rastreado. Essa ação não pode ser desfeita." }, @@ -1545,9 +1542,7 @@ "importInvalid": "{{count}} linhas ilegíveis", "importUndecryptable": "{{count}} protegido pelo navegador; exporte um CSV dele", "imported": "{{count}} senhas importadas", - "deleteAllConfirmTitle": "Excluir todos os dados de senha?", "deleteAllConfirmDescription": "Cada senha salva no perfil {{profile}} é excluída permanentemente. Isso não pode ser desfeito.", - "deleteAll": "Excluir dados", "deletedAll": "Dados de senha excluídos", "form": { "invalidSite": "Insira um endereço de site, como exemplo.com.", @@ -1582,7 +1577,6 @@ "deleteAllDescription": "Todas as senhas salvas, notas e sites marcados para nunca salvar no perfil {{profile}}.", "empty": "O Orca oferece a opção de salvar um login após você entrar em um site. Você também pode importá-los de outro navegador ou de um arquivo CSV.", "moreActions": "Mais ações para {{account}}", - "deleteConfirmTitle": "Excluir a senha do {{site}} para {{username}}?", "deleteConfirmDescription": "O Orca não preencherá mais essa senha. Isso não pode ser desfeito.", "deleteConfirm": "Excluir", "profileDescription": "Logins salvos para o perfil {{profile}}. O Orca preenche um somente quando você o seleciona em um campo de login.", @@ -6706,7 +6700,6 @@ "c6b155911b": "Todos os comandos", "c36912efd5": "Execute-os no botão Comandos Rápidos na barra de abas, ou clique com o botão direito dentro de qualquer terminal.", "3d9dc558e8": "Este comando rápido será removido da sua lista salva.", - "3edf3deaf8": "Excluir \"{{value0}}\"?", "9fcfc29519": "Inserir", "9b3e338d62": "Enter", "4ccc63da87": "Agente", @@ -10582,8 +10575,7 @@ "5ddd8cac7f": "Descartar todas as alterações staged?", "d4df3a61df": "Descartar alterações em \"{{value0}}\"?", "5c0bdbc4cb": "Restaurar \"{{value0}}\"?", - "d97bf697c9": "Isso vai excluir este arquivo permanentemente. Isso não pode ser desfeito.", - "96c772bee9": "Excluir \"{{value0}}\"?" + "d97bf697c9": "Isso vai excluir este arquivo permanentemente. Isso não pode ser desfeito." }, "dialog": { "3bc61dc989": "Cancelar", @@ -10941,7 +10933,6 @@ "abf59262fb": "Revise e edite a entrada de comando completa antes de iniciar um agente.", "4ede779461": "Resolver Conflitos de Review Com IA", "3b203c62f8": "Isso vai remover o comentário do PR permanentemente.", - "ea9b649ce3": "Excluir comentário?", "5788d1059d": "Não foi possível atualizar a thread de review. Verifique o orçamento da API do GitHub.", "07871c0589": "Vincular outro PR", "7f4489f370": "Atualizar", @@ -11280,7 +11271,6 @@ "525259fa17": "Os detalhes da pilha estão temporariamente indisponíveis." }, "useFileDeletion": { - "d979a4fbb5": "Excluir '{{value0}}' permanentemente?", "92276aceb7": "Excluir", "7fb9435c86": "Isso exclui permanentemente o diretório e seu conteúdo no host remoto. Isso não pode ser desfeito.", "23e98f192f": "Isso exclui permanentemente o arquivo no host remoto. Isso não pode ser desfeito.", @@ -11487,7 +11477,6 @@ "unstageCount": "Remover do stage ({{count}})" }, "AiVaultSessionDeleteDialog": { - "title": "Excluir esta sessão?", "description": "\"{{value0}}\" será excluída. Depois de excluída, ela também não poderá mais ser retomada pela própria linha de comando de {{value1}}.", "remoteDescription": "\"{{value0}}\" será excluída de {{value2}}. Depois de excluída, também não poderá mais ser retomada pela linha de comando do {{value1}}.", "confirm": "Excluir" @@ -12378,11 +12367,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "Cancelar", - "591f330288": "Excluir Grupo de Projeto", "2c14ce677a": "Excluindo...", "0e0e6764af": "Projetos contidos", "55f75628c0": "As pastas de projeto no disco não são excluídas.", - "fec7e9c8ae": "Excluir Grupo", "removeContainedProjects_one": "Remover {{count}} projeto contido no grupo", "removeContainedProjects_other": "Remover {{count}} projetos contidos no grupo", "removeContainedProjects_many": "Remover {{count}} projetos contidos no grupo", @@ -12493,16 +12480,12 @@ "otherClientsAria": "Ocultar espaços de trabalho criados por outros clientes Orca em servidores remotos compartilhados" }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "Revisar branches mantidas", "f21976c9a8": "Selecione as branches locais que você quer forçar a exclusão. As branches não selecionadas permanecem em seus repositórios.", "38c947f7c5": "Selecionar todas", "9602129d38": "{{value0}} de {{value1}} selecionada(s)", "ee39e872d5": "Pode estar não mesclada", "676db406fd": "Head indisponível", - "285e1e4882": "Cancelar", - "a0f9863597_one": "Forçar Exclusão de {{count}} Branch", - "a0f9863597_other": "Forçar Exclusão de {{count}} Branches", - "a0f9863597_many": "Forçar Exclusão de {{count}} Branches" + "285e1e4882": "Cancelar" }, "local": { "base": { @@ -12528,8 +12511,6 @@ "5cc1a6701c": "Abrir Configurações", "2b56b35f53": "Você pode alterar isso nas Configurações.", "dd3a45bbbd": "Vamos pular esta confirmação na próxima vez.", - "fc23c4cbdf": "Excluir Espaço de Trabalho", - "86f0ae1257": "Excluir Espaços de Trabalho", "removeFolderProjectInstead": "Remova o projeto de pasta em vez de excluir este espaço de trabalho.", "gitBlocksMainWorktree": "O Git não permite remover a worktree principal.", "removeBatchFolder_one": "Remover {{count}} espaço de trabalho do Orca. As pastas dos projetos no disco não serão excluídas.", @@ -12959,9 +12940,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "Cancelar", "cf95e3b5bb": "Fechar", - "deleteWorkspace": "Excluir Espaço de Trabalho", "deleting": "Excluindo...", - "forceDelete": "Forçar Exclusão", "forceDeleting": "Forçando exclusão..." }, "WorktreeCardStatusSlot": { @@ -14443,7 +14422,6 @@ "loading": "Carregando conteúdo…", "deleteFailed": "O Orca não conseguiu excluir isto do Cloud.", "deleted": "Excluído do Cloud", - "confirmDelete": "Confirmar exclusão", "moreActions": "Mais ações para {{name}}", "deletePackage": "Excluir do Cloud" }, @@ -14593,9 +14571,7 @@ "2faecab10b": "Executar Agora", "13118faadf": "Projeto desconhecido", "73f630b49d": "Cancelar", - "9adfab2596": "Excluir Automação Externa", "1e2e41392f": "Não perguntar novamente", - "080dcb5fbb": "Excluir Automação", "19a6e30eae": "Atualizar automações", "77c2778945": "Automações", "e1bf9b1512": "O espaço de trabalho não está disponível.", @@ -17537,7 +17513,6 @@ "1d411fb6a5": "Salvar um comando rápido para este repositório", "8f1e971966": "Adicionar comando rápido", "3220e2da27": "Este comando rápido será removido da sua lista salva.", - "e8e1a52edb": "Excluir \"{{value0}}\"?", "37e1bb90ce": "Executar: {{value0}}", "77ac113df0": "Iniciar {{value0}}: {{value1}}", "7b1c9d6ae1": "Executar", @@ -17967,7 +17942,6 @@ "7eee951968": "Escaneando espaços de trabalho", "191f0bc98e": "Fechar", "7ae2ad30f4": "Atualizar", - "b2c1331844": "Excluir Espaços de Trabalho Inativos", "7f451a3e2c": "Não foi possível ignorar a sugestão de limpeza", "662b8ec3f8": "Falha na varredura de limpeza de espaços de trabalho", "9cc26c019d": "Remover", @@ -17977,7 +17951,6 @@ "1d3503357d": "Você pode fechar isto e voltar enquanto a exclusão continua.", "4c2990886e": "{{value0}}/{{value1}} excluídos", "86ba852118": "{{value0}}, {{value1}} falharam", - "deleteButtonCount": "Excluir {{value0}}", "archivedStatus": "Arquivado", "74f6c16279": "Voltar" }, @@ -18039,7 +18012,6 @@ "ArtifactsPage": { "signInAgain": "Entre no Orca novamente para carregar os artefatos.", "loadFailed": "Não foi possível carregar os artefatos.", - "deleteTitle": "Excluir artefato?", "deleteDescription": "\"{{name}}\" não estará mais disponível em seu link público.", "delete": "Excluir", "deleteFailed": "Não foi possível excluir o artefato.", @@ -22065,11 +22037,8 @@ "candidateName": "{{value0}} em {{value1}}" }, "stopAgents": { - "titleMany": "Parar estes agentes?", "descriptionOne": "A exclusão interromperá o trabalho atual do agente.", - "descriptionMany": "A exclusão interromperá o trabalho atual destes agentes.", - "confirm": "Parar agentes e excluir", - "confirmOne": "Parar agente e excluir" + "descriptionMany": "A exclusão interromperá o trabalho atual destes agentes." }, "candidateRow": { "gitChip": { @@ -22082,10 +22051,7 @@ "confirm": { "deletingTitle_one": "Excluindo {{count}} espaço de trabalho", "deletingTitle_other": "Excluindo {{count}} espaços de trabalho", - "deleteTitle_one": "Excluir {{count}} espaço de trabalho?", - "deleteTitle_other": "Excluir {{count}} espaços de trabalho?", - "deletingTitle_many": "Excluindo {{count}} espaços de trabalho", - "deleteTitle_many": "Excluir {{count}} espaços de trabalho?" + "deletingTitle_many": "Excluindo {{count}} espaços de trabalho" } }, "creation": { @@ -22814,8 +22780,7 @@ "trashFailed": { "titleRecycleBin": "Não foi possível mover '{{name}}' para a Lixeira. Excluir permanentemente?", "titleTrash": "Não foi possível mover '{{name}}' para a Lixeira. Excluir permanentemente?", - "description": "Isso não pode ser desfeito.", - "confirm": "Excluir permanentemente" + "description": "Isso não pode ser desfeito." } }, "sparsePreset": { @@ -23028,7 +22993,6 @@ "nestedRemoval": { "refreshFailed": "Não foi possível atualizar a lista de espaços de trabalho", "review": "Excluir com worktrees aninhadas…", - "title": "Excluir espaço de trabalho e worktrees aninhadas?", "description": "Isso exclui permanentemente \"{{name}}\", todas as worktrees aninhadas listadas abaixo e todos os arquivos nelas, incluindo alterações não commitadas. Seus terminais e agentes serão interrompidos.", "detached": "HEAD desanexado", "loading": "Verificando worktrees aninhadas…", @@ -23036,7 +23000,6 @@ "cancel": "Cancelar", "retry": "Revisar novamente", "deleting": "Excluindo…", - "confirm": "Excluir todas as worktrees listadas", "preservedBranches": "Algumas branches de worktrees aninhadas foram mantidas" }, "setupTerminalTitle": "Configuração", diff --git a/src/renderer/src/i18n/locales/ru.json b/src/renderer/src/i18n/locales/ru.json index acf5f1b7738..1af528a9f4c 100644 --- a/src/renderer/src/i18n/locales/ru.json +++ b/src/renderer/src/i18n/locales/ru.json @@ -3904,7 +3904,6 @@ "7eee951968": "Сканирование рабочих пространств", "191f0bc98e": "Закрыть", "7ae2ad30f4": "Обновить", - "b2c1331844": "Удалить неактивные рабочие пространства", "7f451a3e2c": "Не удалось проигнорировать рекомендацию по очистке", "662b8ec3f8": "Не удалось проверить рабочие пространства для очистки", "9cc26c019d": "Удалить", @@ -3914,7 +3913,6 @@ "1d3503357d": "Можно закрыть окно и вернуться позже: удаление продолжится.", "4c2990886e": "Удалено: {{value0}}/{{value1}}", "86ba852118": "{{value0}}, с ошибкой: {{value1}}", - "deleteButtonCount": "Удалить {{value0}}", "archivedStatus": "Архивные", "74f6c16279": "Назад" }, @@ -4512,7 +4510,6 @@ "1d411fb6a5": "Сохранить быструю команду для этого репозитория", "8f1e971966": "Добавить быструю команду", "3220e2da27": "Эта быстрая команда будет удалена из сохраненного списка.", - "e8e1a52edb": "Удалить «{{value0}}»?", "37e1bb90ce": "Выполнить: {{value0}}", "77ac113df0": "Запустить {{value0}}: {{value1}}", "7b1c9d6ae1": "Запустить", @@ -6046,7 +6043,6 @@ "loading": "Загрузка содержимого…", "deleteFailed": "Orca не удалось удалить это из Cloud.", "deleted": "Удалено из Cloud", - "confirmDelete": "Подтвердить удаление", "moreActions": "Другие действия для {{name}}", "deletePackage": "Удалить из Cloud" }, @@ -6321,8 +6317,6 @@ "5cc1a6701c": "Открыть настройки", "2b56b35f53": "Это можно изменить в настройках.", "dd3a45bbbd": "В следующий раз подтверждение не потребуется.", - "fc23c4cbdf": "Удалить рабочее пространство", - "86f0ae1257": "Удалить рабочие пространства", "removeFolderProjectInstead": "Удалите проект папки из списка вместо удаления этого рабочего пространства.", "gitBlocksMainWorktree": "Git не позволяет удалить основное рабочее дерево.", "removeBatchFolder_one": "Удалить {{count}} рабочее пространство из Orca. Папки проектов на диске не будут удалены.", @@ -6454,11 +6448,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "Отмена", - "591f330288": "Удалить группу проектов", "2c14ce677a": "Удаление…", "0e0e6764af": "Проекты в группе", "55f75628c0": "Папки проектов на диске не удаляются.", - "fec7e9c8ae": "Удалить группу", "removeContainedProjects_one": "Удалить {{count}} входящий в группу проект из списка", "removeContainedProjects_few": "Удалить {{count}} входящих в группу проекта из списка", "removeContainedProjects_many": "Удалить {{count}} входящих в группу проектов из списка", @@ -7209,9 +7201,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "Отмена", "cf95e3b5bb": "Закрыть", - "deleteWorkspace": "Удалить рабочее пространство", "deleting": "Удаление…", - "forceDelete": "Удалить принудительно", "forceDeleting": "Принудительное удаление…" }, "HostRemoveDialog": { @@ -7499,17 +7489,12 @@ } }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "Проверить сохранённые ветки", "f21976c9a8": "Выберите локальные ветки для принудительного удаления. Невыбранные ветки останутся в репозиториях.", "38c947f7c5": "Выбрать все", "9602129d38": "Выбрано: {{value0}} из {{value1}}", "ee39e872d5": "Возможно, ветка не слита", "676db406fd": "Вершина ветки недоступна", - "285e1e4882": "Отмена", - "a0f9863597_one": "Принудительно удалить {{count}} ветку", - "a0f9863597_few": "Принудительно удалить {{count}} ветки", - "a0f9863597_many": "Принудительно удалить {{count}} веток", - "a0f9863597_other": "Принудительно удалить ветки: {{count}}" + "285e1e4882": "Отмена" }, "WorktreeListScrollToTopButton": { "jumpToTop": "Перейти в начало" @@ -8917,7 +8902,6 @@ "c6b155911b": "Все команды", "c36912efd5": "Запускайте их кнопкой быстрых команд на панели вкладок или через правый щелчок в любом терминале.", "3d9dc558e8": "Эта быстрая команда будет удалена из сохранённого списка.", - "3edf3deaf8": "Удалить «{{value0}}»?", "9fcfc29519": "Вставить", "9b3e338d62": "Enter", "4ccc63da87": "Агент", @@ -13851,7 +13835,6 @@ "5594400d73": "Нет ошибок проверок для исправления.", "4ede779461": "Разрешить конфликты ревью с ИИ", "3b203c62f8": "Комментарий будет безвозвратно удалён из PR.", - "ea9b649ce3": "Удалить комментарий?", "5788d1059d": "Не удалось обновить обсуждение ревью. Проверьте лимит запросов GitHub API.", "07871c0589": "Связать другой PR", "7f4489f370": "Обновить", @@ -14776,8 +14759,7 @@ "5ddd8cac7f": "Отменить все изменения в индексе?", "d4df3a61df": "Отменить изменения в «{{value0}}»?", "5c0bdbc4cb": "Восстановить «{{value0}}»?", - "d97bf697c9": "Этот файл будет безвозвратно удалён. Это действие нельзя отменить.", - "96c772bee9": "Удалить «{{value0}}»?" + "d97bf697c9": "Этот файл будет безвозвратно удалён. Это действие нельзя отменить." }, "dialog": { "3bc61dc989": "Отмена", @@ -15032,7 +15014,6 @@ "unresolved": "Не удалось определить хост этого рабочего пространства. Проверьте соединение и попробуйте снова." }, "useFileDeletion": { - "d979a4fbb5": "Безвозвратно удалить «{{value0}}»?", "92276aceb7": "Удалить", "7fb9435c86": "Папка и всё её содержимое будут безвозвратно удалены на удалённом хосте. Это действие нельзя отменить.", "23e98f192f": "Файл будет безвозвратно удалён на удалённом хосте. Это действие нельзя отменить.", @@ -15237,7 +15218,6 @@ "resumeInNewCliTooltipChat": "Этот чат останется без изменений." }, "AiVaultSessionDeleteDialog": { - "title": "Удалить эту сессию?", "description": "«{{value0}}» будет удалён. После удаления его нельзя будет продолжить даже из командной строки {{value1}}.", "remoteDescription": "Сессия «{{value0}}» будет удалена с {{value2}}. После удаления её нельзя будет продолжить даже из командной строки {{value1}}.", "confirm": "Удалить" @@ -18215,9 +18195,7 @@ "2faecab10b": "Запустить сейчас", "13118faadf": "Неизвестный проект", "73f630b49d": "Отмена", - "9adfab2596": "Удалить внешнюю автоматизацию", "1e2e41392f": "Больше не спрашивать", - "080dcb5fbb": "Удалить автоматизацию", "19a6e30eae": "Обновить автоматизации", "77c2778945": "Автоматизации", "e1bf9b1512": "Рабочее пространство недоступно.", @@ -19187,7 +19165,6 @@ "ArtifactsPage": { "signInAgain": "Войдите в Orca снова для загрузки артефактов.", "loadFailed": "Не удалось загрузить артефакты.", - "deleteTitle": "Удалить артефакт?", "deleteDescription": "«{{name}}» больше не будет доступен по публичной ссылке.", "delete": "Удалить", "deleteFailed": "Не удалось удалить артефакт.", @@ -21214,11 +21191,8 @@ "candidateName": "{{value0}} на {{value1}}" }, "stopAgents": { - "titleMany": "Остановить этих агентов?", "descriptionOne": "Удаление остановит текущую работу агента.", - "descriptionMany": "Удаление остановит текущую работу этих агентов.", - "confirm": "Остановить агентов и удалить", - "confirmOne": "Остановить агента и удалить" + "descriptionMany": "Удаление остановит текущую работу этих агентов." }, "candidateRow": { "gitChip": { @@ -21914,16 +21888,11 @@ "unstagedFilesIncludingUntrackedDescription_few": "Изменения вне индекса будут отменены, а неотслеживаемые файлы среди выбранных ({{count}}) — безвозвратно удалены. Это действие нельзя отменить.", "unstagedFilesIncludingUntrackedDescription_many": "Изменения вне индекса будут отменены, а неотслеживаемые файлы среди выбранных ({{count}}) — безвозвратно удалены. Это действие нельзя отменить.", "delete": "Удалить", - "deleteCount": "Удалить {{count}}", "deleteUntrackedFilesDescription_one": "Будет безвозвратно удалён {{count}} неотслеживаемый файл. Это действие нельзя отменить.", - "deleteUntrackedTitle_one": "Удалить {{count}} неотслеживаемый файл?", - "deleteUntrackedTitle_other": "Удалить {{count}} неотслеживаемого файла?", "discard": "Отменить изменения", "discardAll": "Отменить все изменения", "restore": "Восстановить", - "unstagedFilesIncludingUntrackedDescription_one": "Изменения не в индексе будут отменены, а неотслеживаемые файлы среди выбранных ({{count}}) — безвозвратно удалены. Это действие нельзя отменить.", - "deleteUntrackedTitle_few": "Удалить {{count}} неотслеживаемых файла?", - "deleteUntrackedTitle_many": "Удалить {{count}} неотслеживаемых файлов?" + "unstagedFilesIncludingUntrackedDescription_one": "Изменения не в индексе будут отменены, а неотслеживаемые файлы среди выбранных ({{count}}) — безвозвратно удалены. Это действие нельзя отменить." }, "unlinkedPr": { "status": "Связь с PR №{{number}} удалена" @@ -22405,7 +22374,8 @@ }, "common": { "retry": "Повторить", - "cancel": "Отмена" + "cancel": "Отмена", + "delete": "Удалить" }, "terminal": { "sessionSaveFailure": { @@ -22538,8 +22508,7 @@ "trashFailed": { "titleRecycleBin": "Не удалось переместить «{{name}}» в Корзину. Удалить безвозвратно?", "titleTrash": "Не удалось переместить «{{name}}» в Корзину. Удалить безвозвратно?", - "description": "Это действие нельзя отменить.", - "confirm": "Удалить безвозвратно" + "description": "Это действие нельзя отменить." } }, "sparsePreset": { @@ -22754,7 +22723,6 @@ "nestedRemoval": { "refreshFailed": "Не удалось обновить список рабочих пространств", "review": "Удалить вместе с вложенными рабочими деревьями…", - "title": "Удалить рабочее пространство и вложенные рабочие деревья?", "description": "Это навсегда удалит «{{name}}», все перечисленные ниже вложенные рабочие деревья и все файлы в них, включая незакоммиченные изменения. Их терминалы и агенты будут остановлены.", "detached": "Отсоединённый HEAD", "loading": "Проверка вложенных рабочих деревьев…", @@ -22762,7 +22730,6 @@ "cancel": "Отмена", "retry": "Проверить снова", "deleting": "Удаление…", - "confirm": "Удалить все перечисленные рабочие деревья", "preservedBranches": "Некоторые ветки вложенных рабочих деревьев сохранены" }, "setupTerminalTitle": "Настройка", diff --git a/src/renderer/src/i18n/locales/tr.json b/src/renderer/src/i18n/locales/tr.json index c8aac0ff0f6..9321f2c34d4 100644 --- a/src/renderer/src/i18n/locales/tr.json +++ b/src/renderer/src/i18n/locales/tr.json @@ -3782,7 +3782,6 @@ "7eee951968": "Çalışma alanları taranıyor", "191f0bc98e": "Kapat", "7ae2ad30f4": "Yenile", - "b2c1331844": "Aktif Olmayan Çalışma Alanlarını Sil", "7f451a3e2c": "Temizlik önerisi yok sayılamadı", "662b8ec3f8": "Çalışma alanı temizlik taraması başarısız oldu", "9cc26c019d": "Kaldır", @@ -3792,7 +3791,6 @@ "1d3503357d": "Silme işlemi devam ederken bunu kapatıp geri dönebilirsiniz.", "4c2990886e": "{{value0}}/{{value1}} silindi", "86ba852118": "{{value0}}, {{value1}} başarısız oldu", - "deleteButtonCount": "{{value0}} Sil", "archivedStatus": "Arşivlendi", "74f6c16279": "Geri" }, @@ -4380,7 +4378,6 @@ "1d411fb6a5": "Bu repo için bir hızlı komut kaydedin", "8f1e971966": "Hızlı komut ekle", "3220e2da27": "Bu hızlı komut kaydedilenler listenizden kaldırılacak.", - "e8e1a52edb": "\"{{value0}}\" silinsin mi?", "37e1bb90ce": "Çalıştır: {{value0}}", "77ac113df0": "Başlat {{value0}}: {{value1}}", "7b1c9d6ae1": "Çalıştır", @@ -5786,7 +5783,6 @@ "loading": "İçerik yükleniyor…", "deleteFailed": "Orca bunu Cloud'dan silemedi.", "deleted": "Cloud'dan silindi", - "confirmDelete": "Silmeyi onayla", "moreActions": "{{name}} için diğer işlemler", "deletePackage": "Cloud'dan sil" }, @@ -6053,8 +6049,6 @@ "5cc1a6701c": "Ayarlar'ı Aç", "2b56b35f53": "Bunu Ayarlar'dan değiştirebilirsiniz.", "dd3a45bbbd": "Bir dahaki sefere bu onayı atlayacağız.", - "fc23c4cbdf": "Çalışma Alanını Sil", - "86f0ae1257": "Çalışma Alanlarını Sil", "removeFolderProjectInstead": "Bu çalışma alanını silmek yerine klasör projesini kaldırın.", "gitBlocksMainWorktree": "Git, ana worktree'nin kaldırılmasına izin vermez.", "removeBatchFolder_one": "Orca'dan {{count}} çalışma alanı kaldır. Diskteki proje klasörleri silinmeyecek.", @@ -6160,11 +6154,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "İptal", - "591f330288": "Proje Grubunu Sil", "2c14ce677a": "Siliniyor...", "0e0e6764af": "İçerdiği projeler", "55f75628c0": "Diskteki proje klasörleri silinmez.", - "fec7e9c8ae": "Grubu Sil", "removeContainedProjects_other": "İçerdiği {{count}} projeyi kaldır", "removeContainedProjects_one": "İçerdiği {{count}} projeyi kaldır", "deleteGroupDescription": "{{name}} silinecek.", @@ -6891,9 +6883,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "İptal", "cf95e3b5bb": "Kapat", - "deleteWorkspace": "Çalışma alanını sil", "deleting": "Siliniyor…", - "forceDelete": "Zorla sil", "forceDeleting": "Zorla siliniyor…" }, "HostRemoveDialog": { @@ -7159,15 +7149,12 @@ } }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "Korunan branch'leri incele", "f21976c9a8": "Zorla silmek istediğiniz yerel branch'leri seçin. Seçilmeyen branch'ler repolarında kalır.", "38c947f7c5": "Tümünü seç", "9602129d38": "{{value1}} öğeden {{value0}} tanesi seçildi", "ee39e872d5": "Merge edilmemiş olabilir", "676db406fd": "HEAD kullanılamıyor", - "285e1e4882": "İptal", - "a0f9863597_one": "{{count}} Branch'i Zorla Sil", - "a0f9863597_other": "{{count}} Branch'i Zorla Sil" + "285e1e4882": "İptal" }, "WorktreeListScrollToTopButton": { "jumpToTop": "Başa git" @@ -8549,7 +8536,6 @@ "c6b155911b": "Tüm komutlar", "c36912efd5": "Bunları sekme çubuğundaki Hızlı Komutlar düğmesinden veya herhangi bir terminalde sağ tıklayarak çalıştırın.", "3d9dc558e8": "Bu hızlı komut kayıtlı listenizden kaldırılacak.", - "3edf3deaf8": "\"{{value0}}\" silinsin mi?", "9fcfc29519": "Ekle", "9b3e338d62": "Enter", "4ccc63da87": "Ajan", @@ -13472,7 +13458,6 @@ "abf59262fb": "Bir ajanı başlatmadan önce komut girdisinin tamamını inceleyip düzenleyin.", "4ede779461": "İnceleme Conflict'lerini AI ile Çöz", "3b203c62f8": "Bu, yorumu PR'dan kalıcı olarak kaldırır.", - "ea9b649ce3": "Yorum silinsin mi?", "5788d1059d": "İnceleme dizisi güncellenemedi. GitHub API bütçesini kontrol edin.", "07871c0589": "Başka bir PR bağla", "7f4489f370": "Yenile", @@ -14354,8 +14339,7 @@ "5ddd8cac7f": "Tüm stage edilmiş değişiklikler geri alınsın mı?", "d4df3a61df": "\"{{value0}}\" üzerindeki değişiklikler geri alınsın mı?", "5c0bdbc4cb": "\"{{value0}}\" geri yüklensin mi?", - "d97bf697c9": "Bu, dosyayı kalıcı olarak siler. Bu geri alınamaz.", - "96c772bee9": "\"{{value0}}\" silinsin mi?" + "d97bf697c9": "Bu, dosyayı kalıcı olarak siler. Bu geri alınamaz." }, "dialog": { "3bc61dc989": "İptal", @@ -14592,7 +14576,6 @@ "unresolved": "Bu çalışma alanının sahibi olan host belirlenemedi. Bağlantıyı kontrol edip tekrar deneyin." }, "useFileDeletion": { - "d979a4fbb5": "'{{value0}}' kalıcı olarak silinsin mi?", "92276aceb7": "Sil", "7fb9435c86": "Bu, uzak sunucudaki dizini ve içeriğini kalıcı olarak siler. Bu geri alınamaz.", "23e98f192f": "Bu, uzak sunucudaki dosyayı kalıcı olarak siler. Bu geri alınamaz.", @@ -14781,7 +14764,6 @@ "resumeInNewCliTooltipChat": "Bu sohbet değişmeden kalır." }, "AiVaultSessionDeleteDialog": { - "title": "Bu oturum silinsin mi?", "description": "\"{{value0}}\" silinecek. Silindikten sonra artık {{value1}} aracının kendi komut satırından da devam ettirilemez.", "remoteDescription": "\"{{value0}}\", {{value2}} konumundan silinecek. Silindikten sonra artık {{value1}} aracının kendi komut satırından da devam ettirilemeyecek.", "confirm": "Sil" @@ -17691,9 +17673,7 @@ "2faecab10b": "Şimdi Çalıştır", "13118faadf": "Bilinmeyen proje", "73f630b49d": "İptal", - "9adfab2596": "Harici Otomasyonu Sil", "1e2e41392f": "Bir daha sorma", - "080dcb5fbb": "Otomasyonu Sil", "19a6e30eae": "Otomasyonları yenile", "77c2778945": "Otomasyonlar", "e1bf9b1512": "Çalışma alanı kullanılamıyor.", @@ -18613,7 +18593,6 @@ "ArtifactsPage": { "signInAgain": "Artifact'leri yüklemek için Orca'da yeniden oturum açın.", "loadFailed": "Artifact'ler yüklenemedi.", - "deleteTitle": "Artifact silinsin mi?", "deleteDescription": "\"{{name}}\" artık genel bağlantısında kullanılamayacak.", "delete": "Sil", "deleteFailed": "Artifact silinemedi.", @@ -20515,11 +20494,8 @@ "candidateName": "{{value1}} üzerinde {{value0}}" }, "stopAgents": { - "titleMany": "Bu ajanlar durdurulsun mu?", "descriptionOne": "Silme işlemi ajanın mevcut çalışmasını durdurur.", - "descriptionMany": "Silme işlemi bu ajanların mevcut çalışmalarını durdurur.", - "confirm": "Ajanları durdur ve sil", - "confirmOne": "Ajanı durdur ve sil" + "descriptionMany": "Silme işlemi bu ajanların mevcut çalışmalarını durdurur." }, "candidateRow": { "gitChip": { @@ -21203,10 +21179,7 @@ "unstagedSelectionIncludingUntrackedDescription": "Bu işlem, bu seçimdeki hazırlama alanına alınmamış değişiklikleri geri alır ve izlenmeyen dosyaları kalıcı olarak siler. Bu işlem geri alınamaz.", "unstagedFilesIncludingUntrackedDescription_other": "Bu işlem, bu {{count}} dosyanın hazırlama alanına alınmamış değişikliklerini geri alır ve aralarındaki izlenmeyen dosyaları kalıcı olarak siler. Bu işlem geri alınamaz.", "delete": "Sil", - "deleteCount": "{{count}} öğeyi sil", "deleteUntrackedFilesDescription_one": "Bu işlem, izlenmeyen bu {{count}} dosyayı kalıcı olarak siler. Bu işlem geri alınamaz.", - "deleteUntrackedTitle_one": "İzlenmeyen {{count}} dosya silinsin mi?", - "deleteUntrackedTitle_other": "İzlenmeyen {{count}} dosya silinsin mi?", "discard": "Geri al", "discardAll": "Tümünü geri al", "restore": "Geri yükle", @@ -21682,7 +21655,8 @@ }, "common": { "retry": "Yeniden dene", - "cancel": "İptal" + "cancel": "İptal", + "delete": "Sil" }, "terminal": { "sessionSaveFailure": { @@ -21807,8 +21781,7 @@ "trashFailed": { "titleRecycleBin": "'{{name}}' geri dönüşüm kutusuna taşınamadı. Kalıcı olarak silinsin mi?", "titleTrash": "'{{name}}' çöp kutusuna taşınamadı. Kalıcı olarak silinsin mi?", - "description": "Bu işlem geri alınamaz.", - "confirm": "Kalıcı olarak sil" + "description": "Bu işlem geri alınamaz." } }, "sparsePreset": { @@ -22017,7 +21990,6 @@ "nestedRemoval": { "refreshFailed": "Çalışma alanı listesi yenilenemedi", "review": "İç içe worktree'lerle birlikte sil…", - "title": "Çalışma alanı ve iç içe worktree'ler silinsin mi?", "description": "Bu işlem, \"{{name}}\" çalışma alanını, aşağıda listelenen tüm iç içe worktree'leri ve commit edilmemiş değişiklikler de dahil olmak üzere içlerindeki tüm dosyaları kalıcı olarak siler. Terminalleri ve ajanları durdurulacak.", "detached": "Ayrık HEAD", "loading": "İç içe worktree'ler kontrol ediliyor…", @@ -22025,7 +21997,6 @@ "cancel": "İptal", "retry": "Yeniden incele", "deleting": "Siliniyor…", - "confirm": "Listelenen tüm worktree'leri sil", "preservedBranches": "Bazı iç içe worktree branch'leri korundu" }, "setupTerminalTitle": "Kurulum", diff --git a/src/renderer/src/i18n/locales/zh-Hant.json b/src/renderer/src/i18n/locales/zh-Hant.json index fdc4ceb3682..0d592cf40c5 100644 --- a/src/renderer/src/i18n/locales/zh-Hant.json +++ b/src/renderer/src/i18n/locales/zh-Hant.json @@ -3738,7 +3738,6 @@ "7eee951968": "掃描工作區", "191f0bc98e": "關閉", "7ae2ad30f4": "重新整理", - "b2c1331844": "刪除閒置工作區", "7f451a3e2c": "無法忽略清理建議", "662b8ec3f8": "工作區清理掃描失敗", "9cc26c019d": "移除", @@ -3748,7 +3747,6 @@ "1d3503357d": "刪除作業會繼續進行,您可以先關閉此視窗,稍後再回來。", "4c2990886e": "已刪除 {{value0}}/{{value1}}", "86ba852118": "{{value0}},{{value1}} 個失敗", - "deleteButtonCount": "刪除 {{value0}}", "archivedStatus": "已封存", "74f6c16279": "返回" }, @@ -4331,7 +4329,6 @@ "1d411fb6a5": "為此存放庫儲存快速命令", "8f1e971966": "新增快速命令", "3220e2da27": "該快速命令將從您儲存的清單中刪除。", - "e8e1a52edb": "刪除「{{value0}}」?", "37e1bb90ce": "執行:{{value0}}", "77ac113df0": "啟動 {{value0}}: {{value1}}", "7b1c9d6ae1": "執行", @@ -5667,7 +5664,6 @@ "loading": "正在載入內容...", "deleteFailed": "Orca 無法從雲端刪除此內容。", "deleted": "已從雲端刪除", - "confirmDelete": "確認刪除", "moreActions": "{{name}} 的更多操作", "deletePackage": "從雲端刪除" }, @@ -5930,8 +5926,6 @@ "5cc1a6701c": "開啟設定", "2b56b35f53": "您可以在「設定」中變更此設定。", "dd3a45bbbd": "下次我們將跳過此確認。", - "fc23c4cbdf": "刪除工作區", - "86f0ae1257": "刪除工作區", "removeFolderProjectInstead": "請移除資料夾專案,而非刪除此工作區。", "gitBlocksMainWorktree": "Git 不允許移除主工作樹。", "removeBatchFolder_other": "從 Orca 移除 {{count}} 個工作區。磁碟上的專案資料夾不會被刪除。", @@ -6027,11 +6021,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "取消", - "591f330288": "刪除專案群組", "2c14ce677a": "正在刪除...", "0e0e6764af": "包含的專案", "55f75628c0": "不會刪除磁碟上的專案資料夾。", - "fec7e9c8ae": "刪除群組", "removeContainedProjects_other": "移除內含的 {{count}} 個專案", "deleteGroupDescription": "刪除 {{name}}。", "moreProjects_other": "另有 {{count}} 個" @@ -6744,9 +6736,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "取消", "cf95e3b5bb": "關閉", - "deleteWorkspace": "刪除工作區", "deleting": "正在刪除...", - "forceDelete": "強制刪除", "forceDeleting": "正在強制刪除..." }, "HostRemoveDialog": { @@ -7006,15 +6996,12 @@ } }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "審查保留的分支", "f21976c9a8": "選擇要強制刪除的本機分支。未選擇的分支保留在其存放庫中。", "38c947f7c5": "選擇全部", "9602129d38": "已選擇 {{value0}} 個,共 {{value1}} 個", "ee39e872d5": "可能尚未合併", "676db406fd": "無法取得分支頂端提交", - "285e1e4882": "取消", - "a0f9863597_one": "強制刪除 {{count}} 個分支", - "a0f9863597_other": "強制刪除 {{count}} 個分支" + "285e1e4882": "取消" }, "WorktreeListScrollToTopButton": { "jumpToTop": "跳到頂部" @@ -8393,7 +8380,6 @@ "c6b155911b": "所有命令", "c36912efd5": "從分頁欄中的「快速命令」按鈕執行它們,或者在任何終端機內按一下滑鼠右鍵。", "3d9dc558e8": "該快速命令將從您儲存的清單中刪除。", - "3edf3deaf8": "刪除「{{value0}}」?", "9fcfc29519": "插入", "9b3e338d62": "Enter 鍵", "4ccc63da87": "代理程式", @@ -13315,7 +13301,6 @@ "abf59262fb": "啟動代理程式前,請檢視並編輯完整的命令輸入內容。", "4ede779461": "使用 AI 解決審查衝突", "3b203c62f8": "這將從 PR 中永久刪除該評論。", - "ea9b649ce3": "刪除評論?", "5788d1059d": "無法更新審查討論串。請檢查 GitHub API 配額。", "07871c0589": "連結另一個 PR", "7f4489f370": "重新整理", @@ -14183,8 +14168,7 @@ "5ddd8cac7f": "放棄所有已暫存的變更?", "d4df3a61df": "放棄對「{{value0}}」的變更?", "5c0bdbc4cb": "恢復「{{value0}}」?", - "d97bf697c9": "這將永久刪除該檔案。此操作無法復原。", - "96c772bee9": "刪除「{{value0}}」?" + "d97bf697c9": "這將永久刪除該檔案。此操作無法復原。" }, "dialog": { "3bc61dc989": "取消", @@ -14420,7 +14404,6 @@ "unresolved": "無法確定哪個主機擁有此工作區。請檢查連線後重試。" }, "useFileDeletion": { - "d979a4fbb5": "永久刪除「{{value0}}」?", "92276aceb7": "刪除", "7fb9435c86": "這會永久刪除遠端主機上的目錄及其內容。此操作無法復原。", "23e98f192f": "這會永久刪除遠端主機上的檔案。此操作無法復原。", @@ -14601,7 +14584,6 @@ "resumeInNewCliTooltipChat": "此聊天會維持不變。" }, "AiVaultSessionDeleteDialog": { - "title": "要刪除此工作階段嗎?", "description": "「{{value0}}」將被刪除。刪除後,也將無法透過 {{value1}} 自身的命令列恢復。", "remoteDescription": "「{{value0}}」將從 {{value2}} 刪除。刪除後,也將無法透過 {{value1}} 自身的命令列恢復。", "confirm": "刪除" @@ -17481,9 +17463,7 @@ "2faecab10b": "立即執行", "13118faadf": "未知專案", "73f630b49d": "取消", - "9adfab2596": "刪除外部自動化", "1e2e41392f": "不要再問", - "080dcb5fbb": "刪除自動化", "19a6e30eae": "重新整理自動化", "77c2778945": "自動化", "e1bf9b1512": "工作區不可用。", @@ -18381,7 +18361,6 @@ "ArtifactsPage": { "signInAgain": "請重新登入 Orca 以載入成品。", "loadFailed": "無法載入成品。", - "deleteTitle": "刪除成品?", "deleteDescription": "刪除後,將無法再透過公開連結存取「{{name}}」。", "delete": "刪除", "deleteFailed": "無法刪除成品。", @@ -20223,11 +20202,8 @@ "candidateName": "{{value0}} 在 {{value1}} 上" }, "stopAgents": { - "titleMany": "要停止這些代理程式嗎?", "descriptionOne": "刪除時會停止代理程式目前的工作。", - "descriptionMany": "刪除時會停止這些代理程式目前的工作。", - "confirm": "停止代理程式並刪除", - "confirmOne": "停止代理程式並刪除" + "descriptionMany": "刪除時會停止這些代理程式目前的工作。" }, "candidateRow": { "gitChip": { @@ -20915,9 +20891,6 @@ "unstagedSelectionIncludingUntrackedDescription": "這將捨棄尚未暫存的變更,並永久刪除所選內容中所有未追蹤的檔案。此操作無法復原。", "unstagedFilesIncludingUntrackedDescription_other": "這將捨棄這 {{count}} 個檔案中尚未暫存的變更,並永久刪除其中所有未追蹤的檔案。此操作無法復原。", "delete": "刪除", - "deleteCount": "刪除 {{count}} 個", - "deleteUntrackedTitle_one": "刪除 {{count}} 個未追蹤的檔案?", - "deleteUntrackedTitle_other": "刪除 {{count}} 個未追蹤的檔案?", "discard": "捨棄", "discardAll": "全部捨棄", "restore": "還原" @@ -21388,7 +21361,8 @@ }, "common": { "retry": "重試", - "cancel": "取消" + "cancel": "取消", + "delete": "刪除" }, "terminal": { "sessionSaveFailure": { @@ -21513,8 +21487,7 @@ "trashFailed": { "titleRecycleBin": "無法將「{{name}}」移至資源回收筒。要永久刪除嗎?", "titleTrash": "無法將「{{name}}」移至垃圾桶。要永久刪除嗎?", - "description": "此操作無法復原。", - "confirm": "永久刪除" + "description": "此操作無法復原。" } }, "sparsePreset": { @@ -21723,7 +21696,6 @@ "nestedRemoval": { "refreshFailed": "無法重新整理工作區清單", "review": "連同巢狀工作樹一起刪除…", - "title": "刪除工作區和巢狀工作樹?", "description": "這將永久刪除「{{name}}」、下面列出的每個巢狀工作樹及其中的所有檔案(包括未提交的更改)。它們的終端機和代理程式將被停止。", "detached": "分離 HEAD", "loading": "正在檢查巢狀工作樹…", @@ -21731,7 +21703,6 @@ "cancel": "取消", "retry": "重新檢查", "deleting": "正在刪除…", - "confirm": "刪除所有列出的工作樹", "preservedBranches": "部分巢狀工作樹分支已保留" }, "setupTerminalTitle": "初始設定" diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json index a2efd9b54c4..b68f1c671ae 100644 --- a/src/renderer/src/i18n/locales/zh.json +++ b/src/renderer/src/i18n/locales/zh.json @@ -3436,7 +3436,6 @@ "7eee951968": "检查工作区安全", "191f0bc98e": "关闭", "7ae2ad30f4": "刷新", - "b2c1331844": "删除不活动的工作区", "7f451a3e2c": "无法忽略清理建议", "662b8ec3f8": "工作区清理扫描失败", "9cc26c019d": "移除", @@ -3446,7 +3445,6 @@ "1d3503357d": "删除继续期间,你可以关闭此窗口并稍后回来", "4c2990886e": "已删除 {{value0}}/{{value1}}", "86ba852118": "{{value0}},{{value1}} 个失败", - "deleteButtonCount": "删除 {{value0}}", "archivedStatus": "已归档", "74f6c16279": "返回" }, @@ -4036,7 +4034,6 @@ "1d411fb6a5": "为此仓库保存快捷命令", "8f1e971966": "添加快捷命令", "3220e2da27": "该快捷命令将从您保存的列表中删除", - "e8e1a52edb": "删除\"{{value0}}\"?", "37e1bb90ce": "运行:{{value0}}", "77ac113df0": "开始 {{value0}}: {{value1}}", "7b1c9d6ae1": "执行", @@ -5410,7 +5407,6 @@ "loading": "正在加载内容…", "deleteFailed": "Orca 无法从云端删除此内容", "deleted": "已从云端删除", - "confirmDelete": "确认删除", "moreActions": "{{name}} 的更多操作", "deletePackage": "从云端删除" }, @@ -5616,8 +5612,6 @@ "5cc1a6701c": "打开设置", "2b56b35f53": "您可以在\"设置\"中更改此设置", "dd3a45bbbd": "下次我们将跳过此确认", - "fc23c4cbdf": "删除工作区", - "86f0ae1257": "删除工作区", "removeFolderProjectInstead": "请移除文件夹项目,而不是删除此工作区", "gitBlocksMainWorktree": "Git 不允许移除主工作树", "removeBatchFolder_other": "从 Orca 移除{{count}} 个工作区。磁盘上的项目文件夹不会被删除", @@ -5714,11 +5708,9 @@ }, "ProjectGroupDeleteDialog": { "ca65b78f78": "取消", - "591f330288": "删除项目组", "2c14ce677a": "正在删除...", "0e0e6764af": "包含的项目", "55f75628c0": "不会删除磁盘上的项目文件夹", - "fec7e9c8ae": "删除组", "removeContainedProjects_other": "移除包含的 {{count}} 个项目", "deleteGroupDescription": "删除 {{name}}", "moreProjects_other": "另有 {{count}} 个" @@ -6452,9 +6444,7 @@ "DeleteWorktreeDialogFooter": { "c0e972d726": "取消", "cf95e3b5bb": "关闭", - "deleteWorkspace": "删除工作区", "deleting": "正在删除…", - "forceDelete": "强制删除", "forceDeleting": "正在强制删除…" }, "local": { @@ -6756,16 +6746,12 @@ } }, "PreservedBranchBatchReviewDialog": { - "c4bf8e7eaf": "审查保留的分支", "f21976c9a8": "选择要强制删除的本地分支,未选择的分支保留在其存储库中", "38c947f7c5": "选择全部", "9602129d38": "已选择 {{value1}} 中的 {{value0}}", "ee39e872d5": "可能会取消合并", "676db406fd": "头不可用", - "285e1e4882": "取消", - "a0f9863597_one": "强制删除 {{count}} 分支", - "a0f9863597_other": "强制删除 {{count}} 分支", - "a0f9863597": "强制删除 {{count}} 分支" + "285e1e4882": "取消" }, "WorktreeListScrollToTopButton": { "jumpToTop": "跳转到顶部" @@ -8063,7 +8049,6 @@ "c6b155911b": "所有命令", "c36912efd5": "从选项卡栏中的\"快捷命令\"按钮运行它们,或者在任何终端内右键单击", "3d9dc558e8": "该快捷命令将从您保存的列表中删除", - "3edf3deaf8": "删除\"{{value0}}\"?", "9fcfc29519": "插入", "9b3e338d62": "进入", "4ccc63da87": "代理", @@ -13086,7 +13071,6 @@ "abf59262fb": "启动智能体前请查看并编辑完整的命令输入", "4ede779461": "使用 AI 解决评审冲突", "3b203c62f8": "这将从 PR 中永久删除该评论", - "ea9b649ce3": "删除评论?", "5788d1059d": "无法更新评论主题,检查 GitHub API 预算", "07871c0589": "链接另一个 PR", "7f4489f370": "刷新", @@ -13954,8 +13938,7 @@ "5ddd8cac7f": "放弃所有已暂存的更改?", "d4df3a61df": "放弃对\"{{value0}}\"的更改?", "5c0bdbc4cb": "恢复\"{{value0}}\"?", - "d97bf697c9": "这将永久删除该文件,此操作无法撤销", - "96c772bee9": "删除\"{{value0}}\"?" + "d97bf697c9": "这将永久删除该文件,此操作无法撤销" }, "dialog": { "3bc61dc989": "取消", @@ -14191,7 +14174,6 @@ "unresolved": "无法确定哪个主机拥有此工作区,请检查连接后重试" }, "useFileDeletion": { - "d979a4fbb5": "永久删除\"{{value0}}\"?", "92276aceb7": "删除", "7fb9435c86": "这会永久删除远程主机上的目录及其内容,此操作无法撤销", "23e98f192f": "这会永久删除远程主机上的文件,此操作无法撤销", @@ -14372,7 +14354,6 @@ "resumeInNewCliTooltipChat": "此聊天保持不变" }, "AiVaultSessionDeleteDialog": { - "title": "要删除此会话吗?", "description": "\"{{value0}}\"将被删除,删除后,也将无法通过 {{value1}} 自身的命令行恢复", "remoteDescription": "“{{value0}}”将从 {{value2}} 删除。删除后,也无法再从 {{value1}} 自己的命令行恢复", "confirm": "删除" @@ -17322,9 +17303,7 @@ "2faecab10b": "立即运行", "13118faadf": "未知项目", "73f630b49d": "取消", - "9adfab2596": "删除外部自动化", "1e2e41392f": "不要再问", - "080dcb5fbb": "删除自动化", "19a6e30eae": "刷新自动化", "77c2778945": "自动化", "e1bf9b1512": "工作区不可用", @@ -18170,7 +18149,6 @@ }, "artifacts": { "ArtifactsPage": { - "deleteTitle": "删除产物?", "deleteDescription": "“{{name}}” 将无法再通过其公开链接访问", "delete": "删除", "signInAgain": "重新登录 Orca 以加载产物", @@ -20080,11 +20058,8 @@ "candidateName": "{{value1}} 上的 {{value0}}" }, "stopAgents": { - "titleMany": "停止这些代理?", "descriptionOne": "删除将停止代理当前任务", - "descriptionMany": "删除将停止这些代理当前任务", - "confirm": "停止代理并删除", - "confirmOne": "停止代理并删除" + "descriptionMany": "删除将停止这些代理当前任务" }, "candidateRow": { "gitChip": { @@ -21322,10 +21297,7 @@ "delete": "删除", "restore": "恢复", "discard": "放弃", - "discardAll": "全部丢弃", - "deleteUntrackedTitle_one": "删除 {{count}} 个未跟踪文件?", - "deleteUntrackedTitle_other": "删除 {{count}} 个未跟踪文件?", - "deleteCount": "删除 {{count}} 个" + "discardAll": "全部丢弃" }, "createReviewFailure": { "authRequiredCommand": "{{providerName}} 未通过身份验证。下一步:在此环境中运行 {{command}}", @@ -21750,7 +21722,8 @@ "common": { "retry": "重试", "cancel": "取消", - "unassigned": "未分配" + "unassigned": "未分配", + "delete": "删除" }, "fileExplorer": { "root": { @@ -21770,8 +21743,7 @@ "trashFailed": { "titleRecycleBin": "无法将“{{name}}”移到回收站。是否永久删除?", "titleTrash": "无法将“{{name}}”移到废纸篓。是否永久删除?", - "description": "此操作无法撤销", - "confirm": "永久删除" + "description": "此操作无法撤销" } }, "sparsePreset": { @@ -22138,7 +22110,6 @@ "nestedRemoval": { "refreshFailed": "无法刷新工作区列表", "review": "连同嵌套工作树一起删除…", - "title": "删除工作区和嵌套工作树?", "description": "这将永久删除“{{name}}”、下面列出的每个嵌套工作树及其中的所有文件(包括未提交的更改),它们的终端和代理将被停止", "detached": "分离 HEAD", "loading": "正在检查嵌套工作树…", @@ -22146,7 +22117,6 @@ "cancel": "取消", "retry": "重新检查", "deleting": "正在删除…", - "confirm": "删除所有列出的工作树", "preservedBranches": "部分嵌套工作树分支已保留" }, "setupTerminalTitle": "初始设置" diff --git a/src/shared/plugins/languages/protected-copy-keys.json b/src/shared/plugins/languages/protected-copy-keys.json index e7d5533b609..7e3fc6d70c8 100644 --- a/src/shared/plugins/languages/protected-copy-keys.json +++ b/src/shared/plugins/languages/protected-copy-keys.json @@ -74,13 +74,11 @@ "auto.components.agent-workspace-trust-decision.setting-global-off", "auto.components.agent-workspace-trust-decision.setting-title", "auto.components.agent-workspace-trust-decision.trust-repository", - "auto.components.automations.AutomationsPage.080dcb5fbb", "auto.components.automations.AutomationsPage.15e0bfb13b", "auto.components.automations.AutomationsPage.1e2e41392f", "auto.components.automations.AutomationsPage.36f71740a7", "auto.components.automations.AutomationsPage.73f630b49d", "auto.components.automations.AutomationsPage.8500baacb4", - "auto.components.automations.AutomationsPage.9adfab2596", "auto.components.automations.AutomationsPage.cd8397cc32", "auto.components.automations.AutomationsPage.deleteAutomationDescription", "auto.components.automations.AutomationsPage.deleteExternalAutomationDescription", @@ -206,7 +204,6 @@ "auto.components.right.sidebar.source.control.discard.confirmation.2ae5a785b3", "auto.components.right.sidebar.source.control.discard.confirmation.5c0bdbc4cb", "auto.components.right.sidebar.source.control.discard.confirmation.5ddd8cac7f", - "auto.components.right.sidebar.source.control.discard.confirmation.96c772bee9", "auto.components.right.sidebar.source.control.discard.confirmation.d4df3a61df", "auto.components.right.sidebar.source.control.discard.confirmation.d97bf697c9", "auto.components.right.sidebar.source.control.discard.confirmation.ddf36f291c", @@ -443,10 +440,8 @@ "auto.components.sidebar.DeleteWorktreeDialog.42e610d6cf", "auto.components.sidebar.DeleteWorktreeDialog.4f6750ca7b", "auto.components.sidebar.DeleteWorktreeDialog.5cc1a6701c", - "auto.components.sidebar.DeleteWorktreeDialog.86f0ae1257", "auto.components.sidebar.DeleteWorktreeDialog.dd3a45bbbd", "auto.components.sidebar.DeleteWorktreeDialog.deleteSingleWorktreeAndBranch", - "auto.components.sidebar.DeleteWorktreeDialog.fc23c4cbdf", "auto.components.sidebar.DeleteWorktreeDialog.gitBlocksMainWorktree", "auto.components.sidebar.DeleteWorktreeDialog.removeBatchFolder", "auto.components.sidebar.DeleteWorktreeDialog.removeBatchGit", @@ -458,9 +453,7 @@ "auto.components.sidebar.DeleteWorktreeDialog.removeWithChildrenMixed", "auto.components.sidebar.DeleteWorktreeDialogFooter.c0e972d726", "auto.components.sidebar.DeleteWorktreeDialogFooter.cf95e3b5bb", - "auto.components.sidebar.DeleteWorktreeDialogFooter.deleteWorkspace", "auto.components.sidebar.DeleteWorktreeDialogFooter.deleting", - "auto.components.sidebar.DeleteWorktreeDialogFooter.forceDelete", "auto.components.sidebar.DeleteWorktreeDialogFooter.forceDeleting", "auto.components.sidebar.DeleteWorktreeDirtyChangeHint.changesLost", "auto.components.sidebar.DeleteWorktreeDirtyChangeHint.loadedChangedPaths", @@ -524,17 +517,13 @@ "auto.components.sidebar.PreservedBranchBatchReviewDialog.38c947f7c5", "auto.components.sidebar.PreservedBranchBatchReviewDialog.676db406fd", "auto.components.sidebar.PreservedBranchBatchReviewDialog.9602129d38", - "auto.components.sidebar.PreservedBranchBatchReviewDialog.a0f9863597", - "auto.components.sidebar.PreservedBranchBatchReviewDialog.c4bf8e7eaf", "auto.components.sidebar.PreservedBranchBatchReviewDialog.ee39e872d5", "auto.components.sidebar.PreservedBranchBatchReviewDialog.f21976c9a8", "auto.components.sidebar.ProjectGroupDeleteDialog.0e0e6764af", "auto.components.sidebar.ProjectGroupDeleteDialog.2c14ce677a", "auto.components.sidebar.ProjectGroupDeleteDialog.55f75628c0", - "auto.components.sidebar.ProjectGroupDeleteDialog.591f330288", "auto.components.sidebar.ProjectGroupDeleteDialog.ca65b78f78", "auto.components.sidebar.ProjectGroupDeleteDialog.deleteGroupDescription", - "auto.components.sidebar.ProjectGroupDeleteDialog.fec7e9c8ae", "auto.components.sidebar.ProjectGroupDeleteDialog.moreProjects", "auto.components.sidebar.ProjectGroupDeleteDialog.removeContainedProjects", "auto.components.sidebar.RemoveFolderDialog.4dc5b5065b", @@ -620,7 +609,6 @@ "auto.components.workspace.cleanup.WorkspaceCleanupDialog.9623a5107d", "auto.components.workspace.cleanup.WorkspaceCleanupDialog.archivedStatus", "auto.components.workspace.cleanup.WorkspaceCleanupDialog.b6bae1eed1", - "auto.components.workspace.cleanup.WorkspaceCleanupDialog.deleteButtonCount", "auto.components.workspace.cleanup.WorkspaceCleanupDialog.e8b3741ff7", "auto.components.workspace.cleanup.WorkspaceCleanupDialog.e97e4580c7", "auto.components.workspace.cleanup.WorkspaceCleanupDialog.mergedStatus", @@ -712,6 +700,7 @@ "browserExtensions.uninstall.title", "browserExtensions.unsupported.identity", "browserExtensions.unsupported.line", + "common.delete", "components.managed-runtime.removeConfirm", "components.managed-runtime.removeDescription", "components.managed-runtime.removeKeep", @@ -773,7 +762,7 @@ "components.workspace.cleanup.browse.gitStatusCheckFailed", "components.workspace.cleanup.browse.gitStatusUnverified", "components.workspace.cleanup.browse.projectNamed", - "components.workspace.cleanup.confirm.deleteTitle", + "components.workspace.cleanup.browse.workspaceCount", "components.workspace.cleanup.confirm.deletingTitle", "components.workspace.cleanup.host.candidateName", "components.workspace.cleanup.host.label", @@ -785,11 +774,8 @@ "components.workspace.cleanup.relativeTime.justNow", "components.workspace.cleanup.relativeTime.minutesAgo", "components.workspace.cleanup.relativeTime.never", - "components.workspace.cleanup.stopAgents.confirm", - "components.workspace.cleanup.stopAgents.confirmOne", "components.workspace.cleanup.stopAgents.descriptionMany", "components.workspace.cleanup.stopAgents.descriptionOne", - "components.workspace.cleanup.stopAgents.titleMany", "components.workspace.delete.changes.checking", "components.workspace.delete.changes.checkingDetails", "components.workspace.delete.changes.none", @@ -799,7 +785,6 @@ "components.workspace.delete.unsavedEdits.blocked", "components.workspace.delete.unsavedEdits.discarded", "components.workspace.delete.unsavedEdits.moreFiles", - "fileExplorer.trashFailed.confirm", "fileExplorer.trashFailed.description", "fileExplorer.trashFailed.titleRecycleBin", "fileExplorer.trashFailed.titleTrash", @@ -997,10 +982,8 @@ "settings.browser.sitePermissions.search.permissions", "settings.browser.sitePermissions.title", "sourceControl.discard.delete", - "sourceControl.discard.deleteCount", "sourceControl.discard.deleteUntrackedFileDescription", "sourceControl.discard.deleteUntrackedFilesDescription", - "sourceControl.discard.deleteUntrackedTitle", "sourceControl.discard.discard", "sourceControl.discard.discardAll", "sourceControl.discard.restore", @@ -1080,7 +1063,6 @@ "worktree.delete.detachingChildren", "worktree.delete.nestedChildrenCannotBeKept", "worktree.nestedRemoval.cancel", - "worktree.nestedRemoval.confirm", "worktree.nestedRemoval.deleting", "worktree.nestedRemoval.description", "worktree.nestedRemoval.detached", @@ -1089,7 +1071,6 @@ "worktree.nestedRemoval.refreshFailed", "worktree.nestedRemoval.retry", "worktree.nestedRemoval.review", - "worktree.nestedRemoval.title", "worktree.setupTrust.branchDiffersBody", "worktree.setupTrust.branchDiffersTitle", "worktree.setupTrust.branchSetupScript" diff --git a/tests/e2e/modal-scene-blur.spec.ts b/tests/e2e/modal-scene-blur.spec.ts index 7d8b0d846d2..a43a0d73344 100644 --- a/tests/e2e/modal-scene-blur.spec.ts +++ b/tests/e2e/modal-scene-blur.spec.ts @@ -1,5 +1,4 @@ import { test, expect } from './helpers/orca-app' -import { openSidebarProjectDialog } from './helpers/sidebar-project-dialog' test.use({ seedTestRepo: false, orcaAppExtraEnv: { ORCA_BACKGROUND_LAUNCH: '1' } }) @@ -21,10 +20,15 @@ for (const theme of ['dark', 'light'] as const) { const scene = orcaPage.locator('.app-layout > :first-child') await expect(scene).toHaveCSS('filter', 'none') - await openSidebarProjectDialog(orcaPage) + await orcaPage + .locator('[data-sidebar-header-actions]') + .getByRole('button', { name: 'Add project', exact: true }) + .click() + await orcaPage.getByRole('menuitem', { name: /Clone from URL/ }).click() + const dialog = orcaPage.getByRole('dialog', { name: 'Clone from URL', exact: true }) + await expect(dialog).toBeVisible() await expect(scene).toHaveCSS('filter', 'blur(2px)') await expect(orcaPage.locator('.app-layout')).toHaveCSS('filter', 'none') - const dialog = orcaPage.getByRole('dialog', { name: /Add a project/i }) await expect(dialog).toHaveCSS('filter', 'none') await expect(dialog).toHaveCSS('backdrop-filter', 'none') await expect(orcaPage.locator('[data-slot="dialog-overlay"]')).toHaveCSS( @@ -63,7 +67,7 @@ test('Space window dialogs blur their scene and release it on close', async ({ branches: [{ worktreeId: 'blur-preview', branchName: 'preview' }] }) }) - const dialog = spacePage.getByRole('dialog', { name: 'Review kept branches' }) + const dialog = spacePage.getByRole('dialog', { name: 'Delete', exact: true }) await expect(dialog).toBeVisible() await expect(scene).toHaveCSS('filter', 'blur(2px)') await expect(dialog).toHaveCSS('filter', 'none') diff --git a/tests/e2e/source-control-discard-confirmation.spec.ts b/tests/e2e/source-control-discard-confirmation.spec.ts index 422465009ff..0e0cb9c191f 100644 --- a/tests/e2e/source-control-discard-confirmation.spec.ts +++ b/tests/e2e/source-control-discard-confirmation.spec.ts @@ -94,7 +94,9 @@ async function confirmPendingDelete(page: Page): Promise { } async function expectDeleteDialogLayout(page: Page, fileName: string): Promise { - const dialog = page.getByRole('dialog', { name: `Delete "${fileName}"?` }) + const dialog = page + .getByRole('dialog', { name: 'Delete', exact: true }) + .filter({ hasText: fileName }) await expect(dialog).toBeVisible() await expect .poll( @@ -108,13 +110,11 @@ async function expectDeleteDialogLayout(page: Page, fileName: string): Promise('button')].every((button) => { const rect = button.getBoundingClientRect() return rect.left >= panel.left && rect.right <= panel.right }) return ( - titleRect.height > lineHeight * 1.5 && titleRect.left >= panel.left && titleRect.right <= panel.right && footerRect.left >= panel.left && @@ -123,7 +123,7 @@ async function expectDeleteDialogLayout(page: Page, fileName: string): Promise { await deleteUntrackedFileFromRow(row) await confirmPendingDelete(orcaPage) - await expect( - orcaPage.getByRole('dialog', { name: `Delete "${seededFile.fileName}"?` }) - ).toHaveCount(0) + await expect(orcaPage.getByRole('dialog', { name: 'Delete', exact: true })).toHaveCount(0) await expect(row).toHaveCount(0, { timeout: 10_000 }) await refreshGitStatus(orcaPage) diff --git a/tests/e2e/worktree-delete-shortcut.spec.ts b/tests/e2e/worktree-delete-shortcut.spec.ts index 2a3deb884d7..a24a6922c7b 100644 --- a/tests/e2e/worktree-delete-shortcut.spec.ts +++ b/tests/e2e/worktree-delete-shortcut.spec.ts @@ -71,7 +71,7 @@ test.describe('Worktree Delete Shortcut', () => { const dialog = orcaPage.getByRole('dialog') await expect(dialog).toBeVisible() - await dialog.getByRole('button', { name: 'Delete Workspace', exact: true }).click() + await dialog.getByRole('button', { name: 'Delete', exact: true }).click() await expect .poll(async () => getAllWorktreeIds(orcaPage), { diff --git a/tests/e2e/worktree-lineage-toggle-dialog.spec.ts b/tests/e2e/worktree-lineage-toggle-dialog.spec.ts index 8b21d1724b7..7022c71e9dc 100644 --- a/tests/e2e/worktree-lineage-toggle-dialog.spec.ts +++ b/tests/e2e/worktree-lineage-toggle-dialog.spec.ts @@ -62,9 +62,9 @@ test('locally controlled project-group modal keeps child-toggle input', async ({ await orcaPage.keyboard.press('ControlOrMeta+Alt+KeyH') expect(await orcaPage.evaluate(() => window.__store?.getState().collapsedGroups.size)).toBe(0) await orcaPage.getByRole('menuitem', { name: /Delete/i }).click() - const dialog = orcaPage.getByRole('dialog', { name: 'Delete Project Group' }) + const dialog = orcaPage.getByRole('dialog', { name: 'Delete' }) await expect(dialog).toBeVisible() - await expect(dialog.getByRole('button', { name: 'Delete Group', exact: true })).toBeFocused() + await expect(dialog.getByRole('button', { name: 'Delete', exact: true })).toBeFocused() expect(await orcaPage.evaluate(() => window.__store?.getState().activeModal)).toBe('none') const before = await orcaPage.evaluate(() => [...(window.__store?.getState().collapsedGroups ?? [])].sort() From 4f3306a631401f9bee7684978f77cdbe60a4369c Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 00:21:35 -0800 Subject: [PATCH 048/178] test: remove low-value cache, visual probe, and env cases (#41898) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../integration-token-file-cache.test.ts | 25 ------------------- ...ative-chat-visual-markdown-syntax.test.tsx | 7 ------ src/shared/jcode-runtime-dir.test.ts | 13 +--------- 3 files changed, 1 insertion(+), 44 deletions(-) diff --git a/src/main/credentials/integration-token-file-cache.test.ts b/src/main/credentials/integration-token-file-cache.test.ts index 3707dcae02b..f574052cc79 100644 --- a/src/main/credentials/integration-token-file-cache.test.ts +++ b/src/main/credentials/integration-token-file-cache.test.ts @@ -29,31 +29,6 @@ function createCache() { } describe('integration token file cache', () => { - it('bounds decrypted credentials at 128 entries', () => { - const cache = createCache() - for (let index = 0; index < 132; index += 1) { - const id = `credential-${index}` - writeFileSync(filePath(id), `token-${index}`) - expect(cache.read(id)).toBe(`token-${index}`) - } - expect(cache.read('credential-131', false)).toBe('token-131') - expect(cache.read('credential-0', false)).toBeNull() - expect(decrypt).toHaveBeenCalledTimes(132) - }) - - it('keeps recently used credentials when the bound evicts the oldest entry', () => { - const cache = createCache() - for (let index = 0; index < 128; index += 1) { - writeFileSync(filePath(String(index)), `token-${index}`) - cache.read(String(index)) - } - expect(cache.read('0', false)).toBe('token-0') - writeFileSync(filePath('next'), 'token-next') - cache.read('next') - expect(cache.read('0', false)).toBe('token-0') - expect(cache.read('1', false)).toBeNull() - }) - it('bounds errors, keeps status lazy, and permits retry after keychain approval', () => { const cache = createCache() decrypt.mockImplementation(() => { diff --git a/src/renderer/src/components/native-chat/media/visuals/native-chat-visual-markdown-syntax.test.tsx b/src/renderer/src/components/native-chat/media/visuals/native-chat-visual-markdown-syntax.test.tsx index cbe7be38f76..5fa592445c8 100644 --- a/src/renderer/src/components/native-chat/media/visuals/native-chat-visual-markdown-syntax.test.tsx +++ b/src/renderer/src/components/native-chat/media/visuals/native-chat-visual-markdown-syntax.test.tsx @@ -61,13 +61,6 @@ function mountedFiles(markup: string): string[] { const LINE = '::orca-visual{file="usage.html" title="Usage by day"}' describe('remarkNativeChatVisuals', () => { - it('does not mount a raw HTML placeholder forged without the nonce', () => { - const markup = render( - '
x
\n\nafter' - ) - expect(mountedFiles(markup)).toEqual([]) - }) - it('keeps reference links defined after the directive working', () => { const markup = render(`See [the docs][d].\n\n${LINE}\n\n[d]: https://example.com/docs`) expect(mountedFiles(markup)).toEqual(['usage.html']) diff --git a/src/shared/jcode-runtime-dir.test.ts b/src/shared/jcode-runtime-dir.test.ts index 84ee5e49c42..115d3b67c45 100644 --- a/src/shared/jcode-runtime-dir.test.ts +++ b/src/shared/jcode-runtime-dir.test.ts @@ -3,10 +3,8 @@ import { rm, stat } from 'node:fs/promises' import { buildJcodeRuntimeDir, - buildJcodeRuntimeDirEnv, ensureJcodeRuntimeDir, - JCODE_RUNTIME_DIR_ENV_KEY, - shouldInjectJcodeRuntimeDir + JCODE_RUNTIME_DIR_ENV_KEY } from './jcode-runtime-dir' describe('shared jcode-runtime-dir', () => { @@ -14,15 +12,6 @@ describe('shared jcode-runtime-dir', () => { expect(buildJcodeRuntimeDir('tab-a:leaf-1')).not.toBe(buildJcodeRuntimeDir('tab-a:leaf-2')) }) - it('builds the env only on unix platforms', () => { - expect(buildJcodeRuntimeDirEnv('tab-a:leaf-b', 'darwin')).toEqual({ - [JCODE_RUNTIME_DIR_ENV_KEY]: buildJcodeRuntimeDir('tab-a:leaf-b') - }) - expect(buildJcodeRuntimeDirEnv('tab-a:leaf-b', 'linux')).toBeDefined() - expect(buildJcodeRuntimeDirEnv('tab-a:leaf-b', 'win32')).toBeUndefined() - expect(shouldInjectJcodeRuntimeDir('win32')).toBe(false) - }) - it('ensures the runtime dir exists and returns the env on unix', async () => { const paneKey = 'tab-a:leaf-c' const dir = buildJcodeRuntimeDir(paneKey) From 35e0f9a9f2b1573f75fbe73a41a5f63b03e5aa50 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 00:26:28 -0800 Subject: [PATCH 049/178] test: remove route forwarding and internal mobile marker cases (#41900) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- ...obile-markdown-preview-placeholder.test.ts | 20 -------------- .../native-chat-github-route-repo.test.ts | 27 ------------------- 2 files changed, 47 deletions(-) delete mode 100644 mobile/src/components/mobile-markdown-preview-placeholder.test.ts diff --git a/mobile/src/components/mobile-markdown-preview-placeholder.test.ts b/mobile/src/components/mobile-markdown-preview-placeholder.test.ts deleted file mode 100644 index 94b266df9ad..00000000000 --- a/mobile/src/components/mobile-markdown-preview-placeholder.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { normalizeMobileMarkdownPreviewHtml } from './mobile-markdown-preview-html' - -const marker = '\uE000ORCA_MD_CODE_' -const suffix = '\uE000' - -describe('mobile Markdown code placeholder collisions', () => { - it.each([0, 1, 2, 15, 128, 16384])('preserves a literal marker with %i underscores', (length) => { - const literal = `${marker}${'_'.repeat(length)}0${suffix}` - const input = `${literal} and \`Array\`\n\n\`\`\`html\n

literal

\n\`\`\`` - expect(normalizeMobileMarkdownPreviewHtml(input)).toBe(input) - }) - - it('handles adjacent markers and repeated maximum suffixes', () => { - const literal = `${marker}${marker}__0${suffix}${marker}__1${suffix}${marker}_2${suffix}` - expect(normalizeMobileMarkdownPreviewHtml(`

${literal} and \`

\`

`)).toBe( - `${literal} and \`
\`` - ) - }) -}) diff --git a/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-repo.test.ts b/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-repo.test.ts index b5c81a6fa66..53de19f60a0 100644 --- a/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-repo.test.ts +++ b/src/renderer/src/components/native-chat/links/previews/native-chat-github-route-repo.test.ts @@ -101,33 +101,6 @@ describe('GitHub preview routing for a chat without a repo of its own', () => { expect(pickGitHubPreviewRouteRepo([{ repo: orca, originKey: null }], privatePr)).toBeNull() }) - it('looks a private PR up through a local repo on github.com, as a preview', async () => { - const orca = repo('orca') - mocks.repos = [repo('folder', { kind: 'folder' }), orca] - mocks.slugs.set('orca', 'stablyai/orca') - const item = { type: 'pr', number: 685, title: 'perf: cacheable projections', state: 'draft' } - mocks.lookup.mockResolvedValue(item) - - await expect(loadRoutedGitHubLinkPreview('local', privatePr)).resolves.toBe(item) - expect(mocks.lookup.mock.calls[0][0]).toMatchObject({ - repoPath: '/src/orca', - repoId: 'orca', - owner: 'stablyai', - repo: 'orca-private', - number: 685, - type: 'pr', - preview: true - }) - }) - - it('answers nothing, leaving the web card, when no repo on the chat host uses GitHub', async () => { - mocks.repos = [repo('ghes')] - mocks.slugs.set('ghes', 'git.corp.example/team/app') - const link = parseGitHubIssueOrPRLink('https://github.com/stablyai/orca-private/pull/686')! - await expect(loadRoutedGitHubLinkPreview('local', link)).resolves.toBeNull() - expect(mocks.lookup).not.toHaveBeenCalled() - }) - it('uses the remaining repo after the repo that answered is removed', async () => { const link = parseGitHubIssueOrPRLink('https://github.com/acme/private/pull/701')! const alice = repo('alice', { ghAccount: { host: 'github.com', user: 'alice' } }) From ad0dfec9d4c0f95cd0c99f18e17a0b4214fc9eb0 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 13 Nov 2026 00:41:53 -0800 Subject: [PATCH 050/178] fix(ime): keep plain-committed Hangul so macOS 2-Set can rebuild it in place (#41884) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(ime): keep plain-committed Hangul so macOS 2-Set can rebuild it in place Right after an input-source switch, macOS 2-Set can commit a lone jamo as plain text and then rebuild it (ㅇ + ㅏ → 아) by replacing that committed range. The native text forwarder emptied the field after every commit, so the replacement had nothing to land on and the vowel was dropped (#9738). Keep Hangul commits in the field (Latin commits still clear for #11504), and erase replaced text from the PTY before the replacement is sent. * fix(ime): only erase retained Hangul when the replacement is forwarded and still at the PTY cursor Drop retention on blur, on any composition start, and on chord/named keydowns the pane handles before the forwarder (macOS Ctrl+C, non-Latin control chords). Erase only for insertText/composition replacements that end at the field tail. * ci(e2e): route terminal IME changes to IME specs again after the input/ owner move #39113 moved IME and keyboard policy files under terminal-pane/input/, but the native IBus and synthetic IME routes still matched the old paths, so IME changes selected neither lane. --- .../source-routing/pr-e2e-source-routing.mjs | 5 +- .../ime/terminal-ime-native-text-forwarder.ts | 12 +- ...erminal-ime-retained-hangul-commit.test.ts | 127 ++++++++++++++++ .../terminal-ime-retained-hangul-commit.ts | 138 ++++++++++++++++++ 4 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 src/renderer/src/components/terminal-pane/input/ime/terminal-ime-retained-hangul-commit.test.ts create mode 100644 src/renderer/src/components/terminal-pane/input/ime/terminal-ime-retained-hangul-commit.ts diff --git a/config/scripts/workflow/source-routing/pr-e2e-source-routing.mjs b/config/scripts/workflow/source-routing/pr-e2e-source-routing.mjs index 185f388ef22..418312cf2d3 100644 --- a/config/scripts/workflow/source-routing/pr-e2e-source-routing.mjs +++ b/config/scripts/workflow/source-routing/pr-e2e-source-routing.mjs @@ -19,8 +19,9 @@ const isProductSource = (file) => !/\.test\.tsx?$/.test(file) && !isUnitTestSupp const XTERM_CORE_PATCH = /^config\/patches\/(?:@xterm__xterm@[^/]+\.patch|xterm-src\/@xterm__xterm@[^/]+\.src\.patch)$/ +// Paths follow the input/ime and input/keyboard owners (#39113); the pre-move paths matched nothing. const NATIVE_IME_PRODUCT_SOURCE = - /^(?:src\/shared\/terminal-unicode-provider\.ts$|src\/renderer\/src\/lib\/pane-manager\/terminal-ime-|src\/renderer\/src\/components\/terminal-pane\/(?:terminal-ime-|terminal-ios-hangul-|xterm-bypass-policy))/ + /^(?:src\/shared\/terminal-unicode-provider\.ts$|src\/renderer\/src\/lib\/pane-manager\/terminal-input\/terminal-ime-|src\/renderer\/src\/components\/terminal-pane\/input\/(?:ime\/|keyboard\/xterm-bypass-policy|terminal-pane-pane-input\.ts$))/ /** The harness itself: the session runner, the boundary probes, and the native specs. */ const NATIVE_IME_HARNESS = @@ -401,7 +402,7 @@ export const PR_E2E_SOURCE_ROUTES = [ matches: (file) => isProductSource(file) && (XTERM_CORE_PATCH.test(file) || - /^src\/renderer\/src\/components\/terminal-pane\/(?:terminal-ime-|use-terminal-pane-lifecycle|xterm-bypass-policy|terminal-option-shortcut-policy)/.test( + /^src\/renderer\/src\/components\/terminal-pane\/(?:input\/ime\/terminal-ime-|controller\/use-terminal-pane-lifecycle|input\/keyboard\/(?:xterm-bypass-policy|terminal-option-shortcut-policy))/.test( file )) }, diff --git a/src/renderer/src/components/terminal-pane/input/ime/terminal-ime-native-text-forwarder.ts b/src/renderer/src/components/terminal-pane/input/ime/terminal-ime-native-text-forwarder.ts index b4d9f4a3c10..ac4a8b58577 100644 --- a/src/renderer/src/components/terminal-pane/input/ime/terminal-ime-native-text-forwarder.ts +++ b/src/renderer/src/components/terminal-pane/input/ime/terminal-ime-native-text-forwarder.ts @@ -6,6 +6,7 @@ import { type ImeReleaseKeyEvent } from './terminal-ime-kitty-commit-encoding' import { getLayoutCharacterForCode } from '../../../../lib/keyboard-layout/layout-base-character' +import { installTerminalImeRetainedHangulCommit } from './terminal-ime-retained-hangul-commit' // Claim plain printable keydowns so native input commits supply substituted text before xterm can cancel it; Linux fcitx4 candidate picks need the same path (#28213). Match key length, never source identity or character tables. @@ -115,6 +116,11 @@ export function installTerminalImeNativeTextForwarder(args: { let pendingCommit: PendingCommit | null = null // Rollover and repeat leave multiple keys held; each needs its own release record. const claimedKeyRecords = new Map() + const retainedHangulCommit = installTerminalImeRetainedHangulCommit({ + terminalElement, + sendInput: args.sendInput, + getKittyKeyboardFlags: () => args.getKittyKeyboardFlags?.() ?? 0 + }) const findClaimedRecordId = (event: ImeNativeTextKeyEvent): string | null => { const direct = claimedKeyId(event) @@ -294,7 +300,10 @@ export function installTerminalImeNativeTextForwarder(args: { } event.stopImmediatePropagation() // Clear committed text: macOS reads preceding field contents for double-space period substitution (#11504). - if (event.target instanceof HTMLTextAreaElement) { + if ( + event.target instanceof HTMLTextAreaElement && + !(event.data && retainedHangulCommit.retain(event.target, event.data)) + ) { event.target.value = '' } } @@ -323,6 +332,7 @@ export function installTerminalImeNativeTextForwarder(args: { claimKeyEvent, dispose: () => { cancelPending() + retainedHangulCommit.dispose() terminalElement.removeEventListener( XTERM_COMPOSITION_TRANSACTION_ACCEPTED_EVENT, markCompositionTransactionAccepted, diff --git a/src/renderer/src/components/terminal-pane/input/ime/terminal-ime-retained-hangul-commit.test.ts b/src/renderer/src/components/terminal-pane/input/ime/terminal-ime-retained-hangul-commit.test.ts new file mode 100644 index 00000000000..c61dd906d7e --- /dev/null +++ b/src/renderer/src/components/terminal-pane/input/ime/terminal-ime-retained-hangul-commit.test.ts @@ -0,0 +1,127 @@ +// @vitest-environment happy-dom +/** macOS 2-Set right after an input-source switch: plain-commits ㅇ, then rebuilds it as 아 by replacing it (#9738, m4air trace). */ +import { Terminal } from '@xterm/xterm' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { installTerminalImeNativeTextForwarder } from './terminal-ime-native-text-forwarder' + +function open() { + const container = document.createElement('div') + document.body.appendChild(container) + const terminal = new Terminal() + terminal.open(container) + const textarea = terminal.textarea! + const forwarder = installTerminalImeNativeTextForwarder({ + terminalElement: terminal.element, + isComposing: () => false, + sendInput: (data) => terminal.input(data), + getKittyKeyboardFlags: () => 0 + }) + terminal.attachCustomKeyEventHandler((event) => !forwarder.claimKeyEvent(event)) + const emitted: string[] = [] + terminal.onData((d) => emitted.push(d)) + return { emitted, terminal, textarea, forwarder, container } +} + +function keydown(textarea: HTMLTextAreaElement, key: string, code: string, keyCode: number) { + textarea.dispatchEvent(new KeyboardEvent('keydown', { key, code, keyCode, bubbles: true })) +} + +function plainCommit(textarea: HTMLTextAreaElement, data: string) { + textarea.setRangeText(data, textarea.selectionStart, textarea.selectionEnd, 'end') + textarea.dispatchEvent(new InputEvent('input', { data, inputType: 'insertText', bubbles: true })) +} + +// Blink selects the replacement range, fires beforeinput, then writes the text. +function replacingCommit(textarea: HTMLTextAreaElement, start: number, end: number, data: string) { + textarea.setSelectionRange(start, end) + textarea.dispatchEvent( + new InputEvent('beforeinput', { data, inputType: 'insertText', bubbles: true }) + ) + plainCommit(textarea, data) +} + +describe('#9738 plain-committed jamo rebuilt in place', () => { + let pane: ReturnType + + beforeEach(() => { + // happy-dom has no canvas, and xterm measures text on open(). + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: xterm only calls measureText on this stub. + vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue({ + measureText: () => ({ width: 10 }) + } as unknown as CanvasRenderingContext2D) + pane = open() + }) + + afterEach(() => { + pane.forwarder.dispose() + pane.terminal.dispose() + pane.container.remove() + vi.restoreAllMocks() + }) + + it('keeps the jamo for the rebuild and erases it from the PTY when replaced', () => { + keydown(pane.textarea, 'ㅇ', 'KeyD', 68) + plainCommit(pane.textarea, 'ㅇ') + expect(pane.textarea.value).toBe('ㅇ') + + keydown(pane.textarea, 'ㅏ', 'KeyK', 75) + replacingCommit(pane.textarea, 0, 1, '아') + + expect(pane.emitted.join('')).toBe('ㅇ\x7f아') + }) + + it('erases the jamo before a composition that replaces it begins', () => { + keydown(pane.textarea, 'ㅇ', 'KeyD', 68) + plainCommit(pane.textarea, 'ㅇ') + + pane.textarea.setSelectionRange(0, 1) + pane.textarea.dispatchEvent(new CompositionEvent('compositionstart', { bubbles: true })) + + expect(pane.emitted.join('')).toBe('ㅇ\x7f') + }) + + it('does not erase for an edit whose replacement never reaches the PTY', () => { + keydown(pane.textarea, 'ㅇ', 'KeyD', 68) + plainCommit(pane.textarea, 'ㅇ') + + pane.textarea.setSelectionRange(0, 1) + pane.textarea.dispatchEvent( + new InputEvent('beforeinput', { inputType: 'insertReplacementText', bubbles: true }) + ) + + expect(pane.emitted.join('')).toBe('ㅇ') + }) + + it('does not erase once an ordinary composition has begun after the jamo', () => { + keydown(pane.textarea, 'ㅇ', 'KeyD', 68) + plainCommit(pane.textarea, 'ㅇ') + + pane.textarea.dispatchEvent(new CompositionEvent('compositionstart', { bubbles: true })) + pane.textarea.setSelectionRange(0, 1) + pane.textarea.dispatchEvent( + new InputEvent('beforeinput', { inputType: 'insertCompositionText', bubbles: true }) + ) + + expect(pane.emitted.join('')).toBe('ㅇ') + }) + + it('removes the jamo from the field on Ctrl+C the pane handles ahead of the forwarder', () => { + pane.terminal.attachCustomKeyEventHandler( + (event) => !event.ctrlKey && !pane.forwarder.claimKeyEvent(event) + ) + keydown(pane.textarea, 'ㅇ', 'KeyD', 68) + plainCommit(pane.textarea, 'ㅇ') + + pane.textarea.dispatchEvent( + new KeyboardEvent('keydown', { + key: 'c', + code: 'KeyC', + keyCode: 67, + ctrlKey: true, + bubbles: true + }) + ) + + expect(pane.textarea.value).toBe('') + }) +}) diff --git a/src/renderer/src/components/terminal-pane/input/ime/terminal-ime-retained-hangul-commit.ts b/src/renderer/src/components/terminal-pane/input/ime/terminal-ime-retained-hangul-commit.ts new file mode 100644 index 00000000000..2d15f07fa24 --- /dev/null +++ b/src/renderer/src/components/terminal-pane/input/ime/terminal-ime-retained-hangul-commit.ts @@ -0,0 +1,138 @@ +import { + KITTY_REPORT_EVENT_TYPES, + kittyReportsAllKeysAsEscapeCodes +} from '../../../../../../shared/terminal/protocol/input-modes/terminal-kitty-keyboard-flags' + +// Hangul jamo blocks plus precomposed syllables: text a Korean input method may later rebuild in place. +const HANGUL_TEXT = /[ᄀ-ᇿ㄰-㆏ꥠ-꥿가-힣ힰ-퟿ᅠ-ᅵ]/u + +type RetainedRange = { + textarea: HTMLTextAreaElement + start: number + end: number + text: string +} + +/** + * macOS 2-Set can commit a lone jamo as plain text and then rebuild it in place (ㅇ + ㅏ → 아) by + * replacing that committed range (#9738). The forwarder has already sent the jamo, so keep it in the + * field for the replacement to land, and erase it from the PTY when the input method replaces it. + */ +export type TerminalImeRetainedHangulCommit = { + /** Returns true when the commit stays in the field instead of being cleared. */ + retain: (textarea: HTMLTextAreaElement, committedText: string) => boolean + dispose: () => void +} + +// Only these replacements are forwarded to the PTY afterwards; erasing for any other edit would lose text. +const FORWARDED_REPLACEMENT_INPUT_TYPES = new Set(['insertText', 'insertCompositionText']) +const MODIFIER_KEYS = new Set(['Shift', 'Control', 'Alt', 'Meta', 'CapsLock', 'AltGraph', 'Fn']) + +function encodeBackspace(kittyKeyboardFlags: number): string { + if (!kittyReportsAllKeysAsEscapeCodes(kittyKeyboardFlags)) { + return '\x7f' + } + return (kittyKeyboardFlags & KITTY_REPORT_EVENT_TYPES) !== 0 + ? '\x1b[127u\x1b[127;1:3u' + : '\x1b[127u' +} + +function isIntact(range: RetainedRange): boolean { + return range.textarea.value.slice(range.start, range.end) === range.text +} + +export function installTerminalImeRetainedHangulCommit(args: { + terminalElement: HTMLElement + sendInput: (data: string) => void + getKittyKeyboardFlags: () => number +}): TerminalImeRetainedHangulCommit { + let retained: RetainedRange | null = null + + const retain = (textarea: HTMLTextAreaElement, committedText: string): boolean => { + if (!HANGUL_TEXT.test(committedText) || textarea.selectionStart !== textarea.selectionEnd) { + retained = null + return false + } + const end = textarea.selectionEnd + const start = end - committedText.length + if (start < 0 || textarea.value.slice(start, end) !== committedText) { + retained = null + return false + } + // Consecutive commits extend one range so a rebuild spanning both still erases both. + retained = + retained && retained.textarea === textarea && retained.end === start && isIntact(retained) + ? { textarea, start: retained.start, end, text: retained.text + committedText } + : { textarea, start, end, text: committedText } + return true + } + + // Capture runs before xterm or the forwarder sends the replacement, so the PTY erases it first. + const eraseReplacedRange = (event: Event): void => { + const range = retained + if (!range || event.target !== range.textarea) { + return + } + if (event instanceof InputEvent && !FORWARDED_REPLACEMENT_INPUT_TYPES.has(event.inputType)) { + return + } + const { selectionStart, selectionEnd } = range.textarea + const replacesRetainedTail = + isIntact(range) && + selectionStart < selectionEnd && + selectionStart >= range.start && + // Backspace erases from the PTY cursor, so the replaced text must be the last text sent. + selectionEnd === range.end && + selectionEnd === range.textarea.value.length + // A composition owns the field from here; its clause selections must not read as rebuilds. + if (replacesRetainedTail || event.type === 'compositionstart' || !isIntact(range)) { + retained = null + } + if (!replacesRetainedTail) { + return + } + const replaced = Array.from(range.textarea.value.slice(selectionStart, selectionEnd)).length + args.sendInput(encodeBackspace(args.getKittyKeyboardFlags()).repeat(replaced)) + } + + // Chords and named keys (Backspace, Enter, Ctrl+C) edit the PTY without the input method's text; observed here because the pane handles some before the forwarder sees them. + const dropOnNonTextKeydown = (event: KeyboardEvent): void => { + const range = retained + const isPlainText = event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey + if ( + !range || + isPlainText || + event.keyCode === 229 || + event.isComposing || + MODIFIER_KEYS.has(event.key) + ) { + return + } + retained = null + // Mirror the PTY edit in the field so the input method stops rebuilding text it no longer owns. + if (isIntact(range) && range.end === range.textarea.value.length) { + range.textarea.value = range.textarea.value.slice(0, range.start) + } + } + + // xterm empties the field on blur. + const drop = (): void => { + retained = null + } + + args.terminalElement.addEventListener('compositionstart', eraseReplacedRange, true) + args.terminalElement.addEventListener('beforeinput', eraseReplacedRange, true) + args.terminalElement.addEventListener('keydown', dropOnNonTextKeydown, true) + args.terminalElement.addEventListener('blur', drop, true) + + return { + retain, + dispose: () => { + retained = null + args.terminalElement.removeEventListener('keydown', dropOnNonTextKeydown, true) + args.terminalElement.removeEventListener('blur', drop, true) + args.terminalElement.removeEventListener('compositionstart', eraseReplacedRange, true) + args.terminalElement.removeEventListener('beforeinput', eraseReplacedRange, true) + } + } +} From e7288d7ea5387b279ddf5663d1004f04a1bd793f Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 13 Nov 2026 00:46:08 -0800 Subject: [PATCH 051/178] refactor: group new-workspace modules by domain (#41905) --- config/focus-setting-read-baseline.txt | 12 +- config/react-compiler-bailout-baseline.txt | 12 +- config/react-compiler-uncompiled-files.txt | 4 +- .../scripts/ci/sharding/ci-shard-timings.json | 22 ++-- .../components/NewWorkspaceComposerCard.tsx | 33 +++--- .../components/NewWorkspaceComposerModal.tsx | 6 +- ...ithub-duplicate-issue-candidates.test.tsx} | 0 .../jira-issue-assignee-control.tsx | 2 +- .../tree/NativeChatResumeOnRestartGroups.tsx | 2 +- .../native-chat-composer-drop-scope.test.tsx | 17 +-- .../SmartWorkspaceNameField.fixture.tsx | 2 +- .../new-workspace/SmartWorkspaceNameField.tsx | 6 +- .../ComposerBaseRefPicker.tsx | 2 +- .../ComposerBranchChoice.tsx | 0 .../ComposerParentWorktreePicker.fixture.tsx | 6 +- .../ComposerParentWorktreePicker.test.tsx | 11 +- .../ComposerParentWorktreePicker.tsx | 48 +++----- .../base-ref-picker-visibility.ts | 4 +- .../composer-parent-worktree-picker-props.ts | 21 ++++ .../NewWorkspaceComposerAdvancedSection.tsx | 8 +- .../NewWorkspaceComposerAgentSection.tsx | 0 .../NewWorkspaceComposerFooter.tsx | 0 ...ewWorkspaceComposerNameSection.fixture.tsx | 4 +- .../NewWorkspaceComposerNameSection.test.tsx | 0 .../NewWorkspaceComposerNameSection.tsx | 6 +- ...orkspaceComposerProjectSection.fixture.tsx | 2 +- .../NewWorkspaceComposerProjectSection.tsx | 21 ++-- .../{ => composer}/composer-modal-data.ts | 12 +- .../new-workspace-composer-card-props.ts | 42 +++---- .../new-workspace-composer-ssh-status.ts | 2 +- .../use-composer-file-drag-over.ts | 0 .../use-new-workspace-composer-file-drop.ts | 2 +- .../smart-workspace-cross-repo-dialog.tsx | 0 .../smart-workspace-name-field-copy.ts | 2 +- .../smart-workspace-name-field-model.ts | 16 +-- .../smart-workspace-name-field-surface.tsx | 4 +- .../smart-workspace-name-input-surface.tsx | 6 +- .../smart-workspace-source-popover-focus.ts | 0 ...smart-workspace-source-results-surface.tsx | 6 +- ...se-smart-workspace-field-focus-controls.ts | 2 +- .../use-smart-workspace-name-field-actions.ts | 12 +- ...e-smart-workspace-name-field-controller.ts | 4 +- .../SetProjectLocationDialog.test.tsx | 0 .../SetProjectLocationDialog.tsx | 8 +- .../SetProjectLocationForms.tsx | 6 +- .../pick-local-project-folder.ts | 0 .../ProjectCombobox.dialog-handoff.test.tsx | 110 +----------------- .../ProjectCombobox.tsx | 9 +- .../ProjectComboboxAddProjectOption.tsx | 2 +- .../ProjectComboboxRow.tsx | 0 .../composer-add-project-default-host.ts | 2 +- .../project-combobox-localized-copy.ts | 0 .../project-combobox-matching.ts | 0 .../use-composer-hosted-add-project.ts | 0 .../use-recent-project-ids.test.ts | 2 +- .../use-recent-project-ids.ts | 2 +- .../RunTargetCombobox.fixture.tsx | 2 +- .../{ => run-target}/RunTargetCombobox.tsx | 6 +- .../{ => run-target}/RunTargetComboboxRow.tsx | 5 +- .../{ => run-target}/RunTargetField.tsx | 6 +- .../{ => run-target}/RunTargetRowActions.tsx | 4 +- .../{ => run-target}/RunTargetRowList.tsx | 3 +- .../{ => run-target}/RunTargetSubmenus.tsx | 5 +- .../{ => run-target}/run-target-options.ts | 2 +- .../DirectWorkItemSetupDialog.test.tsx | 0 .../{ => setup}/DirectWorkItemSetupDialog.tsx | 0 .../{ => setup}/SetupCommandPreview.tsx | 0 .../{ => setup}/WorkspaceSetupChoice.test.tsx | 0 .../{ => setup}/WorkspaceSetupChoice.tsx | 0 .../{ => setup}/WorkspaceSetupSection.tsx | 6 +- .../direct-work-item-setup-copy.ts | 0 .../workspace-setup-composer-copy.ts | 0 .../workspace-setup-section-model.ts | 2 +- .../smart-workspace-command-value.ts | 1 - .../SmartWorkspaceSourceResultList.tsx | 4 +- .../SmartWorkspaceSourceTabs.fixture.tsx | 4 +- .../SmartWorkspaceSourceTabs.tsx | 0 .../jira-source-status-message.ts | 0 .../smart-workspace-empty-hint.ts | 2 +- ...smart-workspace-github-direct-link.test.ts | 4 +- .../smart-workspace-github-direct-link.ts | 8 +- .../smart-workspace-localized-options.tsx | 2 +- .../smart-workspace-provider-availability.ts | 2 +- .../smart-workspace-repo-slug.ts | 8 +- .../smart-workspace-source-row-content.tsx | 9 +- .../use-composer-source-scope.ts | 6 +- .../use-jira-composer-project.ts | 12 +- .../use-jira-source-connection.test.tsx | 4 +- .../use-jira-source-connection.ts | 4 +- .../use-jira-url-source.test.tsx | 8 +- .../use-jira-url-source.ts | 12 +- .../use-linear-source-connection.ts | 4 +- ...smart-workspace-field-availability.test.ts | 0 .../use-smart-workspace-field-availability.ts | 8 +- .../use-smart-workspace-github-search.ts | 8 +- .../use-smart-workspace-gitlab-search.ts | 6 +- ...pace-name-field-foundation.source.test.tsx | 12 +- .../use-smart-workspace-secondary-searches.ts | 18 +-- .../use-work-item-source-foundation.ts | 12 +- .../use-work-item-source-presentation.ts | 16 +-- .../use-work-item-source-search.ts | 10 +- .../use-work-item-source-state.ts | 14 +-- .../work-item-source-selection.ts | 14 +-- .../type-ahead-combobox-styles.ts | 0 .../use-type-ahead-combobox.ts | 0 .../{ => type-ahead}/use-wheel-scrollable.ts | 0 .../menu/AddProjectMenu.fixture.tsx | 2 +- .../menu/AddProjectMenu.test.tsx | 4 +- .../review/WorkspaceAttachmentSearch.tsx | 8 +- .../WorkspaceAttachmentSourceStatus.tsx | 4 +- .../folder-workspace-source-selection.ts | 2 +- .../composer-state/composer-source-model.ts | 2 +- .../composer-state/issue-source-actions.ts | 2 +- .../launch-work-item-direct-setup.test.tsx | 2 +- .../smart-workspace-source-results.test.ts | 4 +- ...rt-workspace-linear-read-test-fixtures.tsx | 22 ++-- 116 files changed, 345 insertions(+), 462 deletions(-) rename src/renderer/src/components/{new-workspace/github-source-consumers.test.tsx => github/github-duplicate-issue-candidates.test.tsx} (100%) rename src/renderer/src/components/new-workspace/{ => branch-selection}/ComposerBaseRefPicker.tsx (96%) rename src/renderer/src/components/new-workspace/{ => branch-selection}/ComposerBranchChoice.tsx (100%) rename src/renderer/src/components/new-workspace/{ => branch-selection}/ComposerParentWorktreePicker.fixture.tsx (89%) rename src/renderer/src/components/new-workspace/{ => branch-selection}/ComposerParentWorktreePicker.test.tsx (96%) rename src/renderer/src/components/new-workspace/{ => branch-selection}/ComposerParentWorktreePicker.tsx (92%) rename src/renderer/src/components/new-workspace/{ => branch-selection}/base-ref-picker-visibility.ts (83%) create mode 100644 src/renderer/src/components/new-workspace/branch-selection/composer-parent-worktree-picker-props.ts rename src/renderer/src/components/new-workspace/{ => composer}/NewWorkspaceComposerAdvancedSection.tsx (97%) rename src/renderer/src/components/new-workspace/{ => composer}/NewWorkspaceComposerAgentSection.tsx (100%) rename src/renderer/src/components/new-workspace/{ => composer}/NewWorkspaceComposerFooter.tsx (100%) rename src/renderer/src/components/new-workspace/{ => composer}/NewWorkspaceComposerNameSection.fixture.tsx (93%) rename src/renderer/src/components/new-workspace/{ => composer}/NewWorkspaceComposerNameSection.test.tsx (100%) rename src/renderer/src/components/new-workspace/{ => composer}/NewWorkspaceComposerNameSection.tsx (96%) rename src/renderer/src/components/new-workspace/{ => composer}/NewWorkspaceComposerProjectSection.fixture.tsx (98%) rename src/renderer/src/components/new-workspace/{ => composer}/NewWorkspaceComposerProjectSection.tsx (92%) rename src/renderer/src/components/new-workspace/{ => composer}/composer-modal-data.ts (75%) rename src/renderer/src/components/new-workspace/{ => composer}/new-workspace-composer-card-props.ts (78%) rename src/renderer/src/components/new-workspace/{ => composer}/new-workspace-composer-ssh-status.ts (94%) rename src/renderer/src/components/new-workspace/{ => composer}/use-composer-file-drag-over.ts (100%) rename src/renderer/src/components/new-workspace/{ => composer}/use-new-workspace-composer-file-drop.ts (98%) rename src/renderer/src/components/new-workspace/{ => name-field}/smart-workspace-cross-repo-dialog.tsx (100%) rename src/renderer/src/components/new-workspace/{ => name-field}/smart-workspace-name-field-copy.ts (97%) rename src/renderer/src/components/new-workspace/{ => name-field}/smart-workspace-name-field-model.ts (84%) rename src/renderer/src/components/new-workspace/{ => name-field}/smart-workspace-name-field-surface.tsx (96%) rename src/renderer/src/components/new-workspace/{ => name-field}/smart-workspace-name-input-surface.tsx (97%) rename src/renderer/src/components/new-workspace/{ => name-field}/smart-workspace-source-popover-focus.ts (100%) rename src/renderer/src/components/new-workspace/{ => name-field}/smart-workspace-source-results-surface.tsx (93%) rename src/renderer/src/components/new-workspace/{ => name-field}/use-smart-workspace-field-focus-controls.ts (97%) rename src/renderer/src/components/new-workspace/{ => name-field}/use-smart-workspace-name-field-actions.ts (92%) rename src/renderer/src/components/new-workspace/{ => name-field}/use-smart-workspace-name-field-controller.ts (95%) rename src/renderer/src/components/new-workspace/{ => project-location}/SetProjectLocationDialog.test.tsx (100%) rename src/renderer/src/components/new-workspace/{ => project-location}/SetProjectLocationDialog.tsx (96%) rename src/renderer/src/components/new-workspace/{ => project-location}/SetProjectLocationForms.tsx (96%) rename src/renderer/src/components/new-workspace/{ => project-location}/pick-local-project-folder.ts (100%) rename src/renderer/src/components/new-workspace/{ => project-selection}/ProjectCombobox.dialog-handoff.test.tsx (51%) rename src/renderer/src/components/new-workspace/{ => project-selection}/ProjectCombobox.tsx (98%) rename src/renderer/src/components/new-workspace/{ => project-selection}/ProjectComboboxAddProjectOption.tsx (98%) rename src/renderer/src/components/new-workspace/{ => project-selection}/ProjectComboboxRow.tsx (100%) rename src/renderer/src/components/new-workspace/{ => project-selection}/composer-add-project-default-host.ts (94%) rename src/renderer/src/components/new-workspace/{ => project-selection}/project-combobox-localized-copy.ts (100%) rename src/renderer/src/components/new-workspace/{ => project-selection}/project-combobox-matching.ts (100%) rename src/renderer/src/components/new-workspace/{ => project-selection}/use-composer-hosted-add-project.ts (100%) rename src/renderer/src/components/new-workspace/{ => project-selection}/use-recent-project-ids.test.ts (94%) rename src/renderer/src/components/new-workspace/{ => project-selection}/use-recent-project-ids.ts (94%) rename src/renderer/src/components/new-workspace/{ => run-target}/RunTargetCombobox.fixture.tsx (96%) rename src/renderer/src/components/new-workspace/{ => run-target}/RunTargetCombobox.tsx (97%) rename src/renderer/src/components/new-workspace/{ => run-target}/RunTargetComboboxRow.tsx (95%) rename src/renderer/src/components/new-workspace/{ => run-target}/RunTargetField.tsx (95%) rename src/renderer/src/components/new-workspace/{ => run-target}/RunTargetRowActions.tsx (96%) rename src/renderer/src/components/new-workspace/{ => run-target}/RunTargetRowList.tsx (97%) rename src/renderer/src/components/new-workspace/{ => run-target}/RunTargetSubmenus.tsx (97%) rename src/renderer/src/components/new-workspace/{ => run-target}/run-target-options.ts (98%) rename src/renderer/src/components/new-workspace/{ => setup}/DirectWorkItemSetupDialog.test.tsx (100%) rename src/renderer/src/components/new-workspace/{ => setup}/DirectWorkItemSetupDialog.tsx (100%) rename src/renderer/src/components/new-workspace/{ => setup}/SetupCommandPreview.tsx (100%) rename src/renderer/src/components/new-workspace/{ => setup}/WorkspaceSetupChoice.test.tsx (100%) rename src/renderer/src/components/new-workspace/{ => setup}/WorkspaceSetupChoice.tsx (100%) rename src/renderer/src/components/new-workspace/{ => setup}/WorkspaceSetupSection.tsx (94%) rename src/renderer/src/components/new-workspace/{ => setup}/direct-work-item-setup-copy.ts (100%) rename src/renderer/src/components/new-workspace/{ => setup}/workspace-setup-composer-copy.ts (100%) rename src/renderer/src/components/new-workspace/{ => setup}/workspace-setup-section-model.ts (89%) delete mode 100644 src/renderer/src/components/new-workspace/smart-workspace-command-value.ts rename src/renderer/src/components/new-workspace/{ => source-search}/SmartWorkspaceSourceResultList.tsx (98%) rename src/renderer/src/components/new-workspace/{ => source-search}/SmartWorkspaceSourceTabs.fixture.tsx (85%) rename src/renderer/src/components/new-workspace/{ => source-search}/SmartWorkspaceSourceTabs.tsx (100%) rename src/renderer/src/components/new-workspace/{ => source-search}/jira-source-status-message.ts (100%) rename src/renderer/src/components/new-workspace/{ => source-search}/smart-workspace-empty-hint.ts (92%) rename src/renderer/src/components/new-workspace/{ => source-search}/smart-workspace-github-direct-link.test.ts (90%) rename src/renderer/src/components/new-workspace/{ => source-search}/smart-workspace-github-direct-link.ts (92%) rename src/renderer/src/components/new-workspace/{ => source-search}/smart-workspace-localized-options.tsx (96%) rename src/renderer/src/components/new-workspace/{ => source-search}/smart-workspace-provider-availability.ts (93%) rename src/renderer/src/components/new-workspace/{ => source-search}/smart-workspace-repo-slug.ts (83%) rename src/renderer/src/components/new-workspace/{ => source-search}/smart-workspace-source-row-content.tsx (95%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-composer-source-scope.ts (74%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-jira-composer-project.ts (61%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-jira-source-connection.test.tsx (93%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-jira-source-connection.ts (96%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-jira-url-source.test.tsx (96%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-jira-url-source.ts (95%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-linear-source-connection.ts (97%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-smart-workspace-field-availability.test.ts (100%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-smart-workspace-field-availability.ts (94%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-smart-workspace-github-search.ts (95%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-smart-workspace-gitlab-search.ts (94%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-smart-workspace-name-field-foundation.source.test.tsx (91%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-smart-workspace-secondary-searches.ts (92%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-work-item-source-foundation.ts (95%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-work-item-source-presentation.ts (92%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-work-item-source-search.ts (93%) rename src/renderer/src/components/new-workspace/{ => source-search}/use-work-item-source-state.ts (79%) rename src/renderer/src/components/new-workspace/{ => source-search}/work-item-source-selection.ts (86%) rename src/renderer/src/components/new-workspace/{ => type-ahead}/type-ahead-combobox-styles.ts (100%) rename src/renderer/src/components/new-workspace/{ => type-ahead}/use-type-ahead-combobox.ts (100%) rename src/renderer/src/components/new-workspace/{ => type-ahead}/use-wheel-scrollable.ts (100%) rename src/{renderer/src/components => shared}/new-workspace/smart-workspace-source-results.test.ts (96%) diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index 00962b987d8..84559a65d66 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -64,12 +64,12 @@ 1 src/renderer/src/components/linear-item-drawer-edit-section.tsx 1 src/renderer/src/components/native-chat-recovery/tree/NativeChatResumeOnRestartGroups.tsx 1 src/renderer/src/components/native-chat/media/visuals/NativeChatVisualFrame.tsx -1 src/renderer/src/components/new-workspace/use-jira-source-connection.ts -7 src/renderer/src/components/new-workspace/use-linear-source-connection.ts -1 src/renderer/src/components/new-workspace/use-smart-workspace-name-field-controller.ts -1 src/renderer/src/components/new-workspace/use-smart-workspace-secondary-searches.ts -6 src/renderer/src/components/new-workspace/use-work-item-source-foundation.ts -1 src/renderer/src/components/new-workspace/use-work-item-source-search.ts +1 src/renderer/src/components/new-workspace/source-search/use-jira-source-connection.ts +7 src/renderer/src/components/new-workspace/source-search/use-linear-source-connection.ts +1 src/renderer/src/components/new-workspace/name-field/use-smart-workspace-name-field-controller.ts +1 src/renderer/src/components/new-workspace/source-search/use-smart-workspace-secondary-searches.ts +6 src/renderer/src/components/new-workspace/source-search/use-work-item-source-foundation.ts +1 src/renderer/src/components/new-workspace/source-search/use-work-item-source-search.ts 1 src/renderer/src/components/provider-accounts/provider-account-scope.ts 2 src/renderer/src/components/right-sidebar/checks-panel/review-actions/checks-panel-hosted-review-click-routing.ts 1 src/renderer/src/components/right-sidebar/hosted-review/creation/use-create-pull-request-base-ref-discovery.ts diff --git a/config/react-compiler-bailout-baseline.txt b/config/react-compiler-bailout-baseline.txt index a0db79c5703..643bcde57d2 100644 --- a/config/react-compiler-bailout-baseline.txt +++ b/config/react-compiler-bailout-baseline.txt @@ -260,12 +260,12 @@ 2 src/renderer/src/components/native-chat/viewport/virtualization/NativeChatTranscriptItems.tsx 1 src/renderer/src/components/native-chat/viewport/virtualization/use-native-chat-transcript-window.ts 1 src/renderer/src/components/network/CustomAddressDialog.tsx -1 src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.tsx -1 src/renderer/src/components/new-workspace/RunTargetCombobox.tsx -3 src/renderer/src/components/new-workspace/SetProjectLocationDialog.tsx -2 src/renderer/src/components/new-workspace/use-smart-workspace-name-field-actions.ts -1 src/renderer/src/components/new-workspace/use-smart-workspace-name-field-controller.ts -1 src/renderer/src/components/new-workspace/use-smart-workspace-secondary-searches.ts +1 src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.tsx +1 src/renderer/src/components/new-workspace/run-target/RunTargetCombobox.tsx +3 src/renderer/src/components/new-workspace/project-location/SetProjectLocationDialog.tsx +2 src/renderer/src/components/new-workspace/name-field/use-smart-workspace-name-field-actions.ts +1 src/renderer/src/components/new-workspace/name-field/use-smart-workspace-name-field-controller.ts +1 src/renderer/src/components/new-workspace/source-search/use-smart-workspace-secondary-searches.ts 2 src/renderer/src/components/onboarding/AgentStep.tsx 2 src/renderer/src/components/onboarding/NotificationStep.tsx 8 src/renderer/src/components/onboarding/OnboardingFlow.tsx diff --git a/config/react-compiler-uncompiled-files.txt b/config/react-compiler-uncompiled-files.txt index f542bc6648a..a78f7b1b07a 100644 --- a/config/react-compiler-uncompiled-files.txt +++ b/config/react-compiler-uncompiled-files.txt @@ -7,8 +7,8 @@ src/renderer/src/components/feature-wall/setup/FeatureWallSetupChecklist.tsx src/renderer/src/components/feature-wall/setup/integrations/use-integration-connection-status.ts src/renderer/src/components/integrations/task-trackers/jira-integration-card.tsx src/renderer/src/components/integrations/task-trackers/linear-integration-card.tsx -src/renderer/src/components/new-workspace/use-jira-source-connection.ts -src/renderer/src/components/new-workspace/use-work-item-source-foundation.ts +src/renderer/src/components/new-workspace/source-search/use-jira-source-connection.ts +src/renderer/src/components/new-workspace/source-search/use-work-item-source-foundation.ts src/renderer/src/components/settings/skills/linear/LinearAgentSkillPane.tsx src/renderer/src/components/settings/tasks/use-task-source-provider-readiness.ts src/renderer/src/components/sidebar/workspace-card/details/use-worktree-card-review-details.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index c48decce4df..8df7deddb60 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -4755,21 +4755,21 @@ "src/renderer/src/components/native-chat/session/structured/use-structured-agent-session.clear-send.test.tsx": 2804, "src/renderer/src/components/native-chat/pane/actions/use-structured-native-chat-submit-reveal.test.ts": 2304, "src/renderer/src/components/network/CustomAddressDialog.test.tsx": 568, - "src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.test.tsx": 870, - "src/renderer/src/components/new-workspace/DirectWorkItemSetupDialog.test.tsx": 5296, - "src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.test.tsx": 2493, - "src/renderer/src/components/new-workspace/ProjectCombobox.dialog-handoff.test.tsx": 1043, + "src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.test.tsx": 870, + "src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx": 5296, + "src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx": 2493, + "src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx": 1043, "src/renderer/src/components/new-workspace/RunTargetCombobox.host-identity.test.tsx": 856, "src/renderer/src/components/new-workspace/SetProjectLocationDialog.host-identity.test.tsx": 1094, - "src/renderer/src/components/new-workspace/SetProjectLocationDialog.test.tsx": 1387, + "src/renderer/src/components/new-workspace/project-location/SetProjectLocationDialog.test.tsx": 1387, "src/renderer/src/components/new-workspace/SmartWorkspaceNameField-repo-slug-routing.test.ts": 342, - "src/renderer/src/components/new-workspace/smart-workspace-github-direct-link.test.ts": 347, - "src/renderer/src/components/new-workspace/smart-workspace-source-results.test.ts": 136, - "src/renderer/src/components/new-workspace/use-jira-source-connection.test.tsx": 278, - "src/renderer/src/components/new-workspace/use-jira-url-source.test.tsx": 419, + "src/renderer/src/components/new-workspace/source-search/smart-workspace-github-direct-link.test.ts": 347, + "src/shared/new-workspace/smart-workspace-source-results.test.ts": 136, + "src/renderer/src/components/new-workspace/source-search/use-jira-source-connection.test.tsx": 278, + "src/renderer/src/components/new-workspace/source-search/use-jira-url-source.test.tsx": 419, "src/renderer/src/components/new-workspace/use-linear-source-connection.test.tsx": 1335, - "src/renderer/src/components/new-workspace/use-recent-project-ids.test.ts": 1730, - "src/renderer/src/components/new-workspace/use-smart-workspace-name-field-foundation.source.test.tsx": 2290, + "src/renderer/src/components/new-workspace/project-selection/use-recent-project-ids.test.ts": 1730, + "src/renderer/src/components/new-workspace/source-search/use-smart-workspace-name-field-foundation.source.test.tsx": 2290, "src/renderer/src/components/new-workspace/work-item-source-selection.test.ts": 1736, "src/renderer/src/components/onboarding/OnboardingLanguageLoading.test.tsx": 2167, "src/renderer/src/components/pet/pet-blob-cache.test.ts": 64, diff --git a/src/renderer/src/components/NewWorkspaceComposerCard.tsx b/src/renderer/src/components/NewWorkspaceComposerCard.tsx index 7bed3f4f77a..bbc6a2743ad 100644 --- a/src/renderer/src/components/NewWorkspaceComposerCard.tsx +++ b/src/renderer/src/components/NewWorkspaceComposerCard.tsx @@ -12,7 +12,7 @@ import { type AddRemoteHostMode } from '@/components/sidebar/host-admission/ssh/AddRemoteHostDialog' import { lazyWithRetry } from '@/app-shell/startup/lazy-with-retry' -import type * as SetProjectLocationDialogModule from '@/components/new-workspace/SetProjectLocationDialog' +import type * as SetProjectLocationDialogModule from '@/components/new-workspace/project-location/SetProjectLocationDialog' import { unwrapRuntimeRpcResult } from '@/runtime/rpc/calls/runtime-rpc-result' import { withUiConnectTimeout } from '@/ssh/ssh-connect-ui-timeout' import { isSshConnectInFlight, trackSshConnect } from '@/ssh/ssh-connect-in-flight' @@ -25,26 +25,26 @@ import { import type { RuntimeStatus } from '../../../shared/runtime-session-contracts' import type { AddRepoDialogEntryRequest } from '@/components/sidebar/project-admission/dialog/add-repo-dialog-types' import type { TuiAgent } from '../../../shared/agent-launch/catalog/tui-agent' -import { NewWorkspaceComposerAdvancedSection } from './new-workspace/NewWorkspaceComposerAdvancedSection' -import { NewWorkspaceComposerAgentSection } from './new-workspace/NewWorkspaceComposerAgentSection' -import { NewWorkspaceComposerFooter } from './new-workspace/NewWorkspaceComposerFooter' -import { NewWorkspaceComposerNameSection } from './new-workspace/NewWorkspaceComposerNameSection' -import { NewWorkspaceComposerProjectSection } from './new-workspace/NewWorkspaceComposerProjectSection' +import { NewWorkspaceComposerAdvancedSection } from './new-workspace/composer/NewWorkspaceComposerAdvancedSection' +import { NewWorkspaceComposerAgentSection } from './new-workspace/composer/NewWorkspaceComposerAgentSection' +import { NewWorkspaceComposerFooter } from './new-workspace/composer/NewWorkspaceComposerFooter' +import { NewWorkspaceComposerNameSection } from './new-workspace/composer/NewWorkspaceComposerNameSection' +import { NewWorkspaceComposerProjectSection } from './new-workspace/composer/NewWorkspaceComposerProjectSection' import { EMPTY_EPHEMERAL_VM_RECIPES, EMPTY_PROJECT_HOST_SETUP_OPTIONS, EMPTY_PROJECT_OPTIONS, - type NeedsProjectHostOption, type NewWorkspaceComposerCardProps -} from './new-workspace/new-workspace-composer-card-props' -import { getSshStatusLabel } from './new-workspace/new-workspace-composer-ssh-status' -import { useNewWorkspaceComposerFileDrop } from './new-workspace/use-new-workspace-composer-file-drop' -import { useComposerFileDragOver } from './new-workspace/use-composer-file-drag-over' +} from './new-workspace/composer/new-workspace-composer-card-props' +import type { NeedsSetupProjectHostOption } from '../lib/workspace/creation/project-host-setup-options' +import { getSshStatusLabel } from './new-workspace/composer/new-workspace-composer-ssh-status' +import { useNewWorkspaceComposerFileDrop } from './new-workspace/composer/use-new-workspace-composer-file-drop' +import { useComposerFileDragOver } from './new-workspace/composer/use-composer-file-drag-over' import { buildDefaultAgentSettingsUpdate } from '../../../shared/agent-launch/choices/default-agent-settings' // Lazy-load the project-location browser outside boot; warming shares this module entry with the click. const loadSetProjectLocationDialog = (): Promise => - import('@/components/new-workspace/SetProjectLocationDialog') + import('@/components/new-workspace/project-location/SetProjectLocationDialog') const SetProjectLocationDialog = lazyWithRetry( () => @@ -119,9 +119,8 @@ export default function NewWorkspaceComposerCard( }, [onNestedDialogOpenChange] ) - const [setLocationOption, setSetLocationOption] = React.useState( - null - ) + const [setLocationOption, setSetLocationOption] = + React.useState(null) // Retain the dialog while its cleared option drives the close animation. const [setLocationDialogMounted, setSetLocationDialogMounted] = React.useState(false) @@ -217,7 +216,7 @@ export default function NewWorkspaceComposerCard( [onAddProjectEntryRequest, openModal] ) const handleSetLocation = React.useCallback( - (option: NeedsProjectHostOption): void => { + (option: NeedsSetupProjectHostOption): void => { setSetLocationDialogMounted(true) setSetLocationOption(option) onNestedDialogOpenChange?.(true) @@ -236,7 +235,7 @@ export default function NewWorkspaceComposerCard( [handleSetLocationClose, onProjectHostSetupChange] ) const handleConnectRunTargetHost = React.useCallback( - async (option: NeedsProjectHostOption): Promise => { + async (option: NeedsSetupProjectHostOption): Promise => { const action = option.connectAction if (!action) { return diff --git a/src/renderer/src/components/NewWorkspaceComposerModal.tsx b/src/renderer/src/components/NewWorkspaceComposerModal.tsx index 2951903acf4..42ec4a66bab 100644 --- a/src/renderer/src/components/NewWorkspaceComposerModal.tsx +++ b/src/renderer/src/components/NewWorkspaceComposerModal.tsx @@ -1,4 +1,4 @@ -import type { ComposerModalData } from './new-workspace/composer-modal-data' +import type { ComposerModalData } from './new-workspace/composer/composer-modal-data' import { toast } from 'sonner' import { formatWorkspaceCreateError, @@ -8,7 +8,7 @@ import type { AgentPickerDraft, AgentPickerEntry } from '../../../shared/agent-launch/picker/agent-launch-picker' -import { DirectWorkItemSetupDialog } from './new-workspace/DirectWorkItemSetupDialog' +import { DirectWorkItemSetupDialog } from './new-workspace/setup/DirectWorkItemSetupDialog' import { agentChoicePending } from '@/lib/agent-session/launch/inventory/host-chat-program' import type { AgentLaunchSurface } from '@/lib/agent-session/launch/routing/agent-session-launch-plan' import React, { Suspense, useCallback, useEffect, useRef, useState } from 'react' @@ -25,7 +25,7 @@ import NewWorkspaceComposerCard from '@/components/NewWorkspaceComposerCard' import AgentSettingsDialog, { restoreAgentSettingsInvoker } from '@/components/agent/AgentSettingsDialog' -import { useComposerHostedAddProject } from './new-workspace/use-composer-hosted-add-project' +import { useComposerHostedAddProject } from './new-workspace/project-selection/use-composer-hosted-add-project' import { useComposerState } from '@/hooks/useComposerState' import { composerLaunchWorkspace, diff --git a/src/renderer/src/components/new-workspace/github-source-consumers.test.tsx b/src/renderer/src/components/github/github-duplicate-issue-candidates.test.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/github-source-consumers.test.tsx rename to src/renderer/src/components/github/github-duplicate-issue-candidates.test.tsx diff --git a/src/renderer/src/components/jira-issue-assignee-control.tsx b/src/renderer/src/components/jira-issue-assignee-control.tsx index 781f32ca0f9..97f8801d89d 100644 --- a/src/renderer/src/components/jira-issue-assignee-control.tsx +++ b/src/renderer/src/components/jira-issue-assignee-control.tsx @@ -2,7 +2,7 @@ import { useCallback } from 'react' import { Button } from '@/components/ui/button' import { JiraUserPicker } from '@/components/jira-user-picker' import { getJiraSelfUser } from '@/components/jira-self-user' -import { useJiraSourceConnection } from '@/components/new-workspace/use-jira-source-connection' +import { useJiraSourceConnection } from '@/components/new-workspace/source-search/use-jira-source-connection' import { useAppStore } from '@/store' import { compareBaseSensitivityLocaleText } from '../../../shared/locale-text-collators' import { jiraListAssignableUsersForTarget } from '@/runtime/jira/runtime-jira-user-fields-client' diff --git a/src/renderer/src/components/native-chat-recovery/tree/NativeChatResumeOnRestartGroups.tsx b/src/renderer/src/components/native-chat-recovery/tree/NativeChatResumeOnRestartGroups.tsx index 4a7880e3107..fbcfcd4880a 100644 --- a/src/renderer/src/components/native-chat-recovery/tree/NativeChatResumeOnRestartGroups.tsx +++ b/src/renderer/src/components/native-chat-recovery/tree/NativeChatResumeOnRestartGroups.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react' import { Folder, FolderTree, GitBranch, Monitor } from 'lucide-react' import { RepoIconGlyph } from '@/components/repo/repo-icon' -import { ProjectOptionDetail } from '@/components/new-workspace/ProjectComboboxRow' +import { ProjectOptionDetail } from '@/components/new-workspace/project-selection/ProjectComboboxRow' import { WorktreeHostContextBadge } from '@/components/sidebar/workspace-card/host/WorktreeHostContextBadge' import { useSidebarHostScopeOptions } from '@/components/sidebar/worktree-list/controls/use-sidebar-host-scope-options' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/native-chat/attachments/intake/native-chat-composer-drop-scope.test.tsx b/src/renderer/src/components/native-chat/attachments/intake/native-chat-composer-drop-scope.test.tsx index d9c3e97229d..ffa39d74881 100644 --- a/src/renderer/src/components/native-chat/attachments/intake/native-chat-composer-drop-scope.test.tsx +++ b/src/renderer/src/components/native-chat/attachments/intake/native-chat-composer-drop-scope.test.tsx @@ -9,7 +9,7 @@ import type { NativeChatComposerInput } from '../../composer/editor/native-chat- import { NativeChatPromptEditor } from '../../composer/editor/NativeChatPromptEditor' import { useNativeChatFileDrops } from './use-native-chat-file-drops' import { NativeChatPaneFileDropSurface } from './NativeChatPaneFileDropSurface' -import '../../../new-workspace/use-new-workspace-composer-file-drop' +import '../../../new-workspace/composer/use-new-workspace-composer-file-drop' import { NativeChatImageAttachmentPreview } from '../chips/NativeChatImageAttachmentPreview' import '../../../editor/document-images/useLocalImageSrc' import { resetLocalImageCacheState as resetLocalImageSrcStateForTests } from '../../../editor/document-images/local-image-src-cache' @@ -221,21 +221,6 @@ describe('native chat composer drop scoping', () => { expect(intake.stat).not.toHaveBeenCalled() }) - it('notices an OS drop whose owner changes while checking the files', async () => { - intake.stat.mockImplementation(async () => { - intake.owner = { kind: 'remote' } - }) - const view = render() - - await dropTwoImages(view.container.querySelector('[data-pane="chat-a"] .ProseMirror')!) - await settleAttachments() - - expect(view.container.querySelector('[data-notice="chat-a"]')?.textContent).toBe( - 'This workspace changed hosts while attaching — drop the files again.' - ) - expect(readNativeChatAttachmentCache('chat-a')).toEqual([]) - }) - it('attaches only to the dropped pane and leaves a hidden pane clean on remount', async () => { const view = render( <> diff --git a/src/renderer/src/components/new-workspace/SmartWorkspaceNameField.fixture.tsx b/src/renderer/src/components/new-workspace/SmartWorkspaceNameField.fixture.tsx index 9a6d38bda2f..c4ecfc138e7 100644 --- a/src/renderer/src/components/new-workspace/SmartWorkspaceNameField.fixture.tsx +++ b/src/renderer/src/components/new-workspace/SmartWorkspaceNameField.fixture.tsx @@ -16,7 +16,7 @@ import type { LinearIssue } from '../../../../shared/linear/issue-types' import type { LinearConnectionStatus } from '../../../../shared/linear/workspace-types' import type { Repo } from '../../../../shared/repo-types' import SmartWorkspaceNameField from './SmartWorkspaceNameField' -import type { SmartWorkspaceNameFieldProps } from './smart-workspace-name-field-model' +import type { SmartWorkspaceNameFieldProps } from './name-field/smart-workspace-name-field-model' const repo: Repo = { id: 'repo-orca', diff --git a/src/renderer/src/components/new-workspace/SmartWorkspaceNameField.tsx b/src/renderer/src/components/new-workspace/SmartWorkspaceNameField.tsx index 2f5456b6dba..9357baf49db 100644 --- a/src/renderer/src/components/new-workspace/SmartWorkspaceNameField.tsx +++ b/src/renderer/src/components/new-workspace/SmartWorkspaceNameField.tsx @@ -1,7 +1,7 @@ import React from 'react' -import { renderSmartWorkspaceNameField } from './smart-workspace-name-field-surface' -import type { SmartWorkspaceNameFieldProps } from './smart-workspace-name-field-model' -import { useSmartWorkspaceNameFieldController } from './use-smart-workspace-name-field-controller' +import { renderSmartWorkspaceNameField } from './name-field/smart-workspace-name-field-surface' +import type { SmartWorkspaceNameFieldProps } from './name-field/smart-workspace-name-field-model' +import { useSmartWorkspaceNameFieldController } from './name-field/use-smart-workspace-name-field-controller' import { useTranslate } from '../../i18n/use-translate' export default function SmartWorkspaceNameField( diff --git a/src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx b/src/renderer/src/components/new-workspace/branch-selection/ComposerBaseRefPicker.tsx similarity index 96% rename from src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx rename to src/renderer/src/components/new-workspace/branch-selection/ComposerBaseRefPicker.tsx index a5229577277..753925bf5d2 100644 --- a/src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx +++ b/src/renderer/src/components/new-workspace/branch-selection/ComposerBaseRefPicker.tsx @@ -3,7 +3,7 @@ import { CreateFromPicker } from '@/components/repo/CreateFromPicker' import { useAppStore } from '@/store' import { useRepoMap } from '@/store/repos/use-repository-catalog' import { useWorktreesForRepo } from '@/store/slices/worktrees/listing/use-worktree-catalog' -import { getRepoExecutionHostId } from '../../../../shared/execution-host' +import { getRepoExecutionHostId } from '../../../../../shared/execution-host' import { toast } from 'sonner' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/new-workspace/ComposerBranchChoice.tsx b/src/renderer/src/components/new-workspace/branch-selection/ComposerBranchChoice.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/ComposerBranchChoice.tsx rename to src/renderer/src/components/new-workspace/branch-selection/ComposerBranchChoice.tsx diff --git a/src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.fixture.tsx b/src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.fixture.tsx similarity index 89% rename from src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.fixture.tsx rename to src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.fixture.tsx index 7191c1cb52e..f9d5fb85dc4 100644 --- a/src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.fixture.tsx +++ b/src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.fixture.tsx @@ -1,6 +1,6 @@ -import { defineFixture } from '../../component-fixtures/component-fixture' -import type { Repo } from '../../../../shared/repo-types' -import type { Worktree } from '../../../../shared/worktree/types' +import { defineFixture } from '../../../component-fixtures/component-fixture' +import type { Repo } from '../../../../../shared/repo-types' +import type { Worktree } from '../../../../../shared/worktree/types' import { ComposerParentWorktreePicker } from './ComposerParentWorktreePicker' function repo(id: string, displayName: string, executionHostId?: Repo['executionHostId']): Repo { diff --git a/src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.test.tsx b/src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.test.tsx similarity index 96% rename from src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.test.tsx rename to src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.test.tsx index 5fe6028bf60..80b98f5f36d 100644 --- a/src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.test.tsx +++ b/src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.test.tsx @@ -3,10 +3,13 @@ import type React from 'react' import { cleanup, fireEvent, render } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { Repo } from '../../../../shared/repo-types' -import type { WorkspaceLineage, WorktreeLineage } from '../../../../shared/worktree/lineage-types' -import type { Worktree } from '../../../../shared/worktree/types' -import { folderWorkspaceKey, worktreeWorkspaceKey } from '../../../../shared/workspace-scope' +import type { Repo } from '../../../../../shared/repo-types' +import type { + WorkspaceLineage, + WorktreeLineage +} from '../../../../../shared/worktree/lineage-types' +import type { Worktree } from '../../../../../shared/worktree/types' +import { folderWorkspaceKey, worktreeWorkspaceKey } from '../../../../../shared/workspace-scope' type FakeStoreState = { worktreesByRepo: Record diff --git a/src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.tsx b/src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.tsx similarity index 92% rename from src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.tsx rename to src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.tsx index 47c245cc8fb..cb662a32ec6 100644 --- a/src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.tsx +++ b/src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.tsx @@ -1,4 +1,4 @@ -import { isWorktreeRetiredByRemoval } from '../../../../shared/worktree/removal-visibility' +import { isWorktreeRetiredByRemoval } from '../../../../../shared/worktree/removal-visibility' import React, { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { Check, ChevronsUpDown, GitBranch } from 'lucide-react' @@ -17,7 +17,7 @@ import { import { compareWorktreeDisplayName } from '@/lib/workspace/display/worktree-display-name-order' import '@/components/sidebar/workspace-card/activity/worktree-card-empty-inputs' import { branchDisplayName } from '@/components/sidebar/workspace-card/identity/worktree-branch-display' -import { getCyclicProjectedWorktreeLineageIds } from '../../../../shared/sidebar-list/worktree-lineage-projection' +import { getCyclicProjectedWorktreeLineageIds } from '../../../../../shared/sidebar-list/worktree-lineage-projection' import { clampWorktreeParentPickerIndex, filterWorktreeParentCandidates @@ -31,19 +31,21 @@ import { getLineageChildrenByParentId, getLineageChildWorktree } from '@/components/right-sidebar/folder-workspace-attached-worktrees' -import { COMBOBOX_POPOVER_SURFACE } from './type-ahead-combobox-styles' +import { COMBOBOX_POPOVER_SURFACE } from '../type-ahead/type-ahead-combobox-styles' import { sharesWorktreeLineageBoundary, type WorktreeLineageBoundary -} from '../../../../shared/resolved-worktree-lineage' -import { folderWorkspaceKey } from '../../../../shared/workspace-scope' -import type { WorkspaceLineage, WorktreeLineage } from '../../../../shared/worktree/lineage-types' -import type { Worktree } from '../../../../shared/worktree/types' +} from '../../../../../shared/resolved-worktree-lineage' +import { folderWorkspaceKey } from '../../../../../shared/workspace-scope' +import type { + WorkspaceLineage, + WorktreeLineage +} from '../../../../../shared/worktree/lineage-types' +import type { Worktree } from '../../../../../shared/worktree/types' import { getRepoExecutionHostId, - getWorktreeExecutionHostId, - type ExecutionHostId -} from '../../../../shared/execution-host' + getWorktreeExecutionHostId +} from '../../../../../shared/execution-host' import { useRepoMap } from '@/store/repos/use-repository-catalog' import { selectWorktreesByRepo } from '@/store/workspaces/catalog/workspace-record-fields' import { isCrossStoreCreateParent } from '@/components/sidebar/workspace-actions/parenting/worktree-parent-candidates' @@ -51,32 +53,16 @@ import { ParentMachineBadge } from '@/components/sidebar/workspace-actions/paren import { getWorktreeHostIdentity, isWorktreeHostIdentity -} from '../../../../shared/worktree/host-qualified-identity' +} from '../../../../../shared/worktree/host-qualified-identity' +import type { + ComposerParentWorktreePickerProps, + ParentWorktreeCandidateListProps +} from './composer-parent-worktree-picker-props' /** 20px text leading plus py-2 matches the explicit row height. */ const NO_PARENT_ROW_HEIGHT = 36 const NO_PARENT_ROW_KEY = 'no-parent' -type ComposerParentWorktreePickerProps = { - repoId: string - /** The child's destination host; parents on it nest as usual, others by a cross-machine link. */ - executionHostId?: ExecutionHostId | null - projectId?: string | null - value: string | null - onChange: (id: string | null) => void - disabled?: boolean - /** When the active workspace is a folder, restrict candidates to that folder's subtree. */ - activeFolderWorkspaceId?: string | null -} - -type ParentWorktreeCandidateListProps = Omit< - ComposerParentWorktreePickerProps, - 'onChange' | 'disabled' | 'activeFolderWorkspaceId' -> & { - activeFolderWorkspaceId: string | null - onSelect: (id: string | null) => void -} - /** Pick nesting without changing the base branch; enumerate only in Radix’s open-only list, leaving closed state a string selector. */ function ComposerParentWorktreePickerImpl({ repoId, diff --git a/src/renderer/src/components/new-workspace/base-ref-picker-visibility.ts b/src/renderer/src/components/new-workspace/branch-selection/base-ref-picker-visibility.ts similarity index 83% rename from src/renderer/src/components/new-workspace/base-ref-picker-visibility.ts rename to src/renderer/src/components/new-workspace/branch-selection/base-ref-picker-visibility.ts index 34e3970a928..aebe2f650c0 100644 --- a/src/renderer/src/components/new-workspace/base-ref-picker-visibility.ts +++ b/src/renderer/src/components/new-workspace/branch-selection/base-ref-picker-visibility.ts @@ -1,5 +1,5 @@ -import type { SmartWorkspaceNameSelection } from '@/components/new-workspace/smart-workspace-name-field-model' -import type { SmartNameMode } from '../../../../shared/new-workspace/smart-workspace-source-results' +import type { SmartWorkspaceNameSelection } from '@/components/new-workspace/name-field/smart-workspace-name-field-model' +import type { SmartNameMode } from '../../../../../shared/new-workspace/smart-workspace-source-results' /** Naming source and base ref are independent, except PR/MR/branch selections already carry their own base. */ export function shouldShowComposerBaseRefPicker(args: { diff --git a/src/renderer/src/components/new-workspace/branch-selection/composer-parent-worktree-picker-props.ts b/src/renderer/src/components/new-workspace/branch-selection/composer-parent-worktree-picker-props.ts new file mode 100644 index 00000000000..44783ac234c --- /dev/null +++ b/src/renderer/src/components/new-workspace/branch-selection/composer-parent-worktree-picker-props.ts @@ -0,0 +1,21 @@ +import type { ExecutionHostId } from '../../../../../shared/execution-host' + +export type ComposerParentWorktreePickerProps = { + repoId: string + /** The child's destination host; parents on it nest as usual, others by a cross-machine link. */ + executionHostId?: ExecutionHostId | null + projectId?: string | null + value: string | null + onChange: (id: string | null) => void + disabled?: boolean + /** When the active workspace is a folder, restrict candidates to that folder's subtree. */ + activeFolderWorkspaceId?: string | null +} + +export type ParentWorktreeCandidateListProps = Omit< + ComposerParentWorktreePickerProps, + 'onChange' | 'disabled' | 'activeFolderWorkspaceId' +> & { + activeFolderWorkspaceId: string | null + onSelect: (id: string | null) => void +} diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerAdvancedSection.tsx b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerAdvancedSection.tsx similarity index 97% rename from src/renderer/src/components/new-workspace/NewWorkspaceComposerAdvancedSection.tsx rename to src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerAdvancedSection.tsx index cf5053b0186..cd6ed571e5b 100644 --- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerAdvancedSection.tsx +++ b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerAdvancedSection.tsx @@ -1,10 +1,10 @@ import React from 'react' -import { WorkspaceSetupSection } from './WorkspaceSetupSection' -import { selectWorkspaceSetupCopy } from './workspace-setup-section-model' +import { WorkspaceSetupSection } from '../setup/WorkspaceSetupSection' +import { selectWorkspaceSetupCopy } from '../setup/workspace-setup-section-model' import { getComposerWorkspaceSetupLabels, getComposerSetupStartupLabel -} from './workspace-setup-composer-copy' +} from '../setup/workspace-setup-composer-copy' import { toast } from 'sonner' import SparseCheckoutPresetSelect from '@/components/sparse/SparseCheckoutPresetSelect' import { cn } from '@/lib/class-names' @@ -16,7 +16,7 @@ import { } from '@/lib/text-input/paste/text-control-paste' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' -import { ComposerParentWorktreePicker } from './ComposerParentWorktreePicker' +import { ComposerParentWorktreePicker } from '../branch-selection/ComposerParentWorktreePicker' import type { NewWorkspaceComposerCardProps } from './new-workspace-composer-card-props' type NewWorkspaceComposerAdvancedSectionProps = Pick< diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerAgentSection.tsx b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerAgentSection.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/NewWorkspaceComposerAgentSection.tsx rename to src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerAgentSection.tsx diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerFooter.tsx b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerFooter.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/NewWorkspaceComposerFooter.tsx rename to src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerFooter.tsx diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.fixture.tsx b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.fixture.tsx similarity index 93% rename from src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.fixture.tsx rename to src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.fixture.tsx index e489aa76369..518a4ba5a2d 100644 --- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.fixture.tsx +++ b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.fixture.tsx @@ -1,7 +1,7 @@ import { useState } from 'react' -import { defineFixture } from '../../component-fixtures/component-fixture' +import { defineFixture } from '../../../component-fixtures/component-fixture' import { NewWorkspaceComposerNameSection } from './NewWorkspaceComposerNameSection' -import { buildWorkspaceSourceSelection } from '../../../../shared/new-workspace/workspace-source' +import { buildWorkspaceSourceSelection } from '../../../../../shared/new-workspace/workspace-source' function BranchReusePreview({ busy = false, diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.test.tsx b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.test.tsx rename to src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.tsx b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.tsx similarity index 96% rename from src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.tsx rename to src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.tsx index 9cd7c265664..b47fca97545 100644 --- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.tsx +++ b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.tsx @@ -3,9 +3,9 @@ import { AlertTriangle } from 'lucide-react' import SmartWorkspaceNameField from '@/components/new-workspace/SmartWorkspaceNameField' import { cn } from '@/lib/class-names' import { useTranslate } from '@/i18n/use-translate' -import { shouldShowComposerBaseRefPicker } from './base-ref-picker-visibility' -import { ComposerBaseRefPicker } from './ComposerBaseRefPicker' -import { ComposerBranchChoice } from './ComposerBranchChoice' +import { shouldShowComposerBaseRefPicker } from '../branch-selection/base-ref-picker-visibility' +import { ComposerBaseRefPicker } from '../branch-selection/ComposerBaseRefPicker' +import { ComposerBranchChoice } from '../branch-selection/ComposerBranchChoice' import type { NewWorkspaceComposerCardProps } from './new-workspace-composer-card-props' type NewWorkspaceComposerNameSectionProps = Pick< diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.fixture.tsx b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerProjectSection.fixture.tsx similarity index 98% rename from src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.fixture.tsx rename to src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerProjectSection.fixture.tsx index 974ea83f78b..3384bd557c1 100644 --- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.fixture.tsx +++ b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerProjectSection.fixture.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { defineFixture } from '../../component-fixtures/component-fixture' +import { defineFixture } from '../../../component-fixtures/component-fixture' import type { NewWorkspaceProjectOption } from '@/lib/workspace/creation/new-workspace-project-options' import { getProjectHostSetupSelectionKey, diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.tsx b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerProjectSection.tsx similarity index 92% rename from src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.tsx rename to src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerProjectSection.tsx index b564499c21a..b84320b08b6 100644 --- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerProjectSection.tsx +++ b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerProjectSection.tsx @@ -5,19 +5,16 @@ import type { AddRepoDialogEntryRequest } from '@/components/sidebar/project-adm import { AddProjectMenu } from '@/components/sidebar/project-admission/menu/AddProjectMenu' import { Button } from '@/components/ui/button' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' -import ProjectCombobox, { - type ProjectComboboxAddProjectMenu -} from '@/components/new-workspace/ProjectCombobox' -import RunTargetCombobox from '@/components/new-workspace/RunTargetCombobox' +import ProjectCombobox from '@/components/new-workspace/project-selection/ProjectCombobox' +import type { ProjectComboboxAddProjectMenu } from '../project-selection/ProjectComboboxAddProjectOption' +import RunTargetCombobox from '@/components/new-workspace/run-target/RunTargetCombobox' import { useTranslate } from '@/i18n/use-translate' import { cn } from '@/lib/class-names' import { getSelectedNewWorkspaceProjectOptionId } from '@/lib/workspace/creation/new-workspace-project-options' -import { resolveComposerAddProjectDefaultHostId } from './composer-add-project-default-host' -import type { - EphemeralVmRecipeOption, - NeedsProjectHostOption, - NewWorkspaceComposerCardProps -} from './new-workspace-composer-card-props' +import { resolveComposerAddProjectDefaultHostId } from '../project-selection/composer-add-project-default-host' +import type { EphemeralVmRecipeOption } from '../run-target/run-target-options' +import type { NeedsSetupProjectHostOption } from '../../../lib/workspace/creation/project-host-setup-options' +import type { NewWorkspaceComposerCardProps } from './new-workspace-composer-card-props' import { EMPTY_PROJECT_OPTIONS } from './new-workspace-composer-card-props' type NewWorkspaceComposerProjectSectionProps = Pick< @@ -52,8 +49,8 @@ type NewWorkspaceComposerProjectSectionProps = Pick< handleProjectHostSetupChange: (setupId: string) => void handleAddSshHost: () => void handleAddRemoteServer: () => void - handleConnectRunTargetHost: (option: NeedsProjectHostOption) => Promise - handleSetLocation: (option: NeedsProjectHostOption) => void + handleConnectRunTargetHost: (option: NeedsSetupProjectHostOption) => Promise + handleSetLocation: (option: NeedsSetupProjectHostOption) => void sshStatusLabel: string connectButtonLabel: string selectedProjectName: string diff --git a/src/renderer/src/components/new-workspace/composer-modal-data.ts b/src/renderer/src/components/new-workspace/composer/composer-modal-data.ts similarity index 75% rename from src/renderer/src/components/new-workspace/composer-modal-data.ts rename to src/renderer/src/components/new-workspace/composer/composer-modal-data.ts index 65f375110b3..5beb130b5e4 100644 --- a/src/renderer/src/components/new-workspace/composer-modal-data.ts +++ b/src/renderer/src/components/new-workspace/composer/composer-modal-data.ts @@ -1,15 +1,15 @@ import type { AgentPickerDraft, AgentPickerEntry -} from '../../../../shared/agent-launch/picker/agent-launch-picker' +} from '../../../../../shared/agent-launch/picker/agent-launch-picker' import type { AgentLaunchSurface } from '@/lib/agent-session/launch/routing/agent-session-launch-plan' import type { DirectWorkItemSetupRequest } from '@/lib/workspace/creation/work-item/direct-work-item-setup' import type { LinkedWorkItemSummary } from '@/lib/workspace/creation/new-workspace' -import type { GitHubWorkItem } from '../../../../shared/github/work-items/work-item-types' -import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent' -import type { WorkspaceSource as WorkspaceCreateTelemetrySource } from '../../../../shared/workspace-source' -import type { WorkspaceStatus } from '../../../../shared/worktree/types' -import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context' +import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { TuiAgent } from '../../../../../shared/agent-launch/catalog/tui-agent' +import type { WorkspaceSource as WorkspaceCreateTelemetrySource } from '../../../../../shared/workspace-source' +import type { WorkspaceStatus } from '../../../../../shared/worktree/types' +import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' import type { WorkspaceCreationTargetHint } from '@/lib/workspace/creation/project-host-workspace-target' export type ComposerModalData = { diff --git a/src/renderer/src/components/new-workspace/new-workspace-composer-card-props.ts b/src/renderer/src/components/new-workspace/composer/new-workspace-composer-card-props.ts similarity index 78% rename from src/renderer/src/components/new-workspace/new-workspace-composer-card-props.ts rename to src/renderer/src/components/new-workspace/composer/new-workspace-composer-card-props.ts index b21ea6202a2..da3c61d66b2 100644 --- a/src/renderer/src/components/new-workspace/new-workspace-composer-card-props.ts +++ b/src/renderer/src/components/new-workspace/composer/new-workspace-composer-card-props.ts @@ -1,34 +1,32 @@ import type { AgentPickerDraft, AgentPickerEntry -} from '../../../../shared/agent-launch/picker/agent-launch-picker' +} from '../../../../../shared/agent-launch/picker/agent-launch-picker' import type { AgentComboboxSurfaceRows } from '@/components/agent/agent-combobox-surface-items' import type { ComposerAgentRows } from '@/hooks/composer-state/composer-agent-rows-target' import type RepoCombobox from '@/components/repo/RepoCombobox' import type { NewWorkspaceProjectOption } from '@/lib/workspace/creation/new-workspace-project-options' -import type { - NeedsSetupProjectHostOption, - ProjectHostSetupOption -} from '@/lib/workspace/creation/project-host-setup-options' -import type { SetupConfig } from '../../../../shared/orca-hook-setup-config' +import type { ProjectHostSetupOption } from '@/lib/workspace/creation/project-host-setup-options' +import type { SetupConfig } from '../../../../../shared/orca-hook-setup-config' import type { WorkspaceCreateErrorDisplay } from '@/lib/workspace/creation/workspace-create-error-format' -import type { SmartNameMode } from '../../../../shared/new-workspace/smart-workspace-source-results' -import type SmartWorkspaceNameField from './SmartWorkspaceNameField' -import type { SmartWorkspaceNameSelection } from '@/components/new-workspace/smart-workspace-name-field-model' -import type { GitHubWorkItem } from '../../../../shared/github/work-items/work-item-types' -import type { GitLabWorkItem } from '../../../../shared/gitlab-types' -import type { JiraIssue } from '../../../../shared/jira/jira-types' -import type { LinearIssue } from '../../../../shared/linear/issue-types' -import type { OrcaHooks, SetupAgentStartupPolicy } from '../../../../shared/orca-yaml-hook-types' -import type { SparsePreset } from '../../../../shared/worktree/create-types' -import type { SshConnectionStatus } from '../../../../shared/ssh-types' -import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context' +import type { SmartNameMode } from '../../../../../shared/new-workspace/smart-workspace-source-results' + +import type { SmartWorkspaceNameSelection } from '@/components/new-workspace/name-field/smart-workspace-name-field-model' +import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitLabWorkItem } from '../../../../../shared/gitlab-types' +import type { JiraIssue } from '../../../../../shared/jira/jira-types' +import type { LinearIssue } from '../../../../../shared/linear/issue-types' +import type { SetupAgentStartupPolicy } from '../../../../../shared/orca-yaml-hook-types' +import type { SparsePreset } from '../../../../../shared/worktree/create-types' +import type { SshConnectionStatus } from '../../../../../shared/ssh-types' +import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' import type { AddRepoDialogEntryRequest } from '@/components/sidebar/project-admission/dialog/add-repo-dialog-types' -import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent' -import type { ExecutionHostId } from '../../../../shared/execution-host' +import type { TuiAgent } from '../../../../../shared/agent-launch/catalog/tui-agent' +import type { ExecutionHostId } from '../../../../../shared/execution-host' + +import type { EphemeralVmRecipeOption } from '../run-target/run-target-options' export type RepoOption = React.ComponentProps['repos'][number] -export type EphemeralVmRecipeOption = NonNullable[number] export const EMPTY_PROJECT_OPTIONS: NewWorkspaceProjectOption[] = [] export const EMPTY_PROJECT_HOST_SETUP_OPTIONS: ProjectHostSetupOption[] = [] @@ -149,7 +147,3 @@ export type NewWorkspaceComposerCardProps = { onAddProjectEntryRequest?: (request: AddRepoDialogEntryRequest) => void onNestedDialogOpenChange?: (open: boolean) => void } - -export type NeedsProjectHostOption = NeedsSetupProjectHostOption -export type ReadyProjectHostOption = Extract -export type SmartWorkspaceNameFieldProps = React.ComponentProps diff --git a/src/renderer/src/components/new-workspace/new-workspace-composer-ssh-status.ts b/src/renderer/src/components/new-workspace/composer/new-workspace-composer-ssh-status.ts similarity index 94% rename from src/renderer/src/components/new-workspace/new-workspace-composer-ssh-status.ts rename to src/renderer/src/components/new-workspace/composer/new-workspace-composer-ssh-status.ts index 08a8375e4d4..8459f54a8b7 100644 --- a/src/renderer/src/components/new-workspace/new-workspace-composer-ssh-status.ts +++ b/src/renderer/src/components/new-workspace/composer/new-workspace-composer-ssh-status.ts @@ -1,5 +1,5 @@ import { translate } from '@/i18n/i18n' -import type { SshConnectionStatus } from '../../../../shared/ssh-types' +import type { SshConnectionStatus } from '../../../../../shared/ssh-types' const SSH_STATUS_LABELS: Partial> = { get disconnected() { diff --git a/src/renderer/src/components/new-workspace/use-composer-file-drag-over.ts b/src/renderer/src/components/new-workspace/composer/use-composer-file-drag-over.ts similarity index 100% rename from src/renderer/src/components/new-workspace/use-composer-file-drag-over.ts rename to src/renderer/src/components/new-workspace/composer/use-composer-file-drag-over.ts diff --git a/src/renderer/src/components/new-workspace/use-new-workspace-composer-file-drop.ts b/src/renderer/src/components/new-workspace/composer/use-new-workspace-composer-file-drop.ts similarity index 98% rename from src/renderer/src/components/new-workspace/use-new-workspace-composer-file-drop.ts rename to src/renderer/src/components/new-workspace/composer/use-new-workspace-composer-file-drop.ts index e348669eef6..76f36b3512f 100644 --- a/src/renderer/src/components/new-workspace/use-new-workspace-composer-file-drop.ts +++ b/src/renderer/src/components/new-workspace/composer/use-new-workspace-composer-file-drop.ts @@ -4,7 +4,7 @@ import { createOsFileDropSequence, useOsFileDropOwner } from '@/hooks/use-os-fil import { getNativeFileDropRejectionMessage } from '@/lib/files/drop/native-file-drop-rejection-message' import { useMountedRef } from '@/hooks/useMountedRef' import { useAppStore } from '@/store' -import { parseExecutionHostId, type ExecutionHostId } from '../../../../shared/execution-host' +import { parseExecutionHostId, type ExecutionHostId } from '../../../../../shared/execution-host' type Args = { projectPath?: string | null diff --git a/src/renderer/src/components/new-workspace/smart-workspace-cross-repo-dialog.tsx b/src/renderer/src/components/new-workspace/name-field/smart-workspace-cross-repo-dialog.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/smart-workspace-cross-repo-dialog.tsx rename to src/renderer/src/components/new-workspace/name-field/smart-workspace-cross-repo-dialog.tsx diff --git a/src/renderer/src/components/new-workspace/smart-workspace-name-field-copy.ts b/src/renderer/src/components/new-workspace/name-field/smart-workspace-name-field-copy.ts similarity index 97% rename from src/renderer/src/components/new-workspace/smart-workspace-name-field-copy.ts rename to src/renderer/src/components/new-workspace/name-field/smart-workspace-name-field-copy.ts index f1386cf2ebb..4d7c6415094 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-name-field-copy.ts +++ b/src/renderer/src/components/new-workspace/name-field/smart-workspace-name-field-copy.ts @@ -1,6 +1,6 @@ import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import type { SmartNameMode } from '../../../../shared/new-workspace/smart-workspace-source-results' +import type { SmartNameMode } from '../../../../../shared/new-workspace/smart-workspace-source-results' export function getSmartWorkspaceNameFieldCopy( { diff --git a/src/renderer/src/components/new-workspace/smart-workspace-name-field-model.ts b/src/renderer/src/components/new-workspace/name-field/smart-workspace-name-field-model.ts similarity index 84% rename from src/renderer/src/components/new-workspace/smart-workspace-name-field-model.ts rename to src/renderer/src/components/new-workspace/name-field/smart-workspace-name-field-model.ts index ef146f2b404..4d0a08bbc27 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-name-field-model.ts +++ b/src/renderer/src/components/new-workspace/name-field/smart-workspace-name-field-model.ts @@ -1,16 +1,16 @@ import type React from 'react' import type { useAppStore } from '@/store' -import type { parseGitHubIssueOrPRLink } from '../../../../shared/github/repositories/links' -import type { GitHubWorkItem } from '../../../../shared/github/work-items/work-item-types' -import type { GitLabWorkItem } from '../../../../shared/gitlab-types' -import type { JiraIssue, JiraSite } from '../../../../shared/jira/jira-types' -import type { LinearIssue } from '../../../../shared/linear/issue-types' -import type { BaseRefSearchResult } from '../../../../shared/repo-types' -import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context' +import type { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' +import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitLabWorkItem } from '../../../../../shared/gitlab-types' +import type { JiraIssue, JiraSite } from '../../../../../shared/jira/jira-types' +import type { LinearIssue } from '../../../../../shared/linear/issue-types' +import type { BaseRefSearchResult } from '../../../../../shared/repo-types' +import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' import type { SmartNameMode, SmartWorkspaceSourceRow -} from '../../../../shared/new-workspace/smart-workspace-source-results' +} from '../../../../../shared/new-workspace/smart-workspace-source-results' export type RepoOption = ReturnType['repos'][number] diff --git a/src/renderer/src/components/new-workspace/smart-workspace-name-field-surface.tsx b/src/renderer/src/components/new-workspace/name-field/smart-workspace-name-field-surface.tsx similarity index 96% rename from src/renderer/src/components/new-workspace/smart-workspace-name-field-surface.tsx rename to src/renderer/src/components/new-workspace/name-field/smart-workspace-name-field-surface.tsx index ba3ddbab71a..1574e9c44a5 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-name-field-surface.tsx +++ b/src/renderer/src/components/new-workspace/name-field/smart-workspace-name-field-surface.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Button } from '@/components/ui/button' import { Command } from '@/components/ui/command' import { Popover, PopoverAnchor } from '@/components/ui/popover' -import { SmartWorkspaceSourceTabs } from './SmartWorkspaceSourceTabs' +import { SmartWorkspaceSourceTabs } from '../source-search/SmartWorkspaceSourceTabs' import { cn } from '@/lib/class-names' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' @@ -10,7 +10,7 @@ import { WorkspaceEmojiSuggestionPopover } from '@/components/workspace-emoji/Wo import { renderSmartWorkspaceCrossRepoDialog } from './smart-workspace-cross-repo-dialog' import { renderSmartWorkspaceNameInput } from './smart-workspace-name-input-surface' import { renderSmartWorkspaceSourceResults } from './smart-workspace-source-results-surface' -import { getJiraSourceStatusMessage } from './jira-source-status-message' +import { getJiraSourceStatusMessage } from '../source-search/jira-source-status-message' import type { SmartWorkspaceNameFieldController } from './use-smart-workspace-name-field-controller' export function renderSmartWorkspaceNameField( diff --git a/src/renderer/src/components/new-workspace/smart-workspace-name-input-surface.tsx b/src/renderer/src/components/new-workspace/name-field/smart-workspace-name-input-surface.tsx similarity index 97% rename from src/renderer/src/components/new-workspace/smart-workspace-name-input-surface.tsx rename to src/renderer/src/components/new-workspace/name-field/smart-workspace-name-input-surface.tsx index 9158baa9aa1..20faceabe68 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-name-input-surface.tsx +++ b/src/renderer/src/components/new-workspace/name-field/smart-workspace-name-input-surface.tsx @@ -5,14 +5,14 @@ import { Input } from '@/components/ui/input' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { WorkspaceEmojiPickerButton } from '@/components/workspace-emoji/WorkspaceEmojiPickerButton' import { isImeCompositionKeyDown } from '@/lib/text-input/ime/ime-composition-keyboard-event' -import { isBlockingJiraUrlIntent } from '../../../../shared/new-workspace/smart-workspace-source-results' -import { isBlockingLinearUrlIntent } from '../../../../shared/new-workspace/smart-workspace-linear-intent' +import { isBlockingJiraUrlIntent } from '../../../../../shared/new-workspace/smart-workspace-source-results' +import { isBlockingLinearUrlIntent } from '../../../../../shared/new-workspace/smart-workspace-linear-intent' import { isComposerFieldToFieldFocus } from './smart-workspace-source-popover-focus' import { replaceCompletedWorkspaceEmojiShortcode } from '@/lib/workspace/display/workspace-emoji-shortcodes' import { cn } from '@/lib/class-names' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import { SelectionIcon } from './smart-workspace-source-row-content' +import { SelectionIcon } from '../source-search/smart-workspace-source-row-content' import type { SmartWorkspaceNameFieldController } from './use-smart-workspace-name-field-controller' export function renderSmartWorkspaceNameInput( diff --git a/src/renderer/src/components/new-workspace/smart-workspace-source-popover-focus.ts b/src/renderer/src/components/new-workspace/name-field/smart-workspace-source-popover-focus.ts similarity index 100% rename from src/renderer/src/components/new-workspace/smart-workspace-source-popover-focus.ts rename to src/renderer/src/components/new-workspace/name-field/smart-workspace-source-popover-focus.ts diff --git a/src/renderer/src/components/new-workspace/smart-workspace-source-results-surface.tsx b/src/renderer/src/components/new-workspace/name-field/smart-workspace-source-results-surface.tsx similarity index 93% rename from src/renderer/src/components/new-workspace/smart-workspace-source-results-surface.tsx rename to src/renderer/src/components/new-workspace/name-field/smart-workspace-source-results-surface.tsx index a34c48d7e9a..b48ccd09437 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-source-results-surface.tsx +++ b/src/renderer/src/components/new-workspace/name-field/smart-workspace-source-results-surface.tsx @@ -2,9 +2,9 @@ import React from 'react' import { PopoverContent } from '@/components/ui/popover' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import { JiraProjectPicker } from '../jira-project-picker' -import { LinearWorkspacePicker } from '../linear-workspace-picker' -import { SmartWorkspaceSourceResultList } from './SmartWorkspaceSourceResultList' +import { JiraProjectPicker } from '../../jira-project-picker' +import { LinearWorkspacePicker } from '../../linear-workspace-picker' +import { SmartWorkspaceSourceResultList } from '../source-search/SmartWorkspaceSourceResultList' import type { SmartWorkspaceNameFieldController } from './use-smart-workspace-name-field-controller' export function renderSmartWorkspaceSourceResults( diff --git a/src/renderer/src/components/new-workspace/use-smart-workspace-field-focus-controls.ts b/src/renderer/src/components/new-workspace/name-field/use-smart-workspace-field-focus-controls.ts similarity index 97% rename from src/renderer/src/components/new-workspace/use-smart-workspace-field-focus-controls.ts rename to src/renderer/src/components/new-workspace/name-field/use-smart-workspace-field-focus-controls.ts index 011244b4bf3..21043a591a2 100644 --- a/src/renderer/src/components/new-workspace/use-smart-workspace-field-focus-controls.ts +++ b/src/renderer/src/components/new-workspace/name-field/use-smart-workspace-field-focus-controls.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useRef, useState } from 'react' import type { SmartWorkspaceNameFieldProps } from './smart-workspace-name-field-model' -import type { SmartNameMode } from '../../../../shared/new-workspace/smart-workspace-source-results' +import type { SmartNameMode } from '../../../../../shared/new-workspace/smart-workspace-source-results' type FieldState = { mode: SmartNameMode; crossRepoPrompt?: unknown } diff --git a/src/renderer/src/components/new-workspace/use-smart-workspace-name-field-actions.ts b/src/renderer/src/components/new-workspace/name-field/use-smart-workspace-name-field-actions.ts similarity index 92% rename from src/renderer/src/components/new-workspace/use-smart-workspace-name-field-actions.ts rename to src/renderer/src/components/new-workspace/name-field/use-smart-workspace-name-field-actions.ts index 411000ff244..2c45b46e110 100644 --- a/src/renderer/src/components/new-workspace/use-smart-workspace-name-field-actions.ts +++ b/src/renderer/src/components/new-workspace/name-field/use-smart-workspace-name-field-actions.ts @@ -8,19 +8,19 @@ import { type WorkspaceEmojiReplacement, type getActiveWorkspaceEmojiShortcode } from '@/lib/workspace/display/workspace-emoji-shortcodes' -import type { StandardEmojiShortcodeEntry } from '../../../../shared/emoji-shortcode-catalog' -import type { GitHubWorkItem } from '../../../../shared/github/work-items/work-item-types' -import { buildTaskSourceContextFromRepo } from '../../../../shared/work-items/task-source-context' +import type { StandardEmojiShortcodeEntry } from '../../../../../shared/emoji-shortcode-catalog' +import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import { buildTaskSourceContextFromRepo } from '../../../../../shared/work-items/task-source-context' import type { RepoOption, RowEntry, SmartWorkspaceNameFieldProps } from './smart-workspace-name-field-model' -import { getRepoSlugCached, sameSlug } from './smart-workspace-repo-slug' -import type { useWorkItemSourceSearch } from './use-work-item-source-search' +import { getRepoSlugCached, sameSlug } from '../source-search/smart-workspace-repo-slug' +import type { useWorkItemSourceSearch } from '../source-search/use-work-item-source-search' import type { useSmartWorkspaceFieldFocusControls } from './use-smart-workspace-field-focus-controls' import { useAppStore } from '@/store/index' -import type { useWorkItemSourcePresentation } from './use-work-item-source-presentation' +import type { useWorkItemSourcePresentation } from '../source-search/use-work-item-source-presentation' import { openAppHttpLink } from '@/lib/browser/navigation/app-http-link-open' type Foundation = ReturnType & diff --git a/src/renderer/src/components/new-workspace/use-smart-workspace-name-field-controller.ts b/src/renderer/src/components/new-workspace/name-field/use-smart-workspace-name-field-controller.ts similarity index 95% rename from src/renderer/src/components/new-workspace/use-smart-workspace-name-field-controller.ts rename to src/renderer/src/components/new-workspace/name-field/use-smart-workspace-name-field-controller.ts index 5c5e8e7967c..a416db319b4 100644 --- a/src/renderer/src/components/new-workspace/use-smart-workspace-name-field-controller.ts +++ b/src/renderer/src/components/new-workspace/name-field/use-smart-workspace-name-field-controller.ts @@ -3,9 +3,9 @@ import { getActiveWorkspaceEmojiShortcode, searchWorkspaceEmojiShortcodes } from '@/lib/workspace/display/workspace-emoji-shortcodes' -import type { StandardEmojiShortcodeEntry } from '../../../../shared/emoji-shortcode-catalog' +import type { StandardEmojiShortcodeEntry } from '../../../../../shared/emoji-shortcode-catalog' import type { SmartWorkspaceNameFieldProps } from './smart-workspace-name-field-model' -import { useWorkItemSourceSearch } from './use-work-item-source-search' +import { useWorkItemSourceSearch } from '../source-search/use-work-item-source-search' import { useSmartWorkspaceFieldFocusControls } from './use-smart-workspace-field-focus-controls' import { useSmartWorkspaceNameFieldActions } from './use-smart-workspace-name-field-actions' import { getSmartWorkspaceNameFieldCopy } from './smart-workspace-name-field-copy' diff --git a/src/renderer/src/components/new-workspace/SetProjectLocationDialog.test.tsx b/src/renderer/src/components/new-workspace/project-location/SetProjectLocationDialog.test.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/SetProjectLocationDialog.test.tsx rename to src/renderer/src/components/new-workspace/project-location/SetProjectLocationDialog.test.tsx diff --git a/src/renderer/src/components/new-workspace/SetProjectLocationDialog.tsx b/src/renderer/src/components/new-workspace/project-location/SetProjectLocationDialog.tsx similarity index 96% rename from src/renderer/src/components/new-workspace/SetProjectLocationDialog.tsx rename to src/renderer/src/components/new-workspace/project-location/SetProjectLocationDialog.tsx index 34567bbe71a..644e210bf96 100644 --- a/src/renderer/src/components/new-workspace/SetProjectLocationDialog.tsx +++ b/src/renderer/src/components/new-workspace/project-location/SetProjectLocationDialog.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState, type RefObject } from 'react' -import { useCloneFolderOverride } from '../sidebar/project-admission/clone-folder/use-clone-folder-override' -import { getProjectCloneCheckTarget } from '../../store/projects/project-host-routing' +import { useCloneFolderOverride } from '../../sidebar/project-admission/clone-folder/use-clone-folder-override' +import { getProjectCloneCheckTarget } from '../../../store/projects/project-host-routing' import { Download, FolderOpen } from 'lucide-react' import { Dialog, @@ -16,8 +16,8 @@ import { getProjectHostSetupSelectionKey, type NeedsSetupProjectHostOption } from '@/lib/workspace/creation/project-host-setup-options' -import { parseExecutionHostId } from '../../../../shared/execution-host' -import type { RepoKind } from '../../../../shared/repo-types' +import { parseExecutionHostId } from '../../../../../shared/execution-host' +import type { RepoKind } from '../../../../../shared/repo-types' import { pickLocalProjectLocationFolder } from './pick-local-project-folder' import { CloneForm, ExistingFolderForm, LocationActionButton } from './SetProjectLocationForms' diff --git a/src/renderer/src/components/new-workspace/SetProjectLocationForms.tsx b/src/renderer/src/components/new-workspace/project-location/SetProjectLocationForms.tsx similarity index 96% rename from src/renderer/src/components/new-workspace/SetProjectLocationForms.tsx rename to src/renderer/src/components/new-workspace/project-location/SetProjectLocationForms.tsx index c49e27b6062..9da938d4af5 100644 --- a/src/renderer/src/components/new-workspace/SetProjectLocationForms.tsx +++ b/src/renderer/src/components/new-workspace/project-location/SetProjectLocationForms.tsx @@ -1,6 +1,6 @@ import { ArrowLeft, FolderOpen, LoaderCircle } from 'lucide-react' -import { CloneFolderNameField } from '../sidebar/project-admission/clone-folder/CloneFolderNameField' -import type { CloneFolderOverride } from '../sidebar/project-admission/clone-folder/use-clone-folder-override' +import { CloneFolderNameField } from '../../sidebar/project-admission/clone-folder/CloneFolderNameField' +import type { CloneFolderOverride } from '../../sidebar/project-admission/clone-folder/use-clone-folder-override' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { @@ -12,7 +12,7 @@ import { } from '@/components/ui/select' import { useTranslate } from '@/i18n/use-translate' import { cn } from '@/lib/class-names' -import type { RepoKind } from '../../../../shared/repo-types' +import type { RepoKind } from '../../../../../shared/repo-types' export function ExistingFolderForm({ setupPath, diff --git a/src/renderer/src/components/new-workspace/pick-local-project-folder.ts b/src/renderer/src/components/new-workspace/project-location/pick-local-project-folder.ts similarity index 100% rename from src/renderer/src/components/new-workspace/pick-local-project-folder.ts rename to src/renderer/src/components/new-workspace/project-location/pick-local-project-folder.ts diff --git a/src/renderer/src/components/new-workspace/ProjectCombobox.dialog-handoff.test.tsx b/src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx similarity index 51% rename from src/renderer/src/components/new-workspace/ProjectCombobox.dialog-handoff.test.tsx rename to src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx index f573697d909..c5238a5c8c7 100644 --- a/src/renderer/src/components/new-workspace/ProjectCombobox.dialog-handoff.test.tsx +++ b/src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx @@ -2,58 +2,13 @@ import React, { act, Suspense, useEffect, useRef, useState } from 'react' import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, beforeEach, expect, it, vi } from 'vitest' import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog' import ProjectCombobox from './ProjectCombobox' import { TooltipProvider } from '@/components/ui/tooltip' vi.mock('./use-recent-project-ids', () => ({ useRecentProjectIds: () => [] })) -function Composer({ populated }: { populated: boolean }): React.JSX.Element { - const [adding, setAdding] = useState(false) - const nameRef = useRef(null) - return ( - - - New workspace - setAdding(true)} - /> - - - { - event.preventDefault() - nameRef.current?.focus() - }} - > - Add a project - - - - - - - ) -} - // Mirrors a lazily loaded creation dialog that focuses its primary action from a mount effect. let resolveLateDialog: () => void = () => {} const LateAddDialogBody = React.lazy( @@ -139,69 +94,6 @@ function addOption(): HTMLElement { )! } -describe.each([false, true])( - 'project selector to nested dialog handoff (populated: %s)', - (populated) => { - it.each(['mouse', 'keyboard'] as const)( - 'removes the selector before the creation dialog is active via %s', - async (input) => { - await act(async () => - root.render( - - - - ) - ) - await act(async () => projectField().click()) - expect(document.querySelector('[role="listbox"]')).not.toBeNull() - - await act(async () => { - if (input === 'mouse') { - addOption().dispatchEvent( - new MouseEvent('mousedown', { bubbles: true, cancelable: true }) - ) - addOption().click() - } else if (populated) { - projectField().dispatchEvent( - new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true, cancelable: true }) - ) - } - }) - if (input === 'keyboard') { - await act(async () => { - projectField().dispatchEvent( - new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, cancelable: true }) - ) - }) - } - - expect(document.querySelector('[role="listbox"]')).toBeNull() - expect(document.querySelector('[data-slot="popover-content"]')).toBeNull() - expect(projectField().getAttribute('aria-expanded')).toBe('false') - expect(projectField().hasAttribute('aria-activedescendant')).toBe(false) - const path = document.querySelector('[aria-label="Project path"]')! - expect(document.activeElement).toBe(path) - expect(path.closest('[aria-hidden="true"]')).toBeNull() - expect(projectField().closest('[aria-hidden="true"]')).not.toBeNull() - - await act(async () => { - Array.from(document.querySelectorAll('button')) - .find((b) => b.textContent === 'Cancel')! - .click() - }) - await vi.waitFor(() => - expect(document.activeElement).toBe( - document.querySelector('[aria-label="Workspace name"]') - ) - ) - await act(async () => projectField().click()) - expect(projectField().getAttribute('aria-expanded')).toBe('true') - expect(document.querySelector('[role="listbox"]')).not.toBeNull() - } - ) - } -) - it('keeps the selector closed when a late creation dialog focuses before its trap mounts', async () => { await act(async () => root.render( diff --git a/src/renderer/src/components/new-workspace/ProjectCombobox.tsx b/src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.tsx similarity index 98% rename from src/renderer/src/components/new-workspace/ProjectCombobox.tsx rename to src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.tsx index 5dd73b325d7..31c4bcda0e8 100644 --- a/src/renderer/src/components/new-workspace/ProjectCombobox.tsx +++ b/src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.tsx @@ -19,10 +19,11 @@ import { } from './ProjectComboboxAddProjectOption' import { ProjectOptionMark, ProjectOptionRow } from './ProjectComboboxRow' import { useRecentProjectIds } from './use-recent-project-ids' -import { isWithinComboboxRoot, useTypeAheadCombobox } from './use-type-ahead-combobox' -import { COMBOBOX_FIELD_SHELL, COMBOBOX_POPOVER_SURFACE } from './type-ahead-combobox-styles' - -export type { ProjectComboboxAddProjectMenu } +import { isWithinComboboxRoot, useTypeAheadCombobox } from '../type-ahead/use-type-ahead-combobox' +import { + COMBOBOX_FIELD_SHELL, + COMBOBOX_POPOVER_SURFACE +} from '../type-ahead/type-ahead-combobox-styles' type ProjectComboboxProps = { options: readonly NewWorkspaceProjectOption[] diff --git a/src/renderer/src/components/new-workspace/ProjectComboboxAddProjectOption.tsx b/src/renderer/src/components/new-workspace/project-selection/ProjectComboboxAddProjectOption.tsx similarity index 98% rename from src/renderer/src/components/new-workspace/ProjectComboboxAddProjectOption.tsx rename to src/renderer/src/components/new-workspace/project-selection/ProjectComboboxAddProjectOption.tsx index 901d3cfcce9..2394eeeff2e 100644 --- a/src/renderer/src/components/new-workspace/ProjectComboboxAddProjectOption.tsx +++ b/src/renderer/src/components/new-workspace/project-selection/ProjectComboboxAddProjectOption.tsx @@ -6,7 +6,7 @@ import { ADD_PROJECT_MENU_ATTRIBUTE } from '@/components/sidebar/project-admissi import { AddProjectMenu } from '@/components/sidebar/project-admission/menu/AddProjectMenu' import { useTranslate } from '@/i18n/use-translate' import { cn } from '@/lib/class-names' -import type { ExecutionHostId } from '../../../../shared/execution-host' +import type { ExecutionHostId } from '../../../../../shared/execution-host' export type ProjectComboboxAddProjectMenu = { defaultHostId?: ExecutionHostId | null diff --git a/src/renderer/src/components/new-workspace/ProjectComboboxRow.tsx b/src/renderer/src/components/new-workspace/project-selection/ProjectComboboxRow.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/ProjectComboboxRow.tsx rename to src/renderer/src/components/new-workspace/project-selection/ProjectComboboxRow.tsx diff --git a/src/renderer/src/components/new-workspace/composer-add-project-default-host.ts b/src/renderer/src/components/new-workspace/project-selection/composer-add-project-default-host.ts similarity index 94% rename from src/renderer/src/components/new-workspace/composer-add-project-default-host.ts rename to src/renderer/src/components/new-workspace/project-selection/composer-add-project-default-host.ts index 2d295929f89..678b97456ca 100644 --- a/src/renderer/src/components/new-workspace/composer-add-project-default-host.ts +++ b/src/renderer/src/components/new-workspace/project-selection/composer-add-project-default-host.ts @@ -1,5 +1,5 @@ import type { ProjectHostSetupOption } from '@/lib/workspace/creation/project-host-setup-options' -import type { ExecutionHostId } from '../../../../shared/execution-host' +import type { ExecutionHostId } from '../../../../../shared/execution-host' /** Sync the Add project menu's initial host with the composer's active Run on host selection. */ export function resolveComposerAddProjectDefaultHostId({ diff --git a/src/renderer/src/components/new-workspace/project-combobox-localized-copy.ts b/src/renderer/src/components/new-workspace/project-selection/project-combobox-localized-copy.ts similarity index 100% rename from src/renderer/src/components/new-workspace/project-combobox-localized-copy.ts rename to src/renderer/src/components/new-workspace/project-selection/project-combobox-localized-copy.ts diff --git a/src/renderer/src/components/new-workspace/project-combobox-matching.ts b/src/renderer/src/components/new-workspace/project-selection/project-combobox-matching.ts similarity index 100% rename from src/renderer/src/components/new-workspace/project-combobox-matching.ts rename to src/renderer/src/components/new-workspace/project-selection/project-combobox-matching.ts diff --git a/src/renderer/src/components/new-workspace/use-composer-hosted-add-project.ts b/src/renderer/src/components/new-workspace/project-selection/use-composer-hosted-add-project.ts similarity index 100% rename from src/renderer/src/components/new-workspace/use-composer-hosted-add-project.ts rename to src/renderer/src/components/new-workspace/project-selection/use-composer-hosted-add-project.ts diff --git a/src/renderer/src/components/new-workspace/use-recent-project-ids.test.ts b/src/renderer/src/components/new-workspace/project-selection/use-recent-project-ids.test.ts similarity index 94% rename from src/renderer/src/components/new-workspace/use-recent-project-ids.test.ts rename to src/renderer/src/components/new-workspace/project-selection/use-recent-project-ids.test.ts index 3303787c731..38f04df6944 100644 --- a/src/renderer/src/components/new-workspace/use-recent-project-ids.test.ts +++ b/src/renderer/src/components/new-workspace/project-selection/use-recent-project-ids.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import type { Worktree } from '../../../../shared/worktree/types' +import type { Worktree } from '../../../../../shared/worktree/types' import { orderProjectIdsByRecency } from './use-recent-project-ids' function worktree(partial: Partial): Worktree { diff --git a/src/renderer/src/components/new-workspace/use-recent-project-ids.ts b/src/renderer/src/components/new-workspace/project-selection/use-recent-project-ids.ts similarity index 94% rename from src/renderer/src/components/new-workspace/use-recent-project-ids.ts rename to src/renderer/src/components/new-workspace/project-selection/use-recent-project-ids.ts index b444193e79a..cf3617104b2 100644 --- a/src/renderer/src/components/new-workspace/use-recent-project-ids.ts +++ b/src/renderer/src/components/new-workspace/project-selection/use-recent-project-ids.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react' -import type { Worktree } from '../../../../shared/worktree/types' +import type { Worktree } from '../../../../../shared/worktree/types' import { useAllWorktrees } from '@/store/slices/worktrees/listing/use-worktree-catalog' import { NEW_WORKSPACE_PROJECT_GROUP_OPTION_PREFIX } from '@/lib/workspace/creation/new-workspace-project-options' diff --git a/src/renderer/src/components/new-workspace/RunTargetCombobox.fixture.tsx b/src/renderer/src/components/new-workspace/run-target/RunTargetCombobox.fixture.tsx similarity index 96% rename from src/renderer/src/components/new-workspace/RunTargetCombobox.fixture.tsx rename to src/renderer/src/components/new-workspace/run-target/RunTargetCombobox.fixture.tsx index 84fd7478975..b0b531166de 100644 --- a/src/renderer/src/components/new-workspace/RunTargetCombobox.fixture.tsx +++ b/src/renderer/src/components/new-workspace/run-target/RunTargetCombobox.fixture.tsx @@ -1,4 +1,4 @@ -import { defineFixture } from '../../component-fixtures/component-fixture' +import { defineFixture } from '../../../component-fixtures/component-fixture' import type { ProjectHostSetupOption } from '@/lib/workspace/creation/project-host-setup-options' import RunTargetCombobox from './RunTargetCombobox' diff --git a/src/renderer/src/components/new-workspace/RunTargetCombobox.tsx b/src/renderer/src/components/new-workspace/run-target/RunTargetCombobox.tsx similarity index 97% rename from src/renderer/src/components/new-workspace/RunTargetCombobox.tsx rename to src/renderer/src/components/new-workspace/run-target/RunTargetCombobox.tsx index 475a2672d29..bc72d466f7f 100644 --- a/src/renderer/src/components/new-workspace/RunTargetCombobox.tsx +++ b/src/renderer/src/components/new-workspace/run-target/RunTargetCombobox.tsx @@ -8,9 +8,9 @@ import { type ProjectHostSetupOption, type ReadyProjectHostSetupOption } from '@/lib/workspace/creation/project-host-setup-options' -import type { ExecutionHostId } from '../../../../shared/execution-host' -import { isWithinComboboxRoot, useTypeAheadCombobox } from './use-type-ahead-combobox' -import { COMBOBOX_POPOVER_SURFACE } from './type-ahead-combobox-styles' +import type { ExecutionHostId } from '../../../../../shared/execution-host' +import { isWithinComboboxRoot, useTypeAheadCombobox } from '../type-ahead/use-type-ahead-combobox' +import { COMBOBOX_POPOVER_SURFACE } from '../type-ahead/type-ahead-combobox-styles' import { buildRunTargetRows, getEphemeralVmLabel, diff --git a/src/renderer/src/components/new-workspace/RunTargetComboboxRow.tsx b/src/renderer/src/components/new-workspace/run-target/RunTargetComboboxRow.tsx similarity index 95% rename from src/renderer/src/components/new-workspace/RunTargetComboboxRow.tsx rename to src/renderer/src/components/new-workspace/run-target/RunTargetComboboxRow.tsx index 050a11dd2c9..2879d9d6a0c 100644 --- a/src/renderer/src/components/new-workspace/RunTargetComboboxRow.tsx +++ b/src/renderer/src/components/new-workspace/run-target/RunTargetComboboxRow.tsx @@ -1,10 +1,7 @@ import React from 'react' import { ChevronRight } from 'lucide-react' import { cn } from '@/lib/class-names' -import { ProjectOptionDetail } from './ProjectComboboxRow' -import { HostRowIcon } from '../host-row-icon' - -export { HostRowIcon } +import { ProjectOptionDetail } from '../project-selection/ProjectComboboxRow' /** Match the project picker with details below each target name. */ export function RunTargetRow({ diff --git a/src/renderer/src/components/new-workspace/RunTargetField.tsx b/src/renderer/src/components/new-workspace/run-target/RunTargetField.tsx similarity index 95% rename from src/renderer/src/components/new-workspace/RunTargetField.tsx rename to src/renderer/src/components/new-workspace/run-target/RunTargetField.tsx index ce81396509f..9315a445924 100644 --- a/src/renderer/src/components/new-workspace/RunTargetField.tsx +++ b/src/renderer/src/components/new-workspace/run-target/RunTargetField.tsx @@ -5,9 +5,9 @@ import { PopoverAnchor } from '@/components/ui/popover' import { cn } from '@/lib/class-names' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { useTranslate } from '@/i18n/use-translate' -import type { ExecutionHostId } from '../../../../shared/execution-host' -import { HostRowIcon } from './RunTargetComboboxRow' -import { COMBOBOX_FIELD_SHELL } from './type-ahead-combobox-styles' +import type { ExecutionHostId } from '../../../../../shared/execution-host' +import { HostRowIcon } from '../../host-row-icon' +import { COMBOBOX_FIELD_SHELL } from '../type-ahead/type-ahead-combobox-styles' /** Overlay the committed selection while keeping the input ready to search. */ export default function RunTargetField({ diff --git a/src/renderer/src/components/new-workspace/RunTargetRowActions.tsx b/src/renderer/src/components/new-workspace/run-target/RunTargetRowActions.tsx similarity index 96% rename from src/renderer/src/components/new-workspace/RunTargetRowActions.tsx rename to src/renderer/src/components/new-workspace/run-target/RunTargetRowActions.tsx index a25909784b5..738dbdc9c07 100644 --- a/src/renderer/src/components/new-workspace/RunTargetRowActions.tsx +++ b/src/renderer/src/components/new-workspace/run-target/RunTargetRowActions.tsx @@ -2,9 +2,9 @@ import React from 'react' import { AlertTriangle, LoaderCircle } from 'lucide-react' import { Button } from '@/components/ui/button' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' -import type { ExecutionHostId } from '../../../../shared/execution-host' +import type { ExecutionHostId } from '../../../../../shared/execution-host' import { useTranslate } from '@/i18n/use-translate' -import { HostRowIcon } from '../host-row-icon' +import { HostRowIcon } from '../../host-row-icon' /** Glyph for a host that can't run yet: connecting, broken, or merely dormant. */ export function NeedsSetupHostIcon({ diff --git a/src/renderer/src/components/new-workspace/RunTargetRowList.tsx b/src/renderer/src/components/new-workspace/run-target/RunTargetRowList.tsx similarity index 97% rename from src/renderer/src/components/new-workspace/RunTargetRowList.tsx rename to src/renderer/src/components/new-workspace/run-target/RunTargetRowList.tsx index b2b7d7d4195..7e46ee475c6 100644 --- a/src/renderer/src/components/new-workspace/RunTargetRowList.tsx +++ b/src/renderer/src/components/new-workspace/run-target/RunTargetRowList.tsx @@ -3,7 +3,8 @@ import type { NeedsSetupProjectHostOption, ReadyProjectHostSetupOption } from '@/lib/workspace/creation/project-host-setup-options' -import { HostRowIcon, RunTargetRow } from './RunTargetComboboxRow' +import { HostRowIcon } from '../../host-row-icon' +import { RunTargetRow } from './RunTargetComboboxRow' import { ConnectHostButton, NeedsSetupHostIcon, SetLocationButton } from './RunTargetRowActions' import { RecipesSubmenuRow } from './RunTargetSubmenus' import type { EphemeralVmRecipeOption, RunTargetRowModel } from './run-target-options' diff --git a/src/renderer/src/components/new-workspace/RunTargetSubmenus.tsx b/src/renderer/src/components/new-workspace/run-target/RunTargetSubmenus.tsx similarity index 97% rename from src/renderer/src/components/new-workspace/RunTargetSubmenus.tsx rename to src/renderer/src/components/new-workspace/run-target/RunTargetSubmenus.tsx index c27b157b661..5e3c9e2242e 100644 --- a/src/renderer/src/components/new-workspace/RunTargetSubmenus.tsx +++ b/src/renderer/src/components/new-workspace/run-target/RunTargetSubmenus.tsx @@ -7,10 +7,9 @@ import { RunTargetRow } from './RunTargetComboboxRow' import { getEphemeralVmLabel, getRecipeDetail, - RUN_TARGET_ADD_HOST_KEY, type EphemeralVmRecipeOption } from './run-target-options' -import { COMBOBOX_POPOVER_SURFACE } from './type-ahead-combobox-styles' +import { COMBOBOX_POPOVER_SURFACE } from '../type-ahead/type-ahead-combobox-styles' const SUBMENU_CONTENT = cn('w-72 p-1', COMBOBOX_POPOVER_SURFACE) @@ -185,5 +184,3 @@ export function AddHostSubmenuRow({ ) } - -export { RUN_TARGET_ADD_HOST_KEY } diff --git a/src/renderer/src/components/new-workspace/run-target-options.ts b/src/renderer/src/components/new-workspace/run-target/run-target-options.ts similarity index 98% rename from src/renderer/src/components/new-workspace/run-target-options.ts rename to src/renderer/src/components/new-workspace/run-target/run-target-options.ts index 620b0da9f32..0c4008c64e6 100644 --- a/src/renderer/src/components/new-workspace/run-target-options.ts +++ b/src/renderer/src/components/new-workspace/run-target/run-target-options.ts @@ -1,4 +1,4 @@ -import type { OrcaHooks } from '../../../../shared/orca-yaml-hook-types' +import type { OrcaHooks } from '../../../../../shared/orca-yaml-hook-types' import { getProjectHostSetupSelectionKey, type NeedsSetupProjectHostOption, diff --git a/src/renderer/src/components/new-workspace/DirectWorkItemSetupDialog.test.tsx b/src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/DirectWorkItemSetupDialog.test.tsx rename to src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx diff --git a/src/renderer/src/components/new-workspace/DirectWorkItemSetupDialog.tsx b/src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/DirectWorkItemSetupDialog.tsx rename to src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.tsx diff --git a/src/renderer/src/components/new-workspace/SetupCommandPreview.tsx b/src/renderer/src/components/new-workspace/setup/SetupCommandPreview.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/SetupCommandPreview.tsx rename to src/renderer/src/components/new-workspace/setup/SetupCommandPreview.tsx diff --git a/src/renderer/src/components/new-workspace/WorkspaceSetupChoice.test.tsx b/src/renderer/src/components/new-workspace/setup/WorkspaceSetupChoice.test.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/WorkspaceSetupChoice.test.tsx rename to src/renderer/src/components/new-workspace/setup/WorkspaceSetupChoice.test.tsx diff --git a/src/renderer/src/components/new-workspace/WorkspaceSetupChoice.tsx b/src/renderer/src/components/new-workspace/setup/WorkspaceSetupChoice.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/WorkspaceSetupChoice.tsx rename to src/renderer/src/components/new-workspace/setup/WorkspaceSetupChoice.tsx diff --git a/src/renderer/src/components/new-workspace/WorkspaceSetupSection.tsx b/src/renderer/src/components/new-workspace/setup/WorkspaceSetupSection.tsx similarity index 94% rename from src/renderer/src/components/new-workspace/WorkspaceSetupSection.tsx rename to src/renderer/src/components/new-workspace/setup/WorkspaceSetupSection.tsx index 0ed897fab96..c374a167ac2 100644 --- a/src/renderer/src/components/new-workspace/WorkspaceSetupSection.tsx +++ b/src/renderer/src/components/new-workspace/setup/WorkspaceSetupSection.tsx @@ -1,7 +1,7 @@ import type { ReactNode } from 'react' -import type { SetupConfig } from '../../../../shared/orca-hook-setup-config' -import type { SetupDecision } from '../../../../shared/worktree/create-types' -import type { SetupAgentStartupPolicy } from '../../../../shared/orca-yaml-hook-types' +import type { SetupConfig } from '../../../../../shared/orca-hook-setup-config' +import type { SetupDecision } from '../../../../../shared/worktree/create-types' +import type { SetupAgentStartupPolicy } from '../../../../../shared/orca-yaml-hook-types' import { Switch } from '@/components/ui/switch' import { Button } from '@/components/ui/button' import { cn } from '@/lib/class-names' diff --git a/src/renderer/src/components/new-workspace/direct-work-item-setup-copy.ts b/src/renderer/src/components/new-workspace/setup/direct-work-item-setup-copy.ts similarity index 100% rename from src/renderer/src/components/new-workspace/direct-work-item-setup-copy.ts rename to src/renderer/src/components/new-workspace/setup/direct-work-item-setup-copy.ts diff --git a/src/renderer/src/components/new-workspace/workspace-setup-composer-copy.ts b/src/renderer/src/components/new-workspace/setup/workspace-setup-composer-copy.ts similarity index 100% rename from src/renderer/src/components/new-workspace/workspace-setup-composer-copy.ts rename to src/renderer/src/components/new-workspace/setup/workspace-setup-composer-copy.ts diff --git a/src/renderer/src/components/new-workspace/workspace-setup-section-model.ts b/src/renderer/src/components/new-workspace/setup/workspace-setup-section-model.ts similarity index 89% rename from src/renderer/src/components/new-workspace/workspace-setup-section-model.ts rename to src/renderer/src/components/new-workspace/setup/workspace-setup-section-model.ts index 488590bfa02..1ac8ad68a1f 100644 --- a/src/renderer/src/components/new-workspace/workspace-setup-section-model.ts +++ b/src/renderer/src/components/new-workspace/setup/workspace-setup-section-model.ts @@ -1,4 +1,4 @@ -import type { SetupConfig } from '../../../../shared/orca-hook-setup-config' +import type { SetupConfig } from '../../../../../shared/orca-hook-setup-config' export type WorkspaceSetupKindCopy = { configLabel: string diff --git a/src/renderer/src/components/new-workspace/smart-workspace-command-value.ts b/src/renderer/src/components/new-workspace/smart-workspace-command-value.ts deleted file mode 100644 index d0c201cce47..00000000000 --- a/src/renderer/src/components/new-workspace/smart-workspace-command-value.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '../../../../shared/new-workspace/smart-workspace-command-value' diff --git a/src/renderer/src/components/new-workspace/SmartWorkspaceSourceResultList.tsx b/src/renderer/src/components/new-workspace/source-search/SmartWorkspaceSourceResultList.tsx similarity index 98% rename from src/renderer/src/components/new-workspace/SmartWorkspaceSourceResultList.tsx rename to src/renderer/src/components/new-workspace/source-search/SmartWorkspaceSourceResultList.tsx index e1fe802d6c3..a2ade66aeef 100644 --- a/src/renderer/src/components/new-workspace/SmartWorkspaceSourceResultList.tsx +++ b/src/renderer/src/components/new-workspace/source-search/SmartWorkspaceSourceResultList.tsx @@ -1,12 +1,12 @@ import React from 'react' -import { isIntegrationCredentialDecryptionError } from '../../../../shared/integration-credential-errors' +import { isIntegrationCredentialDecryptionError } from '../../../../../shared/integration-credential-errors' import { Button } from '@/components/ui/button' import { CommandGroup, CommandItem, CommandList } from '@/components/ui/command' import { useTranslate } from '@/i18n/use-translate' import { cn } from '@/lib/class-names' import { translateSmartWorkspaceEmptyHint } from './smart-workspace-empty-hint' import { RowIcon, RowLabel } from './smart-workspace-source-row-content' -import type { RowEntry } from './smart-workspace-name-field-model' +import type { RowEntry } from '../name-field/smart-workspace-name-field-model' import type { WorkItemSourceSearch } from './use-work-item-source-search' export function SmartWorkspaceSourceResultList({ diff --git a/src/renderer/src/components/new-workspace/SmartWorkspaceSourceTabs.fixture.tsx b/src/renderer/src/components/new-workspace/source-search/SmartWorkspaceSourceTabs.fixture.tsx similarity index 85% rename from src/renderer/src/components/new-workspace/SmartWorkspaceSourceTabs.fixture.tsx rename to src/renderer/src/components/new-workspace/source-search/SmartWorkspaceSourceTabs.fixture.tsx index ec381e94432..81bfa2d957a 100644 --- a/src/renderer/src/components/new-workspace/SmartWorkspaceSourceTabs.fixture.tsx +++ b/src/renderer/src/components/new-workspace/source-search/SmartWorkspaceSourceTabs.fixture.tsx @@ -1,8 +1,8 @@ import { useState } from 'react' -import { defineFixture } from '../../component-fixtures/component-fixture' +import { defineFixture } from '../../../component-fixtures/component-fixture' import { getSmartWorkspaceNameModes } from './smart-workspace-localized-options' import { SmartWorkspaceSourceTabs } from './SmartWorkspaceSourceTabs' -import type { SmartNameMode } from '../../../../shared/new-workspace/smart-workspace-source-results' +import type { SmartNameMode } from '../../../../../shared/new-workspace/smart-workspace-source-results' function Preview() { const [mode, setMode] = useState('smart') diff --git a/src/renderer/src/components/new-workspace/SmartWorkspaceSourceTabs.tsx b/src/renderer/src/components/new-workspace/source-search/SmartWorkspaceSourceTabs.tsx similarity index 100% rename from src/renderer/src/components/new-workspace/SmartWorkspaceSourceTabs.tsx rename to src/renderer/src/components/new-workspace/source-search/SmartWorkspaceSourceTabs.tsx diff --git a/src/renderer/src/components/new-workspace/jira-source-status-message.ts b/src/renderer/src/components/new-workspace/source-search/jira-source-status-message.ts similarity index 100% rename from src/renderer/src/components/new-workspace/jira-source-status-message.ts rename to src/renderer/src/components/new-workspace/source-search/jira-source-status-message.ts diff --git a/src/renderer/src/components/new-workspace/smart-workspace-empty-hint.ts b/src/renderer/src/components/new-workspace/source-search/smart-workspace-empty-hint.ts similarity index 92% rename from src/renderer/src/components/new-workspace/smart-workspace-empty-hint.ts rename to src/renderer/src/components/new-workspace/source-search/smart-workspace-empty-hint.ts index be815d42a24..9fbbdeca5cf 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-empty-hint.ts +++ b/src/renderer/src/components/new-workspace/source-search/smart-workspace-empty-hint.ts @@ -1,5 +1,5 @@ import { translate } from '@/i18n/i18n' -import type { SmartNameMode } from '../../../../shared/new-workspace/smart-workspace-source-results' +import type { SmartNameMode } from '../../../../../shared/new-workspace/smart-workspace-source-results' export function translateSmartWorkspaceEmptyHint(mode: SmartNameMode): string { switch (mode) { diff --git a/src/renderer/src/components/new-workspace/smart-workspace-github-direct-link.test.ts b/src/renderer/src/components/new-workspace/source-search/smart-workspace-github-direct-link.test.ts similarity index 90% rename from src/renderer/src/components/new-workspace/smart-workspace-github-direct-link.test.ts rename to src/renderer/src/components/new-workspace/source-search/smart-workspace-github-direct-link.test.ts index 87e6ae7c32d..c49dbdda1eb 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-github-direct-link.test.ts +++ b/src/renderer/src/components/new-workspace/source-search/smart-workspace-github-direct-link.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest' -import { parseGitHubIssueOrPRLink } from '../../../../shared/github/repositories/links' -import type { RepoOption } from './smart-workspace-name-field-model' +import { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' +import type { RepoOption } from '../name-field/smart-workspace-name-field-model' import { resolveSmartWorkspaceGithubDirectLink } from './smart-workspace-github-direct-link' const slugs: Record = { diff --git a/src/renderer/src/components/new-workspace/smart-workspace-github-direct-link.ts b/src/renderer/src/components/new-workspace/source-search/smart-workspace-github-direct-link.ts similarity index 92% rename from src/renderer/src/components/new-workspace/smart-workspace-github-direct-link.ts rename to src/renderer/src/components/new-workspace/source-search/smart-workspace-github-direct-link.ts index 17cac1bfd7c..c1b58035893 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-github-direct-link.ts +++ b/src/renderer/src/components/new-workspace/source-search/smart-workspace-github-direct-link.ts @@ -1,20 +1,20 @@ import type React from 'react' -import type { parseGitHubIssueOrPRLink } from '../../../../shared/github/repositories/links' +import type { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' import { lookupGitHubWorkItemByOwnerRepoForSource, lookupGitHubWorkItemForSource } from '@/lib/work-items/github/github-work-item-source-lookup' import { lookupSmartGitHubSubmitItem } from '@/lib/work-items/github/smart-github-submit' -import type { GitHubWorkItem } from '../../../../shared/github/work-items/work-item-types' +import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' import { buildTaskSourceContextFromRepo, type TaskSourceContext -} from '../../../../shared/work-items/task-source-context' +} from '../../../../../shared/work-items/task-source-context' import type { CrossRepoPrompt, RepoBackedSearchTarget, RepoOption -} from './smart-workspace-name-field-model' +} from '../name-field/smart-workspace-name-field-model' import { findMatchingRepoForSlug, getRepoSlugCached, sameSlug } from './smart-workspace-repo-slug' type DirectLink = NonNullable> diff --git a/src/renderer/src/components/new-workspace/smart-workspace-localized-options.tsx b/src/renderer/src/components/new-workspace/source-search/smart-workspace-localized-options.tsx similarity index 96% rename from src/renderer/src/components/new-workspace/smart-workspace-localized-options.tsx rename to src/renderer/src/components/new-workspace/source-search/smart-workspace-localized-options.tsx index d4778066407..ad81585d733 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-localized-options.tsx +++ b/src/renderer/src/components/new-workspace/source-search/smart-workspace-localized-options.tsx @@ -4,7 +4,7 @@ import { CaseSensitive, GitBranch, Github, Gitlab, Sparkles } from 'lucide-react import { JiraIcon } from '@/components/icons/JiraIcon' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import type { SmartNameMode } from '../../../../shared/new-workspace/smart-workspace-source-results' +import type { SmartNameMode } from '../../../../../shared/new-workspace/smart-workspace-source-results' export type MrStateFilter = 'opened' | 'merged' | 'closed' | 'all' diff --git a/src/renderer/src/components/new-workspace/smart-workspace-provider-availability.ts b/src/renderer/src/components/new-workspace/source-search/smart-workspace-provider-availability.ts similarity index 93% rename from src/renderer/src/components/new-workspace/smart-workspace-provider-availability.ts rename to src/renderer/src/components/new-workspace/source-search/smart-workspace-provider-availability.ts index d10b78171c4..7973919b746 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-provider-availability.ts +++ b/src/renderer/src/components/new-workspace/source-search/smart-workspace-provider-availability.ts @@ -1,4 +1,4 @@ -import { parseExecutionHostId, type ExecutionHostId } from '../../../../shared/execution-host' +import { parseExecutionHostId, type ExecutionHostId } from '../../../../../shared/execution-host' export function canUseGitLabSmartSource({ localGitlabAvailable, diff --git a/src/renderer/src/components/new-workspace/smart-workspace-repo-slug.ts b/src/renderer/src/components/new-workspace/source-search/smart-workspace-repo-slug.ts similarity index 83% rename from src/renderer/src/components/new-workspace/smart-workspace-repo-slug.ts rename to src/renderer/src/components/new-workspace/source-search/smart-workspace-repo-slug.ts index 3fa1ae993d0..ca7ed3c0617 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-repo-slug.ts +++ b/src/renderer/src/components/new-workspace/source-search/smart-workspace-repo-slug.ts @@ -1,15 +1,15 @@ -import type { RepoSlug } from '../../../../shared/github/repositories/links' +import type { RepoSlug } from '../../../../../shared/github/repositories/links' import { callRuntimeRpc } from '@/runtime/rpc/calls/runtime-rpc-call' import { getGitHubRuntimeRepoId, getGitHubSourceRuntimeTarget } from '@/lib/work-items/github/github-source-runtime-context' -import { githubRepoIdentityKey } from '../../../../shared/github/repositories/repository-identity-key' +import { githubRepoIdentityKey } from '../../../../../shared/github/repositories/repository-identity-key' import { getTaskSourceCacheScope, type TaskSourceContext -} from '../../../../shared/work-items/task-source-context' -import type { RepoOption, RepoSlugTarget } from './smart-workspace-name-field-model' +} from '../../../../../shared/work-items/task-source-context' +import type { RepoOption, RepoSlugTarget } from '../name-field/smart-workspace-name-field-model' export function sameSlug(left: RepoSlug, right: RepoSlug): boolean { return githubRepoIdentityKey(left) === githubRepoIdentityKey(right) diff --git a/src/renderer/src/components/new-workspace/smart-workspace-source-row-content.tsx b/src/renderer/src/components/new-workspace/source-search/smart-workspace-source-row-content.tsx similarity index 95% rename from src/renderer/src/components/new-workspace/smart-workspace-source-row-content.tsx rename to src/renderer/src/components/new-workspace/source-search/smart-workspace-source-row-content.tsx index e22af1ec0e2..f7f6e9e341e 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-source-row-content.tsx +++ b/src/renderer/src/components/new-workspace/source-search/smart-workspace-source-row-content.tsx @@ -12,9 +12,12 @@ import { JiraIcon } from '@/components/icons/JiraIcon' import { Trans } from 'react-i18next' import { i18n } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' -import { branchRefLabel } from '../../../../shared/git/refs/branch-ref-label' -import type { JiraSite } from '../../../../shared/jira/jira-types' -import type { RowEntry, SmartWorkspaceNameSelection } from './smart-workspace-name-field-model' +import { branchRefLabel } from '../../../../../shared/git/refs/branch-ref-label' +import type { JiraSite } from '../../../../../shared/jira/jira-types' +import type { + RowEntry, + SmartWorkspaceNameSelection +} from '../name-field/smart-workspace-name-field-model' export function RowIcon({ row }: { row: RowEntry }): React.JSX.Element { if (row.kind === 'use-name') { diff --git a/src/renderer/src/components/new-workspace/use-composer-source-scope.ts b/src/renderer/src/components/new-workspace/source-search/use-composer-source-scope.ts similarity index 74% rename from src/renderer/src/components/new-workspace/use-composer-source-scope.ts rename to src/renderer/src/components/new-workspace/source-search/use-composer-source-scope.ts index f4c8686ebfa..993c6b716cb 100644 --- a/src/renderer/src/components/new-workspace/use-composer-source-scope.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-composer-source-scope.ts @@ -1,7 +1,7 @@ import { useState } from 'react' -import type { Repo } from '../../../../shared/repo-types' -import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context' -import { getRepoExecutionHostId } from '../../../../shared/execution-host' +import type { Repo } from '../../../../../shared/repo-types' +import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import { getRepoExecutionHostId } from '../../../../../shared/execution-host' export function useComposerSourceScope( repo: Repo | null, diff --git a/src/renderer/src/components/new-workspace/use-jira-composer-project.ts b/src/renderer/src/components/new-workspace/source-search/use-jira-composer-project.ts similarity index 61% rename from src/renderer/src/components/new-workspace/use-jira-composer-project.ts rename to src/renderer/src/components/new-workspace/source-search/use-jira-composer-project.ts index d932249a15c..e0965c0f3f3 100644 --- a/src/renderer/src/components/new-workspace/use-jira-composer-project.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-jira-composer-project.ts @@ -1,9 +1,9 @@ -import type { JiraProjectReference } from '../../../../shared/jira/jira-project-reference' -import type { Repo } from '../../../../shared/repo-types' -import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context' -import { getRepoExecutionHostId } from '../../../../shared/execution-host' -import { resolveJiraSourceHostId } from '../../../../shared/jira/jira-source-host' -import { useJiraProjectInventory } from '../use-jira-project-inventory' +import type { JiraProjectReference } from '../../../../../shared/jira/jira-project-reference' +import type { Repo } from '../../../../../shared/repo-types' +import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import { getRepoExecutionHostId } from '../../../../../shared/execution-host' +import { resolveJiraSourceHostId } from '../../../../../shared/jira/jira-source-host' +import { useJiraProjectInventory } from '../../use-jira-project-inventory' import { useComposerSourceScope } from './use-composer-source-scope' const LOCAL_SOURCE = { kind: 'local' } as const diff --git a/src/renderer/src/components/new-workspace/use-jira-source-connection.test.tsx b/src/renderer/src/components/new-workspace/source-search/use-jira-source-connection.test.tsx similarity index 93% rename from src/renderer/src/components/new-workspace/use-jira-source-connection.test.tsx rename to src/renderer/src/components/new-workspace/source-search/use-jira-source-connection.test.tsx index 5b2925b7346..7aa90ae9d67 100644 --- a/src/renderer/src/components/new-workspace/use-jira-source-connection.test.tsx +++ b/src/renderer/src/components/new-workspace/source-search/use-jira-source-connection.test.tsx @@ -3,8 +3,8 @@ import { act, renderHook } from '@testing-library/react' import type * as React from 'react' import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context' -import type { JiraConnectionStatus } from '../../../../shared/jira/jira-types' +import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import type { JiraConnectionStatus } from '../../../../../shared/jira/jira-types' import { useJiraSourceConnection } from './use-jira-source-connection' const mocks = vi.hoisted(() => ({ diff --git a/src/renderer/src/components/new-workspace/use-jira-source-connection.ts b/src/renderer/src/components/new-workspace/source-search/use-jira-source-connection.ts similarity index 96% rename from src/renderer/src/components/new-workspace/use-jira-source-connection.ts rename to src/renderer/src/components/new-workspace/source-search/use-jira-source-connection.ts index 0ab50773d1a..0bef385179d 100644 --- a/src/renderer/src/components/new-workspace/use-jira-source-connection.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-jira-source-connection.ts @@ -4,8 +4,8 @@ import { getTaskSourceCacheScope, getTaskSourceRuntimeSettings, type TaskSourceContext -} from '../../../../shared/work-items/task-source-context' -import type { JiraConnectionStatus } from '../../../../shared/jira/jira-types' +} from '../../../../../shared/work-items/task-source-context' +import type { JiraConnectionStatus } from '../../../../../shared/jira/jira-types' import { getProviderRuntimeContextKey } from '@/runtime/hosts/observations/provider-runtime-context' export type JiraSourceConnection = { diff --git a/src/renderer/src/components/new-workspace/use-jira-url-source.test.tsx b/src/renderer/src/components/new-workspace/source-search/use-jira-url-source.test.tsx similarity index 96% rename from src/renderer/src/components/new-workspace/use-jira-url-source.test.tsx rename to src/renderer/src/components/new-workspace/source-search/use-jira-url-source.test.tsx index 267127543d6..727adaf3a1a 100644 --- a/src/renderer/src/components/new-workspace/use-jira-url-source.test.tsx +++ b/src/renderer/src/components/new-workspace/source-search/use-jira-url-source.test.tsx @@ -2,8 +2,12 @@ import { act, renderHook } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { JiraConnectionStatus, JiraIssue, JiraSite } from '../../../../shared/jira/jira-types' -import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context' +import type { + JiraConnectionStatus, + JiraIssue, + JiraSite +} from '../../../../../shared/jira/jira-types' +import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' import { useJiraUrlSource } from './use-jira-url-source' const mocks = vi.hoisted(() => ({ diff --git a/src/renderer/src/components/new-workspace/use-jira-url-source.ts b/src/renderer/src/components/new-workspace/source-search/use-jira-url-source.ts similarity index 95% rename from src/renderer/src/components/new-workspace/use-jira-url-source.ts rename to src/renderer/src/components/new-workspace/source-search/use-jira-url-source.ts index 9baf170761a..f68286bb46e 100644 --- a/src/renderer/src/components/new-workspace/use-jira-url-source.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-jira-url-source.ts @@ -7,16 +7,16 @@ import { isResolvedJiraIssueMatch, parseJiraIssueUrl, type ParsedJiraIssueUrl -} from '../../../../shared/jira/jira-issue-url' -import { getJiraSummaryLookupErrorCode } from '../../../../shared/jira/jira-summary-lookup' +} from '../../../../../shared/jira/jira-issue-url' +import { getJiraSummaryLookupErrorCode } from '../../../../../shared/jira/jira-summary-lookup' import { getTaskSourceCacheScope, normalizeTaskSourceContext, type TaskSourceContext -} from '../../../../shared/work-items/task-source-context' -import { parseExecutionHostId } from '../../../../shared/execution-host' -import { WORKTREE_LINKED_WORK_ITEM_CONTEXT_RUNTIME_CAPABILITY } from '../../../../shared/protocol-version' -import type { JiraIssue, JiraSite } from '../../../../shared/jira/jira-types' +} from '../../../../../shared/work-items/task-source-context' +import { parseExecutionHostId } from '../../../../../shared/execution-host' +import { WORKTREE_LINKED_WORK_ITEM_CONTEXT_RUNTIME_CAPABILITY } from '../../../../../shared/worktree-runtime-capabilities' +import type { JiraIssue, JiraSite } from '../../../../../shared/jira/jira-types' import { canReuseLoadedJiraStatus, type JiraSourceConnection } from './use-jira-source-connection' const LOOKUP_DEBOUNCE_MS = 200 diff --git a/src/renderer/src/components/new-workspace/use-linear-source-connection.ts b/src/renderer/src/components/new-workspace/source-search/use-linear-source-connection.ts similarity index 97% rename from src/renderer/src/components/new-workspace/use-linear-source-connection.ts rename to src/renderer/src/components/new-workspace/source-search/use-linear-source-connection.ts index 1cd9e6bd503..85252a07f39 100644 --- a/src/renderer/src/components/new-workspace/use-linear-source-connection.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-linear-source-connection.ts @@ -13,8 +13,8 @@ import { getProviderRuntimeContextKey } from '@/runtime/hosts/observations/provi import { getTaskSourceRuntimeSettings, type TaskSourceContext -} from '../../../../shared/work-items/task-source-context' -import type { LinearConnectionStatus } from '../../../../shared/linear/workspace-types' +} from '../../../../../shared/work-items/task-source-context' +import type { LinearConnectionStatus } from '../../../../../shared/linear/workspace-types' type LinearSourceConnection = { status: LinearConnectionStatus | null diff --git a/src/renderer/src/components/new-workspace/use-smart-workspace-field-availability.test.ts b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.test.ts similarity index 100% rename from src/renderer/src/components/new-workspace/use-smart-workspace-field-availability.test.ts rename to src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.test.ts diff --git a/src/renderer/src/components/new-workspace/use-smart-workspace-field-availability.ts b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.ts similarity index 94% rename from src/renderer/src/components/new-workspace/use-smart-workspace-field-availability.ts rename to src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.ts index d3cc9d4adeb..b8b0119ae07 100644 --- a/src/renderer/src/components/new-workspace/use-smart-workspace-field-availability.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.ts @@ -1,14 +1,14 @@ import { useEffect, useMemo } from 'react' -import { filterAvailableTaskProviders } from '../../../../shared/work-items/task-providers' -import type { ExecutionHostId } from '../../../../shared/execution-host' +import { filterAvailableTaskProviders } from '../../../../../shared/work-items/task-providers' +import type { ExecutionHostId } from '../../../../../shared/execution-host' import { getMrStateFilters, getSmartWorkspaceNameModes } from './smart-workspace-localized-options' import { SEARCH_DEBOUNCE_MS, type NormalizedWorkItemSourceSearchProps -} from './smart-workspace-name-field-model' +} from '../name-field/smart-workspace-name-field-model' import { canUseGitLabSmartSource } from './smart-workspace-provider-availability' import type { useWorkItemSourceState } from './use-work-item-source-state' -import { useTranslate } from '../../i18n/use-translate' +import { useTranslate } from '../../../i18n/use-translate' type FieldState = ReturnType diff --git a/src/renderer/src/components/new-workspace/use-smart-workspace-github-search.ts b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-github-search.ts similarity index 95% rename from src/renderer/src/components/new-workspace/use-smart-workspace-github-search.ts rename to src/renderer/src/components/new-workspace/source-search/use-smart-workspace-github-search.ts index 65ff165b4ef..987f816a515 100644 --- a/src/renderer/src/components/new-workspace/use-smart-workspace-github-search.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-github-search.ts @@ -1,14 +1,14 @@ import { useEffect, useMemo } from 'react' import { useAppStore } from '@/store' -import { normalizeGitHubLinkQuery } from '../../../../shared/new-workspace/github-links' -import { parseGitHubIssueOrPRLink } from '../../../../shared/github/repositories/links' +import { normalizeGitHubLinkQuery } from '../../../../../shared/new-workspace/github-links' +import { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' import { lookupGitHubWorkItemByOwnerRepoForSource, lookupGitHubWorkItemForSource } from '@/lib/work-items/github/github-work-item-source-lookup' import { lookupSmartGitHubSubmitItem } from '@/lib/work-items/github/smart-github-submit' -import type { GitHubWorkItem } from '../../../../shared/github/work-items/work-item-types' -import { RESULT_LIMIT } from './smart-workspace-name-field-model' +import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import { RESULT_LIMIT } from '../name-field/smart-workspace-name-field-model' import { resolveSmartWorkspaceGithubDirectLink } from './smart-workspace-github-direct-link' import type { useWorkItemSourceFoundation } from './use-work-item-source-foundation' diff --git a/src/renderer/src/components/new-workspace/use-smart-workspace-gitlab-search.ts b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-gitlab-search.ts similarity index 94% rename from src/renderer/src/components/new-workspace/use-smart-workspace-gitlab-search.ts rename to src/renderer/src/components/new-workspace/source-search/use-smart-workspace-gitlab-search.ts index 62278de3bf5..55fca23aff7 100644 --- a/src/renderer/src/components/new-workspace/use-smart-workspace-gitlab-search.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-gitlab-search.ts @@ -3,9 +3,9 @@ import { listGitLabWorkItemsForSource, lookupGitLabWorkItemByPathForSource } from '@/lib/work-items/gitlab/gitlab-work-item-source-lookup' -import { parseGitLabIssueOrMRLink } from '../../../../shared/new-workspace/gitlab-links' -import type { GitLabWorkItem } from '../../../../shared/gitlab-types' -import { RESULT_LIMIT } from './smart-workspace-name-field-model' +import { parseGitLabIssueOrMRLink } from '../../../../../shared/new-workspace/gitlab-links' +import type { GitLabWorkItem } from '../../../../../shared/gitlab-types' +import { RESULT_LIMIT } from '../name-field/smart-workspace-name-field-model' import type { useWorkItemSourceFoundation } from './use-work-item-source-foundation' type Foundation = ReturnType diff --git a/src/renderer/src/components/new-workspace/use-smart-workspace-name-field-foundation.source.test.tsx b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-name-field-foundation.source.test.tsx similarity index 91% rename from src/renderer/src/components/new-workspace/use-smart-workspace-name-field-foundation.source.test.tsx rename to src/renderer/src/components/new-workspace/source-search/use-smart-workspace-name-field-foundation.source.test.tsx index e1900287378..a8eb2ed5132 100644 --- a/src/renderer/src/components/new-workspace/use-smart-workspace-name-field-foundation.source.test.tsx +++ b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-name-field-foundation.source.test.tsx @@ -2,11 +2,11 @@ import { linearIssueReadFixture } from '@/store/slices/linear/linear-slice-test- import { act, cleanup, renderHook, waitFor } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { useWorkItemSourceSearch } from './use-work-item-source-search' -import { referenceLinearIssue } from '../sidebar/workspace-card/review/workspace-reference-fixtures.test-support' -import type { Repo } from '../../../../shared/repo-types' -import type { LinearIssue } from '../../../../shared/linear/issue-types' -import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context' -import type { NormalizedWorkItemSourceSearchProps } from './smart-workspace-name-field-model' +import { referenceLinearIssue } from '../../sidebar/workspace-card/review/workspace-reference-fixtures.test-support' +import type { Repo } from '../../../../../shared/repo-types' +import type { LinearIssue } from '../../../../../shared/linear/issue-types' +import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import type { NormalizedWorkItemSourceSearchProps } from '../name-field/smart-workspace-name-field-model' // @vitest-environment happy-dom const mocks = vi.hoisted(() => ({ @@ -60,7 +60,7 @@ vi.mock('./use-jira-source-connection', () => ({ useJiraSourceConnection: () => ({ status: null, loaded: false }) })) vi.mock('./use-jira-url-source', () => ({ useJiraUrlSource: mocks.readJiraSource })) -vi.mock('../use-jira-project-inventory', () => ({ +vi.mock('../../use-jira-project-inventory', () => ({ useJiraProjectInventory: () => ({ projects: [], connected: true, error: null, loading: false }) })) diff --git a/src/renderer/src/components/new-workspace/use-smart-workspace-secondary-searches.ts b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-secondary-searches.ts similarity index 92% rename from src/renderer/src/components/new-workspace/use-smart-workspace-secondary-searches.ts rename to src/renderer/src/components/new-workspace/source-search/use-smart-workspace-secondary-searches.ts index 0b1ccd3a61a..c871451e331 100644 --- a/src/renderer/src/components/new-workspace/use-smart-workspace-secondary-searches.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-secondary-searches.ts @@ -5,20 +5,20 @@ import { mapLinearIssueRead, type LinearIssueRead } from '@/store/slices/linear/linear-issue-read-lifecycle' -import type { LinearIssue } from '../../../../shared/linear/issue-types' -import type { LinearCollectionResult } from '../../../../shared/linear/workspace-types' +import type { LinearIssue } from '../../../../../shared/linear/issue-types' +import type { LinearCollectionResult } from '../../../../../shared/linear/workspace-types' import { linearIssueViewError } from '@/lib/work-items/linear/linear-read-error' -import { getRepoExecutionHostId } from '../../../../shared/execution-host' +import { getRepoExecutionHostId } from '../../../../../shared/execution-host' import { useEffect, useMemo, useRef } from 'react' import { searchRuntimeRepoBaseRefDetails } from '@/runtime/source-control/references/runtime-repo-client' import { lookupLinearIssueUrl } from '@/lib/work-items/linear/linear-issue-url-lookup' import { searchJiraIssuesByKeyOrText } from '@/components/jira-key-or-text-search' -import { linearWorkspaceScopeSignature } from '../../../../shared/linear/workspace-types' -import { isIntegrationCredentialDecryptionError } from '../../../../shared/integration-credential-errors' -import { getSmartWorkspaceLinearSearchQuery } from '../../../../shared/new-workspace/smart-workspace-linear-intent' -import { RESULT_LIMIT } from './smart-workspace-name-field-model' -import { restrictJiraQueryToProject } from '../../../../shared/jira/jira-project-reference' -import { getBranchSearchRequest } from '../../../../shared/new-workspace/smart-workspace-source-results' +import { linearWorkspaceScopeSignature } from '../../../../../shared/linear/workspace-types' +import { isIntegrationCredentialDecryptionError } from '../../../../../shared/integration-credential-errors' +import { getSmartWorkspaceLinearSearchQuery } from '../../../../../shared/new-workspace/smart-workspace-linear-intent' +import { RESULT_LIMIT } from '../name-field/smart-workspace-name-field-model' +import { restrictJiraQueryToProject } from '../../../../../shared/jira/jira-project-reference' +import { getBranchSearchRequest } from '../../../../../shared/new-workspace/smart-workspace-source-results' import type { useWorkItemSourceFoundation } from './use-work-item-source-foundation' export function useSmartWorkspaceSecondarySearches({ diff --git a/src/renderer/src/components/new-workspace/use-work-item-source-foundation.ts b/src/renderer/src/components/new-workspace/source-search/use-work-item-source-foundation.ts similarity index 95% rename from src/renderer/src/components/new-workspace/use-work-item-source-foundation.ts rename to src/renderer/src/components/new-workspace/source-search/use-work-item-source-foundation.ts index 667e097bae2..ada1373cb3e 100644 --- a/src/renderer/src/components/new-workspace/use-work-item-source-foundation.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-work-item-source-foundation.ts @@ -3,13 +3,13 @@ import { useLinearIssueResult } from '@/hooks/linear-issue-committed-state' import { getLinearSourceScope } from '@/store/slices/linear/linear-slice-scope' import { getSelectedWorkspaceId } from '@/store/slices/linear/linear-cache' import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' -import { parseBoundedSmartWorkspaceLinearIssueUrlIntent } from '../../../../shared/new-workspace/smart-workspace-linear-intent' -import { isSmartWorkspaceSourceQueryWithinLimit } from '../../../../shared/new-workspace/smart-workspace-source-results' +import { parseBoundedSmartWorkspaceLinearIssueUrlIntent } from '../../../../../shared/new-workspace/smart-workspace-linear-intent' +import { isSmartWorkspaceSourceQueryWithinLimit } from '../../../../../shared/new-workspace/smart-workspace-source-query' import React, { useMemo } from 'react' import { useShallow } from 'zustand/react/shallow' import { useAppStore } from '@/store/index' import { getLocalPreflightContext } from '@/runtime/hosts/preflight/local-preflight-context' -import { buildTaskSourceContextFromRepo } from '../../../../shared/work-items/task-source-context' +import { buildTaskSourceContextFromRepo } from '../../../../../shared/work-items/task-source-context' import { useJiraSourceConnection } from './use-jira-source-connection' import { useJiraUrlSource } from './use-jira-url-source' import { useLinearSourceConnection } from './use-linear-source-connection' @@ -19,12 +19,12 @@ import { useJiraComposerProject } from './use-jira-composer-project' import { useComposerSourceScope } from './use-composer-source-scope' import type { LinearIssueData } from '@/store/slices/linear/linear-issue-read' import type { LinearIssueView } from '@/store/slices/linear/linear-issue-read-lifecycle' -import type { LinearIssue } from '../../../../shared/linear/issue-types' -import { localPreflightContextKey } from '../../runtime/hosts/preflight/local-preflight-context-key' +import type { LinearIssue } from '../../../../../shared/linear/issue-types' +import { localPreflightContextKey } from '../../../runtime/hosts/preflight/local-preflight-context-key' import type { NormalizedWorkItemSourceSearchProps, RepoBackedSearchTarget -} from './smart-workspace-name-field-model' +} from '../name-field/smart-workspace-name-field-model' export function useWorkItemSourceFoundation(props: NormalizedWorkItemSourceSearchProps) { const { diff --git a/src/renderer/src/components/new-workspace/use-work-item-source-presentation.ts b/src/renderer/src/components/new-workspace/source-search/use-work-item-source-presentation.ts similarity index 92% rename from src/renderer/src/components/new-workspace/use-work-item-source-presentation.ts rename to src/renderer/src/components/new-workspace/source-search/use-work-item-source-presentation.ts index 6d28c7b02d4..52fc1aa595c 100644 --- a/src/renderer/src/components/new-workspace/use-work-item-source-presentation.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-work-item-source-presentation.ts @@ -1,22 +1,22 @@ import { useEffect, useMemo } from 'react' import { CaseSensitive, LoaderCircle, Search } from 'lucide-react' -import { parseGitHubIssueOrPRLink } from '../../../../shared/github/repositories/links' -import { parseGitLabIssueOrMRLink } from '../../../../shared/new-workspace/gitlab-links' +import { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' +import { parseGitLabIssueOrMRLink } from '../../../../../shared/new-workspace/gitlab-links' import { isSmartWorkspaceLinearIssueIntentMatch, parseBoundedSmartWorkspaceLinearIssueInput, parseBoundedSmartWorkspaceLinearIssueUrlIntent, prioritizeSmartWorkspaceLinearIssueResults -} from '../../../../shared/new-workspace/smart-workspace-linear-intent' -import { resolveSmartWorkspaceCommandValue } from './smart-workspace-command-value' +} from '../../../../../shared/new-workspace/smart-workspace-linear-intent' +import { resolveSmartWorkspaceCommandValue } from '../../../../../shared/new-workspace/smart-workspace-command-value' import { buildSmartWorkspaceSourceRows, getVisibleBranchResults, getVisibleHeldProviderResults, - isBlockingTaskUrlResolution, - isSmartWorkspaceSourceQueryWithinLimit -} from '../../../../shared/new-workspace/smart-workspace-source-results' -import { RESULT_LIMIT, type RowEntry } from './smart-workspace-name-field-model' + isBlockingTaskUrlResolution +} from '../../../../../shared/new-workspace/smart-workspace-source-results' +import { isSmartWorkspaceSourceQueryWithinLimit } from '../../../../../shared/new-workspace/smart-workspace-source-query' +import { RESULT_LIMIT, type RowEntry } from '../name-field/smart-workspace-name-field-model' import type { useWorkItemSourceFoundation } from './use-work-item-source-foundation' type Foundation = ReturnType diff --git a/src/renderer/src/components/new-workspace/use-work-item-source-search.ts b/src/renderer/src/components/new-workspace/source-search/use-work-item-source-search.ts similarity index 93% rename from src/renderer/src/components/new-workspace/use-work-item-source-search.ts rename to src/renderer/src/components/new-workspace/source-search/use-work-item-source-search.ts index 98ee5f0f157..1ff9e36e07f 100644 --- a/src/renderer/src/components/new-workspace/use-work-item-source-search.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-work-item-source-search.ts @@ -1,16 +1,14 @@ import { resolveWorkItemSourceRow } from './work-item-source-selection' import { useCallback, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { - getJiraIssueSearchQuery, - isSmartWorkspaceSourceQueryWithinLimit -} from '../../../../shared/new-workspace/smart-workspace-source-results' -import { parseBoundedSmartWorkspaceLinearIssueUrlIntent } from '../../../../shared/new-workspace/smart-workspace-linear-intent' +import { getJiraIssueSearchQuery } from '../../../../../shared/new-workspace/smart-workspace-source-results' +import { isSmartWorkspaceSourceQueryWithinLimit } from '../../../../../shared/new-workspace/smart-workspace-source-query' +import { parseBoundedSmartWorkspaceLinearIssueUrlIntent } from '../../../../../shared/new-workspace/smart-workspace-linear-intent' import { EMPTY_REPO_SEARCH_REPOS, type NormalizedWorkItemSourceSearchProps, type WorkItemSourceSearchProps -} from './smart-workspace-name-field-model' +} from '../name-field/smart-workspace-name-field-model' import { useWorkItemSourceFoundation } from './use-work-item-source-foundation' import { useSmartWorkspaceGithubSearch } from './use-smart-workspace-github-search' import { useSmartWorkspaceGitlabSearch } from './use-smart-workspace-gitlab-search' diff --git a/src/renderer/src/components/new-workspace/use-work-item-source-state.ts b/src/renderer/src/components/new-workspace/source-search/use-work-item-source-state.ts similarity index 79% rename from src/renderer/src/components/new-workspace/use-work-item-source-state.ts rename to src/renderer/src/components/new-workspace/source-search/use-work-item-source-state.ts index 7447094f87b..079a73451f1 100644 --- a/src/renderer/src/components/new-workspace/use-work-item-source-state.ts +++ b/src/renderer/src/components/new-workspace/source-search/use-work-item-source-state.ts @@ -1,13 +1,13 @@ import { useGitHubTaskItems } from '@/hooks/useGitHubTaskItems' import { useRef, useState } from 'react' -import type { RepoSlug } from '../../../../shared/github/repositories/links' -import type { GitHubWorkItem } from '../../../../shared/github/work-items/work-item-types' -import type { GitLabWorkItem } from '../../../../shared/gitlab-types' -import type { JiraIssue } from '../../../../shared/jira/jira-types' -import type { BaseRefSearchResult } from '../../../../shared/repo-types' -import type { SmartNameMode } from '../../../../shared/new-workspace/smart-workspace-source-results' +import type { RepoSlug } from '../../../../../shared/github/repositories/links' +import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitLabWorkItem } from '../../../../../shared/gitlab-types' +import type { JiraIssue } from '../../../../../shared/jira/jira-types' +import type { BaseRefSearchResult } from '../../../../../shared/repo-types' +import type { SmartNameMode } from '../../../../../shared/new-workspace/smart-workspace-source-results' import type { MrStateFilter } from './smart-workspace-localized-options' -import type { CrossRepoPrompt } from './smart-workspace-name-field-model' +import type { CrossRepoPrompt } from '../name-field/smart-workspace-name-field-model' export function useWorkItemSourceState(textOnly: boolean, value: string) { const [mode, setMode] = useState(textOnly ? 'text' : 'smart') diff --git a/src/renderer/src/components/new-workspace/work-item-source-selection.ts b/src/renderer/src/components/new-workspace/source-search/work-item-source-selection.ts similarity index 86% rename from src/renderer/src/components/new-workspace/work-item-source-selection.ts rename to src/renderer/src/components/new-workspace/source-search/work-item-source-selection.ts index 7d4a4fa2efd..a76cd60255a 100644 --- a/src/renderer/src/components/new-workspace/work-item-source-selection.ts +++ b/src/renderer/src/components/new-workspace/source-search/work-item-source-selection.ts @@ -1,17 +1,17 @@ -import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context' -import { getMatchingJiraSites, parseJiraIssueUrl } from '../../../../shared/jira/jira-issue-url' -import type { JiraSite } from '../../../../shared/jira/jira-types' +import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import { getMatchingJiraSites, parseJiraIssueUrl } from '../../../../../shared/jira/jira-issue-url' +import type { JiraSite } from '../../../../../shared/jira/jira-types' import { buildGitHubWorkspaceSource, buildGitLabWorkspaceSource, buildLinearWorkspaceSource, buildJiraWorkspaceSource, type WorkspaceSourceLinkedItem -} from '../../../../shared/new-workspace/workspace-source' -import { parseGitHubIssueOrPRLink } from '../../../../shared/github/repositories/links' -import { parseGitLabIssueOrMRLink } from '../../../../shared/new-workspace/gitlab-links' +} from '../../../../../shared/new-workspace/workspace-source' +import { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' +import { parseGitLabIssueOrMRLink } from '../../../../../shared/new-workspace/gitlab-links' import { bindJiraIssueSourceContext } from './use-jira-url-source' -import type { RowEntry } from './smart-workspace-name-field-model' +import type { RowEntry } from '../name-field/smart-workspace-name-field-model' export type WorkItemSourceContexts = Record< 'github' | 'gitlab' | 'linear' | 'jira', diff --git a/src/renderer/src/components/new-workspace/type-ahead-combobox-styles.ts b/src/renderer/src/components/new-workspace/type-ahead/type-ahead-combobox-styles.ts similarity index 100% rename from src/renderer/src/components/new-workspace/type-ahead-combobox-styles.ts rename to src/renderer/src/components/new-workspace/type-ahead/type-ahead-combobox-styles.ts diff --git a/src/renderer/src/components/new-workspace/use-type-ahead-combobox.ts b/src/renderer/src/components/new-workspace/type-ahead/use-type-ahead-combobox.ts similarity index 100% rename from src/renderer/src/components/new-workspace/use-type-ahead-combobox.ts rename to src/renderer/src/components/new-workspace/type-ahead/use-type-ahead-combobox.ts diff --git a/src/renderer/src/components/new-workspace/use-wheel-scrollable.ts b/src/renderer/src/components/new-workspace/type-ahead/use-wheel-scrollable.ts similarity index 100% rename from src/renderer/src/components/new-workspace/use-wheel-scrollable.ts rename to src/renderer/src/components/new-workspace/type-ahead/use-wheel-scrollable.ts diff --git a/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.fixture.tsx b/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.fixture.tsx index 75f98c2decc..95ec5c47508 100644 --- a/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.fixture.tsx +++ b/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.fixture.tsx @@ -4,7 +4,7 @@ import { getDefaultSettings } from '../../../../../../shared/constants' import type { PublicKnownRuntimeEnvironment } from '../../../../../../shared/remote-runtime/host-catalog/runtime-environments' import type { NewWorkspaceProjectOption } from '@/lib/workspace/creation/new-workspace-project-options' import type { ProjectHostSetupOption } from '@/lib/workspace/creation/project-host-setup-options' -import { NewWorkspaceComposerProjectSection } from '@/components/new-workspace/NewWorkspaceComposerProjectSection' +import { NewWorkspaceComposerProjectSection } from '@/components/new-workspace/composer/NewWorkspaceComposerProjectSection' import { SidebarHeaderActions } from '../../sidebar-header-actions' const ubuntuRuntime: PublicKnownRuntimeEnvironment = { diff --git a/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.test.tsx b/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.test.tsx index 447ae4083c3..5462d37a07b 100644 --- a/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.test.tsx +++ b/src/renderer/src/components/sidebar/project-admission/menu/AddProjectMenu.test.tsx @@ -12,11 +12,11 @@ import { getProjectHostSetupSelectionKey, type ProjectHostSetupOption } from '@/lib/workspace/creation/project-host-setup-options' -import { NewWorkspaceComposerProjectSection } from '@/components/new-workspace/NewWorkspaceComposerProjectSection' +import { NewWorkspaceComposerProjectSection } from '@/components/new-workspace/composer/NewWorkspaceComposerProjectSection' import { SidebarHeaderActions } from '../../sidebar-header-actions' import type { AddRepoDialogEntryRequest } from '../dialog/add-repo-dialog-types' -vi.mock('@/components/new-workspace/use-recent-project-ids', () => ({ +vi.mock('@/components/new-workspace/project-selection/use-recent-project-ids', () => ({ useRecentProjectIds: () => [] })) vi.mock('@/components/sidebar/host-admission/ssh/AddRemoteHostDialog', () => ({ diff --git a/src/renderer/src/components/sidebar/workspace-card/review/WorkspaceAttachmentSearch.tsx b/src/renderer/src/components/sidebar/workspace-card/review/WorkspaceAttachmentSearch.tsx index 233e5038831..c490573bfda 100644 --- a/src/renderer/src/components/sidebar/workspace-card/review/WorkspaceAttachmentSearch.tsx +++ b/src/renderer/src/components/sidebar/workspace-card/review/WorkspaceAttachmentSearch.tsx @@ -12,11 +12,11 @@ import { import { useTranslate } from '@/i18n/use-translate' import { isScreenSubmitShortcut } from '@/lib/keyboard/screen-submit-shortcut' import type { WorkspaceAttachment } from '../../../../../../shared/worktree/types' -import type { RowEntry } from '../../../new-workspace/smart-workspace-name-field-model' +import type { RowEntry } from '../../../new-workspace/name-field/smart-workspace-name-field-model' import type { SmartNameMode } from '../../../../../../shared/new-workspace/smart-workspace-source-results' -import { useWorkItemSourceSearch } from '../../../new-workspace/use-work-item-source-search' -import { SmartWorkspaceSourceTabs } from '../../../new-workspace/SmartWorkspaceSourceTabs' -import { SmartWorkspaceSourceResultList } from '../../../new-workspace/SmartWorkspaceSourceResultList' +import { useWorkItemSourceSearch } from '../../../new-workspace/source-search/use-work-item-source-search' +import { SmartWorkspaceSourceTabs } from '../../../new-workspace/source-search/SmartWorkspaceSourceTabs' +import { SmartWorkspaceSourceResultList } from '../../../new-workspace/source-search/SmartWorkspaceSourceResultList' import { getWorkspaceAttachmentKey } from '../../../../../../shared/workspace-attachments/workspace-attachment-normalization' import { ATTACHMENT_INPUT_KINDS, diff --git a/src/renderer/src/components/sidebar/workspace-card/review/WorkspaceAttachmentSourceStatus.tsx b/src/renderer/src/components/sidebar/workspace-card/review/WorkspaceAttachmentSourceStatus.tsx index c69720e6f95..2e76ce0f280 100644 --- a/src/renderer/src/components/sidebar/workspace-card/review/WorkspaceAttachmentSourceStatus.tsx +++ b/src/renderer/src/components/sidebar/workspace-card/review/WorkspaceAttachmentSourceStatus.tsx @@ -1,8 +1,8 @@ import type React from 'react' import { Button } from '@/components/ui/button' import { useTranslate } from '@/i18n/use-translate' -import { getJiraSourceStatusMessage } from '../../../new-workspace/jira-source-status-message' -import type { WorkItemSourceSearch } from '../../../new-workspace/use-work-item-source-search' +import { getJiraSourceStatusMessage } from '../../../new-workspace/source-search/jira-source-status-message' +import type { WorkItemSourceSearch } from '../../../new-workspace/source-search/use-work-item-source-search' export function WorkspaceAttachmentSourceStatus({ controller, diff --git a/src/renderer/src/components/sidebar/workspace-creation/folder/folder-workspace-source-selection.ts b/src/renderer/src/components/sidebar/workspace-creation/folder/folder-workspace-source-selection.ts index c694d72f11f..46fd00eae1a 100644 --- a/src/renderer/src/components/sidebar/workspace-creation/folder/folder-workspace-source-selection.ts +++ b/src/renderer/src/components/sidebar/workspace-creation/folder/folder-workspace-source-selection.ts @@ -10,7 +10,7 @@ import '../../../../../../shared/project-groups' import '../../../../../../shared/repo-kind' import type { FolderWorkspace } from '../../../../../../shared/folder-workspaces/folder-workspace-types' -import type { SmartWorkspaceNameSelection } from '@/components/new-workspace/smart-workspace-name-field-model' +import type { SmartWorkspaceNameSelection } from '@/components/new-workspace/name-field/smart-workspace-name-field-model' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' import './folder-workspace-source-repos' diff --git a/src/renderer/src/hooks/composer-state/composer-source-model.ts b/src/renderer/src/hooks/composer-state/composer-source-model.ts index 8ccc0f8b7a6..cd6d1339fcc 100644 --- a/src/renderer/src/hooks/composer-state/composer-source-model.ts +++ b/src/renderer/src/hooks/composer-state/composer-source-model.ts @@ -6,7 +6,7 @@ import type { TaskSourceContext } from '../../../../shared/work-items/task-sourc import type { LinearIssue } from '../../../../shared/linear/issue-types' import type { SparsePreset } from '../../../../shared/worktree/create-types' import type { PendingSmartGitHubSubmitResolution } from './source-selection-decisions' -import type { SmartWorkspaceNameSelection } from '@/components/new-workspace/smart-workspace-name-field-model' +import type { SmartWorkspaceNameSelection } from '@/components/new-workspace/name-field/smart-workspace-name-field-model' export type ComposerSourceModel = { addComposerAttachments: (paths: string[]) => void applyLinkedGitLabWorkItem: (item: GitLabWorkItem) => void diff --git a/src/renderer/src/hooks/composer-state/issue-source-actions.ts b/src/renderer/src/hooks/composer-state/issue-source-actions.ts index 2af025914fd..c3264f93677 100644 --- a/src/renderer/src/hooks/composer-state/issue-source-actions.ts +++ b/src/renderer/src/hooks/composer-state/issue-source-actions.ts @@ -54,7 +54,7 @@ import { import { getJiraIssueKeyCasingSeedOptions } from '@/lib/work-items/jira/jira-issue-key-casing' import '@/lib/workspace/creation/new-workspace' import type { LinkedWorkItemSummary } from '@/lib/workspace/creation/new-workspace' -import type { SmartWorkspaceNameSelection } from '@/components/new-workspace/smart-workspace-name-field-model' +import type { SmartWorkspaceNameSelection } from '@/components/new-workspace/name-field/smart-workspace-name-field-model' import type { TaskSourceContext } from '../../../../shared/work-items/task-source-context' export function useIssueSourceActions(input: IssueSourceActionsInput) { diff --git a/src/renderer/src/lib/workspace/creation/work-item/launch-work-item-direct-setup.test.tsx b/src/renderer/src/lib/workspace/creation/work-item/launch-work-item-direct-setup.test.tsx index 5ecc1b4bf8f..6c373140871 100644 --- a/src/renderer/src/lib/workspace/creation/work-item/launch-work-item-direct-setup.test.tsx +++ b/src/renderer/src/lib/workspace/creation/work-item/launch-work-item-direct-setup.test.tsx @@ -12,7 +12,7 @@ const { launchWorkItemDirect } = await import('./launch-work-item-direct') const { __resetTrustPromptChainForTests } = await import('../../../../runtime/repo-hooks/ensure-hooks-confirmed') const { DirectWorkItemSetupDialog } = - await import('@/components/new-workspace/DirectWorkItemSetupDialog') + await import('@/components/new-workspace/setup/DirectWorkItemSetupDialog') const hooks = { scripts: { setup: 'echo prepared' } } const args = { diff --git a/src/renderer/src/components/new-workspace/smart-workspace-source-results.test.ts b/src/shared/new-workspace/smart-workspace-source-results.test.ts similarity index 96% rename from src/renderer/src/components/new-workspace/smart-workspace-source-results.test.ts rename to src/shared/new-workspace/smart-workspace-source-results.test.ts index aa29dc0bb3c..e4505f69699 100644 --- a/src/renderer/src/components/new-workspace/smart-workspace-source-results.test.ts +++ b/src/shared/new-workspace/smart-workspace-source-results.test.ts @@ -3,12 +3,12 @@ import { buildSmartWorkspaceSourceRows, getVisibleBranchResults, shouldHoldSourceResultsForQuery -} from '../../../../shared/new-workspace/smart-workspace-source-results' +} from './smart-workspace-source-results' import { isSmartWorkspaceLinearIssueIntentMatch, parseBoundedSmartWorkspaceLinearIssueInput, prioritizeSmartWorkspaceLinearIssueResults -} from '../../../../shared/new-workspace/smart-workspace-linear-intent' +} from './smart-workspace-linear-intent' describe('Branch source results', () => { it('hides held branch results when the live query diverges from the settled query', () => { diff --git a/tests/e2e/smart-workspace-linear-read-test-fixtures.tsx b/tests/e2e/smart-workspace-linear-read-test-fixtures.tsx index 210a20e7aef..c8c28dc8d50 100644 --- a/tests/e2e/smart-workspace-linear-read-test-fixtures.tsx +++ b/tests/e2e/smart-workspace-linear-read-test-fixtures.tsx @@ -12,8 +12,8 @@ import { referenceLinearIssue, referenceRepo } from '../../src/renderer/src/components/sidebar/workspace-card/review/workspace-reference-fixtures.test-support' -import { SmartWorkspaceSourceResultList } from '../../src/renderer/src/components/new-workspace/SmartWorkspaceSourceResultList' -import { useWorkItemSourceSearch } from '../../src/renderer/src/components/new-workspace/use-work-item-source-search' +import { SmartWorkspaceSourceResultList } from '../../src/renderer/src/components/new-workspace/source-search/SmartWorkspaceSourceResultList' +import { useWorkItemSourceSearch } from '../../src/renderer/src/components/new-workspace/source-search/use-work-item-source-search' import type * as MainLinearClient from '../../src/main/linear/accounts/linear-account-client' import type * as RuntimeLinearClient from '@/runtime/linear/runtime-linear-client' import type * as RuntimeLinearIssueMutations from '@/runtime/linear/runtime-linear-issue-client' @@ -216,12 +216,18 @@ vi.mock('@/runtime/hosts/preflight/local-preflight-context-key', () => ({ localPreflightContextKey: () => 'test' })) vi.mock('@/i18n/i18n', () => ({ translate: (_key: string, fallback: string) => fallback })) -vi.mock('../../src/renderer/src/components/new-workspace/use-jira-source-connection', () => ({ - useJiraSourceConnection: () => ({ status: null, loaded: false }) -})) -vi.mock('../../src/renderer/src/components/new-workspace/use-jira-url-source', () => ({ - useJiraUrlSource: () => ({ intent: false }) -})) +vi.mock( + '../../src/renderer/src/components/new-workspace/source-search/use-jira-source-connection', + () => ({ + useJiraSourceConnection: () => ({ status: null, loaded: false }) + }) +) +vi.mock( + '../../src/renderer/src/components/new-workspace/source-search/use-jira-url-source', + () => ({ + useJiraUrlSource: () => ({ intent: false }) + }) +) vi.mock('../../src/renderer/src/components/use-jira-project-inventory', () => ({ useJiraProjectInventory: () => ({ projects: [], connected: true, error: null, loading: false }) })) From 4a81270d6f12deca4903795c5cfe62ed4069b338 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 00:47:44 -0800 Subject: [PATCH 052/178] test: remove duplicate language wrapper and orphan host teardown (#41909) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 1 - ...uctured-agent-session-host-test-abandon.ts | 16 ----- .../plugin-language-pack-registry.test.ts | 60 ------------------- 3 files changed, 77 deletions(-) delete mode 100644 src/main/native-chat/agent-session-wire/host/testing/structured-agent-session-host-test-abandon.ts delete mode 100644 src/main/plugins/contributions/plugin-language-pack-registry.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 8df7deddb60..def8b154703 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -2642,7 +2642,6 @@ "src/main/plugins/catalog/plugin-update-review-notices.test.ts": 175, "src/main/plugins/contributions/plugin-content-pack-registry.test.ts": 589, "src/main/plugins/contributions/plugin-icon-theme-registry.test.ts": 268, - "src/main/plugins/contributions/plugin-language-pack-registry.test.ts": 587, "src/main/plugins/contributions/plugin-syntax-registry.test.ts": 221, "src/main/plugins/contributions/plugin-theme-registries.test.ts": 250, "src/main/plugins/hooks/plugin-agent-hook-sites.test.ts": 279, diff --git a/src/main/native-chat/agent-session-wire/host/testing/structured-agent-session-host-test-abandon.ts b/src/main/native-chat/agent-session-wire/host/testing/structured-agent-session-host-test-abandon.ts deleted file mode 100644 index b26fbff2522..00000000000 --- a/src/main/native-chat/agent-session-wire/host/testing/structured-agent-session-host-test-abandon.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Simulate crash without releasing ownership. Stop delivery/renewals and drain in-flight writes -// before closing test databases so pending producers cannot commit after cleanup. - -import type { StructuredAgentSessionHost } from '../structured-agent-session-host' - -export async function abandonStructuredAgentSessionHost( - host: StructuredAgentSessionHost -): Promise { - // Stop admission before draining to prevent new delivery steps. - host['conversationDelivery'].dispose() - host['lifetime'].dispose() - await host['runtimeState'].stopLeaseRenewal() - await host['tasks'].drainAttaches() - await Promise.all([...host['sessions'].values()].map((session) => session.journal.close())) - host['sessions'].clear() -} diff --git a/src/main/plugins/contributions/plugin-language-pack-registry.test.ts b/src/main/plugins/contributions/plugin-language-pack-registry.test.ts deleted file mode 100644 index 0449989ed40..00000000000 --- a/src/main/plugins/contributions/plugin-language-pack-registry.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, describe, expect, it } from 'vitest' -import { fingerprintPluginConsent } from '../../../shared/plugins/approval/plugin-consent-fingerprint' -import { pluginManifestSchema } from '../../../shared/plugins/manifest/plugin-manifest' -import { PluginContentVerifier } from '../plugin-content-integrity' -import type { ValidDiscoveredPlugin } from '../plugin-discovery' -import { PluginLanguagePackRegistry } from './plugin-language-pack-registry' - -const roots: string[] = [] - -async function pluginWithCatalog(catalog: unknown): Promise { - const rootDir = await mkdtemp(join(tmpdir(), 'orca-plugin-language-registry-')) - roots.push(rootDir) - await mkdir(join(rootDir, 'locales')) - await writeFile(join(rootDir, 'locales', 'pt-BR.json'), JSON.stringify(catalog)) - const manifest = pluginManifestSchema.parse({ - manifestVersion: 1, - id: 'portuguese', - publisher: 'samples', - name: 'Portuguese', - version: '1.0.0', - engines: { orca: '>=1.0.0' }, - pluginApi: 1, - contributes: { - languagePacks: [{ locale: 'pt-BR', path: 'locales/pt-BR.json' }] - }, - capabilities: [] - }) - return { - pluginKey: 'samples.portuguese', - rootDir, - manifest, - consentFingerprint: fingerprintPluginConsent(manifest), - contentHash: null, - isDev: true - } -} - -afterEach(async () => { - await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true }))) -}) - -describe('PluginLanguagePackRegistry', () => { - it('loads a pack that covers protected security copy, leaving that copy in English', async () => { - const plugin = await pluginWithCatalog({ - common: { save: 'Sávé' }, - auto: { components: { settings: { PluginConsentDialog: { disclaimer: 'Safe' } } } } - }) - const registry = new PluginLanguagePackRegistry(new PluginContentVerifier()) - - await registry.reconcile([plugin], () => true) - expect(registry.error(plugin.pluginKey)).toBeNull() - expect(registry.list()[0]?.catalog).toEqual({ - common: { save: 'Sávé' }, - auto: { components: { settings: {} } } - }) - }) -}) From 28e0a0243c364e0292d59a5076a29b4d7ec68eda Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 13 Nov 2026 00:58:30 -0800 Subject: [PATCH 053/178] perf(sidebar): reuse tab ownership across activity projections (#41060) * perf(sidebar): reuse tab ownership index for activity summaries * perf(sidebar): share tab ownership across activity projections --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> --- .../worktree-agent-activity-summary.ts | 9 ++----- .../activity/worktree-agent-row-selectors.ts | 19 ++------------- .../activity/worktree-activity-state.ts | 8 ++----- .../lib/workspace/tabs/tab-worktree-index.ts | 24 +++++++++++++++++++ 4 files changed, 30 insertions(+), 30 deletions(-) create mode 100644 src/renderer/src/lib/workspace/tabs/tab-worktree-index.ts diff --git a/src/renderer/src/components/sidebar/workspace-card/activity/worktree-agent-activity-summary.ts b/src/renderer/src/components/sidebar/workspace-card/activity/worktree-agent-activity-summary.ts index 65dd3bcf182..f945ab011d1 100644 --- a/src/renderer/src/components/sidebar/workspace-card/activity/worktree-agent-activity-summary.ts +++ b/src/renderer/src/components/sidebar/workspace-card/activity/worktree-agent-activity-summary.ts @@ -18,6 +18,7 @@ import { import type { AgentStatusOrchestrationContext } from '../../../../../../shared/agent-status/contracts/agent-status-types' import { agentVerdictDisplayMark } from '../../../../../../shared/agent-status/lifecycle/agent-main-agent-verdict' import { applyAgentPaneActivityFlags } from '../../../../../../shared/worktree-status-rollup' +import { getTabIdToWorktreeId } from '@/lib/workspace/tabs/tab-worktree-index' export type WorktreeAgentActivitySummary = { hasPermission: boolean @@ -105,13 +106,7 @@ function getWorktreeAgentActivitySummaries( return agentActivityCache.summaries } - // Index tabs once per snapshot so per-card summaries cost O(worktrees + agents). - const tabIdToWorktreeId = new Map() - for (const [worktreeId, tabs] of Object.entries(state.tabsByWorktree)) { - for (const tab of tabs) { - tabIdToWorktreeId.set(tab.id, worktreeId) - } - } + const tabIdToWorktreeId = getTabIdToWorktreeId(state.tabsByWorktree) const summaries = new Map() const summaryForWorktree = (worktreeId: string): WorktreeAgentActivitySummary => { diff --git a/src/renderer/src/components/sidebar/workspace-card/activity/worktree-agent-row-selectors.ts b/src/renderer/src/components/sidebar/workspace-card/activity/worktree-agent-row-selectors.ts index 12b6f915d6c..958474fd018 100644 --- a/src/renderer/src/components/sidebar/workspace-card/activity/worktree-agent-row-selectors.ts +++ b/src/renderer/src/components/sidebar/workspace-card/activity/worktree-agent-row-selectors.ts @@ -1,6 +1,7 @@ import { getStructuredAgentSessionTabsByPaneKey } from '@/components/native-chat/session/status/structured-agent-session-tabs' import type { RetainedAgentEntry } from '@/store/slices/agent-status/agent-status-contract' import type { AppState } from '@/store/types' +import { getTabIdToWorktreeId } from '@/lib/workspace/tabs/tab-worktree-index' import type { AgentStatusEntry } from '../../../../../../shared/agent-status/contracts/agent-status-types' import type { MigrationUnsupportedPtyEntry } from '../../../../../../shared/agent-status/contracts/agent-status-ipc-payload' import { parsePaneKey } from '../../../../../../shared/workspace-layout/stable-pane-id' @@ -55,7 +56,6 @@ type RetainedEntriesByWorktreeCache = { entriesByWorktree: Map } -let tabWorktreeIndexCache: TabWorktreeIndexCache | null = null let liveTabWorktreeIndexCache: LiveTabWorktreeIndexCache | null = null let liveEntriesByWorktreeCache: LiveEntriesByWorktreeCache | null = null let migrationUnsupportedByWorktreeCache: MigrationUnsupportedByWorktreeCache | null = null @@ -74,22 +74,7 @@ export function reuseArrayIfEqual(previous: T[] | undefined, next: T[]): T[] return previous } -// Settings reuses this tab index to avoid rescanning every bucket on each store write. -export function getTabIdToWorktreeId( - tabsByWorktree: WorktreeAgentRowsState['tabsByWorktree'] -): Map { - if (tabWorktreeIndexCache?.tabsByWorktree === tabsByWorktree) { - return tabWorktreeIndexCache.tabIdToWorktreeId - } - const tabIdToWorktreeId = new Map() - for (const [worktreeId, tabs] of Object.entries(tabsByWorktree)) { - for (const tab of tabs) { - tabIdToWorktreeId.set(tab.id, worktreeId) - } - } - tabWorktreeIndexCache = { tabsByWorktree, tabIdToWorktreeId } - return tabIdToWorktreeId -} +export { getTabIdToWorktreeId } function getLiveTabIdToWorktreeId( tabsByWorktree: WorktreeAgentRowsState['tabsByWorktree'], diff --git a/src/renderer/src/lib/workspace/activity/worktree-activity-state.ts b/src/renderer/src/lib/workspace/activity/worktree-activity-state.ts index b7f4d8d816f..a2ff6739af1 100644 --- a/src/renderer/src/lib/workspace/activity/worktree-activity-state.ts +++ b/src/renderer/src/lib/workspace/activity/worktree-activity-state.ts @@ -1,4 +1,5 @@ import type { AppState } from '@/store/types' +import { getTabIdToWorktreeId } from '../tabs/tab-worktree-index' import { createAgentStatusEpochSelector } from '../../agent-status/evidence/agent-status-epoch-selector' import { tabHasLivePty } from '../../../../../shared/tab-has-live-pty' import type { TerminalTab } from '../../../../../shared/workspace-layout/terminal-tab-types' @@ -50,12 +51,7 @@ export function getLiveAgentStatusByWorktreeId( if (entries.length === 0) { return new Map() } - const worktreeIdByTabId = new Map() - for (const [worktreeId, tabs] of Object.entries(tabsByWorktree ?? {})) { - for (const tab of tabs) { - worktreeIdByTabId.set(tab.id, worktreeId) - } - } + const worktreeIdByTabId = getTabIdToWorktreeId(tabsByWorktree) const result = new Map() for (const entry of entries) { const worktreeId = resolveAgentStatusWorktreeId(entry, worktreeIdByTabId) diff --git a/src/renderer/src/lib/workspace/tabs/tab-worktree-index.ts b/src/renderer/src/lib/workspace/tabs/tab-worktree-index.ts new file mode 100644 index 00000000000..57ba419cde7 --- /dev/null +++ b/src/renderer/src/lib/workspace/tabs/tab-worktree-index.ts @@ -0,0 +1,24 @@ +export type TabOwnershipRows = Record + +// Keep full and projected catalogs cached without retaining replaced snapshots. +const tabWorktreeIndexes = new WeakMap>() + +export function getTabIdToWorktreeId( + tabsByWorktree: TabOwnershipRows | null | undefined +): Map { + if (!tabsByWorktree) { + return new Map() + } + const cached = tabWorktreeIndexes.get(tabsByWorktree) + if (cached) { + return cached + } + const index = new Map() + for (const [worktreeId, tabs] of Object.entries(tabsByWorktree)) { + for (const tab of tabs) { + index.set(tab.id, worktreeId) + } + } + tabWorktreeIndexes.set(tabsByWorktree, index) + return index +} From 2850c00006847196dc3625587265e00d3b84d117 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 01:17:39 -0800 Subject: [PATCH 054/178] test: remove low-value smoke, wrapper, and copy assertions (#41925) * test: remove prebuilt declarative demo smoke suite * test: remove static Box summary copy assertion * test: remove redundant wrapper cases and copied assertions --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 1 - .../native-chat-link-peek-context.test.tsx | 5 -- .../listing/detected-worktree-meta.test.ts | 62 --------------- .../catalog/plugin-catalog-release.test.ts | 14 ---- .../plugin-declarative-catalog-rich.test.ts | 3 - .../plugin-declarative-example.test.ts | 79 ------------------- .../plugin-declarative-tree.test.ts | 10 +-- .../testing/orca-testing-box.test.ts | 5 -- .../testing/orca-testing-hooks.test.ts | 24 ------ .../testing/testing-editor-host.test.ts | 17 ---- 10 files changed, 2 insertions(+), 218 deletions(-) delete mode 100644 src/shared/plugins/declarative/plugin-declarative-example.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index def8b154703..6e57540494d 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -6518,7 +6518,6 @@ "src/shared/plugins/contributions/plugin-content-pack-contributions.test.ts": 227, "src/shared/plugins/contributions/plugin-custom-editor-contribution.test.ts": 238, "src/shared/plugins/declarative/plugin-declarative-catalog-rich.test.ts": 147, - "src/shared/plugins/declarative/plugin-declarative-example.test.ts": 198, "src/shared/plugins/declarative/plugin-declarative-tree.test.ts": 168, "src/shared/plugins/declarative/runtime/declarative-act-snapshot.test.ts": 64, "src/shared/plugins/declarative/runtime/declarative-error-location.test.ts": 63, diff --git a/src/renderer/src/components/native-chat/links/previews/native-chat-link-peek-context.test.tsx b/src/renderer/src/components/native-chat/links/previews/native-chat-link-peek-context.test.tsx index b5972eab6a1..62d314a06ad 100644 --- a/src/renderer/src/components/native-chat/links/previews/native-chat-link-peek-context.test.tsx +++ b/src/renderer/src/components/native-chat/links/previews/native-chat-link-peek-context.test.tsx @@ -32,11 +32,6 @@ it.each([ owner: { executionHostId: 'runtime:env' as const, connectionId: 'T' }, runtimeEnvironmentId: 'env', expectedHost: 'nested:env/T' - }, - { - owner: { executionHostId: 'runtime:env' as const, connectionId: 'U' }, - runtimeEnvironmentId: 'env', - expectedHost: 'nested:env/U' } ])( 'routes a folder chat preview through $expectedHost', diff --git a/src/renderer/src/store/slices/worktrees/listing/detected-worktree-meta.test.ts b/src/renderer/src/store/slices/worktrees/listing/detected-worktree-meta.test.ts index 2c85e16d7b3..cf3ac012d8e 100644 --- a/src/renderer/src/store/slices/worktrees/listing/detected-worktree-meta.test.ts +++ b/src/renderer/src/store/slices/worktrees/listing/detected-worktree-meta.test.ts @@ -1,76 +1,14 @@ import { describe, expect, it } from 'vitest' -import { hostWorktreeTarget } from '../../../../../../shared/worktree/host-qualified-identity' import { findKnownWorktreeById } from './detected-worktree-meta' import { makeFolderWorkspace, makeProjectGroup } from '../../../../components/sidebar/workspace-creation/folder/folder-workspace-composer-submit-fixtures' -import { findFolderWorkspaceOwner } from '@/lib/workspace/ownership/folder-workspace-runtime-owner' -import { selectWorkspaceByHostTarget } from '../../../workspaces/selection/workspace-host-target' -import { resolveWorkspaceDirectory } from '@/lib/workspace/paths/workspace-directory' import { resolveQualifiedFolderWorkspaceResult } from '../../../../../../shared/folder-workspaces/folder-workspace-execution-host' import type { Repo } from '../../../../../../shared/repo-types' -import { findIndexedFolderWorkspaceOwner } from '@/lib/workspace/ownership/worktree-runtime-owner-index' -import { folderWorkspaceToWorktree } from '../../../../../../shared/folder-workspaces/folder-workspace-worktree' -import { resolveNotificationTabOwner } from '@/attention/notification-subject-owner' -import { getWorktreeDeleteStateKey } from '../teardown/worktree-delete-state' describe('inherited folder consumer identity', () => { - it.each([false, true])( - 'keeps physical twins qualified through native owner, store and directory, reversed=%s', - (reverse) => { - const inherited = makeFolderWorkspace({ - id: 'same', - projectGroupId: 'inherited', - folderPath: '/remote' - }) - const local = makeFolderWorkspace({ - id: 'same', - executionHostId: 'local', - folderPath: '/local' - }) - const folderWorkspaces = reverse ? [inherited, local] : [local, inherited] - const state = { - folderWorkspaces, - projectGroups: [{ id: 'inherited', executionHostId: 'ssh:box' as const }], - repos: [], - worktreesByRepo: {}, - detectedWorktreesByRepo: {}, - floatingWorkspacePath: null - } - const raw = findIndexedFolderWorkspaceOwner(folderWorkspaces, 'same', 'ssh:box') - const converted = folderWorkspaceToWorktree(inherited) - const notification = resolveNotificationTabOwner(state, { - worktreeId: 'folder:same', - executionHostId: 'ssh:box' - }) - expect(resolveQualifiedFolderWorkspaceResult(state, 'same', 'ssh:box').kind).toBe('selected') - expect(findFolderWorkspaceOwner(state, 'same', 'ssh:box')).toBe(inherited) - const projected = findKnownWorktreeById(state, 'folder:same', 'ssh:box') - expect(projected).toMatchObject({ path: '/remote', hostId: 'ssh:box' }) - expect( - selectWorkspaceByHostTarget( - state, - hostWorktreeTarget({ id: 'folder:same', hostId: 'ssh:box' }) - ) - ).toBe(projected) - expect(resolveWorkspaceDirectory(state, 'folder:same', 'ssh:box')).toBe('/remote') - expect(findKnownWorktreeById(state, 'folder:same')).toBeUndefined() - expect(findIndexedFolderWorkspaceOwner(folderWorkspaces, 'same', 'ssh:box')).toBe(raw) - expect(folderWorkspaceToWorktree(inherited)).toEqual(converted) - expect( - resolveNotificationTabOwner(state, { - worktreeId: 'folder:same', - executionHostId: 'ssh:box' - }) - ).toEqual(notification) - expect(inherited.executionHostId).toBeUndefined() - expect(getWorktreeDeleteStateKey(state, 'folder:same', 'ssh:box')).not.toBe( - getWorktreeDeleteStateKey(state, 'folder:same', 'local') - ) - } - ) it.each(['group', 'repo'])( 'invalidates only a changed derived host projection from its %s catalog', (source) => { diff --git a/src/shared/plugins/catalog/plugin-catalog-release.test.ts b/src/shared/plugins/catalog/plugin-catalog-release.test.ts index e8929abdd25..07e8adb4085 100644 --- a/src/shared/plugins/catalog/plugin-catalog-release.test.ts +++ b/src/shared/plugins/catalog/plugin-catalog-release.test.ts @@ -5,7 +5,6 @@ import { pluginCatalogReleaseSchema, type PluginCatalogRelease } from './plugin-catalog-release' -import { pluginContributionSummary } from '../contributions/plugin-contribution-summary' import { parsePluginManifest, type PluginManifest } from '../manifest/plugin-manifest' const COMMIT = 'c'.repeat(40) @@ -111,13 +110,6 @@ describe('catalog releases', () => { expect(check(release({ types: ['syntax'], fileTypes: ['typ', 'gleam'] }), syntaxManifest)).toBe( 'file types do not match the catalog release' ) - // A newer catalog's odd entries are dropped instead of hiding the whole release. - expect( - pluginCatalogReleaseSchema.parse({ - ...release(), - fileTypes: ['.typ', 'typ', 7, 'typ', ...Array.from({ length: 40 }, (_, i) => `t${i}`)] - }).fileTypes - ).toEqual(['typ', ...Array.from({ length: 31 }, (_, i) => `t${i}`)]) expect(check(release({ types: ['syntax'], name: 'Other' }), syntaxManifest)).toBe( 'plugin name does not match the catalog release' ) @@ -132,12 +124,6 @@ describe('catalog release summaries', () => { }) const base = { manifest: themed, resolvedCommit: COMMIT, contentHash: HASH } const listed = release({ types: ['app-theme'] }) - expect( - pluginCatalogReleaseError({ - ...base, - release: { ...listed, contributes: pluginContributionSummary(themed), capabilities: [] } - }) - ).toBeNull() expect( pluginCatalogReleaseError({ ...base, diff --git a/src/shared/plugins/declarative/plugin-declarative-catalog-rich.test.ts b/src/shared/plugins/declarative/plugin-declarative-catalog-rich.test.ts index 116c32b85f2..623bc4022df 100644 --- a/src/shared/plugins/declarative/plugin-declarative-catalog-rich.test.ts +++ b/src/shared/plugins/declarative/plugin-declarative-catalog-rich.test.ts @@ -35,9 +35,6 @@ describe('catalog 2 components', () => { expect(result.tree?.children.map((child) => typeof child !== 'string' && child.type)).toEqual( ['Unsupported', 'Unsupported', 'Text'] ) - expect(result.warnings).toEqual( - expect.arrayContaining(['Table: ignored invalid rows', 'Tree: ignored invalid items']) - ) } }) }) diff --git a/src/shared/plugins/declarative/plugin-declarative-example.test.ts b/src/shared/plugins/declarative/plugin-declarative-example.test.ts deleted file mode 100644 index b3c1808ebeb..00000000000 --- a/src/shared/plugins/declarative/plugin-declarative-example.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import path from 'node:path' -import { pathToFileURL } from 'node:url' -import { afterAll, expect, it } from 'vitest' -import { parsePluginDeclarativeTree } from './plugin-declarative-tree' - -const ROOT = path.resolve(import.meta.dirname, '..', '..', '..', '..') -const RUNTIME = path.join(ROOT, 'resources', 'plugins', 'declarative', 'orca-runtime.mjs') -const EXAMPLE = path.join(ROOT, 'examples', 'plugins', 'hello-components', 'dist', 'panel.js') -const scratch = mkdtempSync(path.join(tmpdir(), 'orca-declarative-')) -afterAll(() => rmSync(scratch, { recursive: true, force: true })) - -it('runs the prebuilt sample against the shipped guest runtime, like the guest import map would', async () => { - // Why copies: a fresh module instance per test, with `orca` resolved the way the guest import map does. - const runtimeCopy = path.join(scratch, 'orca-runtime.mjs') - writeFileSync(runtimeCopy, readFileSync(RUNTIME)) - const runtimeUrl = pathToFileURL(runtimeCopy).href - const panel = readFileSync(EXAMPLE, 'utf8').replace( - /from "orca(?:\/jsx-runtime)?"/g, - `from ${JSON.stringify(runtimeUrl)}` - ) - const panelCopy = path.join(scratch, 'panel.mjs') - writeFileSync(panelCopy, panel) - - const orca = await import(/* @vite-ignore */ runtimeUrl) - const posted: { event: string; payload: { revision: number; tree: unknown } }[] = [] - const runtime = orca.installOrcaDeclarativeRuntime({ - post: (event: string, payload: { revision: number; tree: unknown }) => - posted.push({ event, payload }), - bridge: null - }) - await runtime.boot(await import(/* @vite-ignore */ pathToFileURL(panelCopy).href)) - const { document } = runtime - - const first = posted.at(-1)!.payload - const parsed = parsePluginDeclarativeTree(first.tree) - expect(parsed).toMatchObject({ ok: true, warnings: [] }) - expect(JSON.stringify(first.tree)).toContain( - '{"type":"Badge","key":"#1","props":{"tone":"accent"},"children":["1"," open"]}' - ) - - const input = JSON.stringify(first.tree).match(/"Input".*?"onChange":\{"\$callback":"([^"]+)"/) - expect(input).not.toBeNull() - expect( - runtime.dispatchEvent({ - document, - revision: first.revision, - callback: input![1], - args: ['Ship it'], - edit: 1 - }) - ).toBe(true) - runtime.flush() - const second = posted.at(-1)!.payload - const submit = JSON.stringify(second.tree).match(/"onSubmit":\{"\$callback":"([^"]+)"/) - expect( - runtime.dispatchEvent({ - document, - revision: second.revision, - callback: submit![1], - args: ['Ship it'] - }) - ).toBe(true) - runtime.flush() - const renders = posted.filter((entry) => entry.event === 'ui.render') - expect(JSON.stringify(renders.at(-1)!.payload.tree)).toContain('Ship it') - expect(parsePluginDeclarativeTree(renders.at(-1)!.payload.tree).ok).toBe(true) - // The list is a persisted atom: the change goes to Orca for the next run. - expect(posted.filter((entry) => entry.event === 'ui.persist')).toEqual([ - { - event: 'ui.persist', - payload: expect.objectContaining({ - entries: [expect.objectContaining({ scope: 'plugin', key: 'todos', version: 1 })] - }) - } - ]) - runtime.dispose() -}) diff --git a/src/shared/plugins/declarative/plugin-declarative-tree.test.ts b/src/shared/plugins/declarative/plugin-declarative-tree.test.ts index 79c8c6f9d88..ad287ecfe48 100644 --- a/src/shared/plugins/declarative/plugin-declarative-tree.test.ts +++ b/src/shared/plugins/declarative/plugin-declarative-tree.test.ts @@ -8,7 +8,7 @@ const node = (type: string, props: Record = {}, children: unkno }) describe('parsePluginDeclarativeTree', () => { - it('drops unknown props and enum values with a warning instead of refusing', () => { + it('drops unknown props and enum values instead of refusing', () => { const result = parsePluginDeclarativeTree( node('Stack', { className: 'p-4', gap: 'huge' }, [ node('Text', { tone: '#ff0000' }, ['x']), @@ -22,12 +22,6 @@ describe('parsePluginDeclarativeTree', () => { { type: 'IconButton', props: { icon: 'skull' } } ] }) - expect(result.ok && result.warnings).toEqual([ - 'Stack: ignored unknown prop "className"', - 'Stack: ignored invalid gap', - 'Text: ignored invalid tone', - 'IconButton: unknown icon "skull"; showing a fallback glyph' - ]) }) it('draws unknown components, and known ones missing a required prop, as placeholders', () => { @@ -45,7 +39,7 @@ describe('parsePluginDeclarativeTree', () => { }) describe('ui.* payloads', () => { - it('counts text children toward the node cap and charges each one', () => { + it('counts text children toward the aggregate node cap', () => { const many = (count: number) => ({ type: 'Text', props: {}, diff --git a/src/shared/plugins/declarative/testing/orca-testing-box.test.ts b/src/shared/plugins/declarative/testing/orca-testing-box.test.ts index 54bcbfb115b..7bea965299b 100644 --- a/src/shared/plugins/declarative/testing/orca-testing-box.test.ts +++ b/src/shared/plugins/declarative/testing/orca-testing-box.test.ts @@ -45,9 +45,4 @@ describe('orca/testing reads Box', () => { expect(panel.errors).toEqual([]) expect(panel.warnings).toEqual([]) }) - - it('summarizes what sits in Boxes, and danger or warning Boxes themselves', async () => { - panel = await mount({ default: Status }) - expect(panel.summary()).toEqual(['Heading "Services"', 'Box danger "billing Down"']) - }) }) diff --git a/src/shared/plugins/declarative/testing/orca-testing-hooks.test.ts b/src/shared/plugins/declarative/testing/orca-testing-hooks.test.ts index dbe2b415f1a..4944076da36 100644 --- a/src/shared/plugins/declarative/testing/orca-testing-hooks.test.ts +++ b/src/shared/plugins/declarative/testing/orca-testing-hooks.test.ts @@ -36,28 +36,4 @@ describe('orca/testing hooks', () => { event: send }) }) - - it('fires editor.save.before and returns the text the worker wrote', async () => { - const formatter = { - default(orca: { - hooks: { - on(hook: string, handler: (e: unknown, next: (e: unknown) => unknown) => unknown): unknown - } - }) { - orca.hooks.on('editor.save.before', (event, next) => - next({ ...Object(event), text: `${String(Object(event).text).trim()}\n` }) - ) - } - } - const worker = await mountWorker(formatter, { - events: [], - commands: [], - hooks: ['editor.save.before'] - }) - const save = { workspaceId: 'wt-1', path: 'src/a.ts', languageId: 'typescript', text: ' x ' } - await expect(worker.hooks.fire('editor.save.before', save)).resolves.toEqual({ - kind: 'continue', - event: { ...save, text: 'x\n' } - }) - }) }) diff --git a/src/shared/plugins/declarative/testing/testing-editor-host.test.ts b/src/shared/plugins/declarative/testing/testing-editor-host.test.ts index c449e6367b7..dd2c3c73d3f 100644 --- a/src/shared/plugins/declarative/testing/testing-editor-host.test.ts +++ b/src/shared/plugins/declarative/testing/testing-editor-host.test.ts @@ -36,20 +36,3 @@ describe('mockEditorHost', () => { doc.dispose() }) }) - -describe('mockEditorHost saves as Orca does', () => { - it('blocks a save after an oversized edit until a later edit lands', async () => { - host = mockEditorHost({ text: 'a' }) - const { document } = host.orca - const send = (text: string): Promise => - document.applyEdit({ baseVersion: 1, content: { kind: 'text', text } }) - await session() - await expect(send('x'.repeat(64 * 1024 * 1024 + 1))).rejects.toMatchObject({ - code: 'too_large' - }) - await expect(host.flush()).rejects.toThrow(/too large/) - await send('fits') - await expect(host.flush()).resolves.toBeUndefined() - expect(host.text).toBe('fits') - }) -}) From 799f88ec66452db91bb1e2e419ef08f186920a50 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 13 Nov 2026 01:20:39 -0800 Subject: [PATCH 055/178] perf: window expanded sidebar workspace families (#41237) * perf: virtualize individual sidebar workspace descendants * chore: combine sidebar identity imports * fix: preserve sidebar interactions with individual rows * perf: derive sidebar fallback lookups only when needed * test: document narrow sidebar geometry fixtures * chore: keep sidebar lookup compatible with React compiler * Dismiss lineage tooltips when toggling child workspaces * fix: use measured rows for sidebar collapse and scroll * test: exercise descendant deletion scroll takeover * fix: keep sidebar scroll marks compiler compatible * fix: complete sidebar reveal and folder surface integration --- src/renderer/src/assets/main.css | 18 ++ .../use-worktree-context-menu-commands.ts | 4 - .../worktree-context-menu-policy.ts | 15 +- .../sleep/sleep-worktree-sidebar-position.ts | 17 +- .../activity/WorktreeCardAgents.tsx | 31 +- .../identity/WorktreeLineageToggle.test.tsx | 52 ++++ .../worktree-lineage-toggle-handler-cache.ts | 1 - .../worktree-list/drag/drop-commit-context.ts | 5 + .../worktree-list/drag/pointer-commit.ts | 6 +- .../drag/project-group-header-drag.ts | 9 +- .../worktree-list/drag/project-header-drag.ts | 9 +- .../worktree-list/drag/use-document-drop.ts | 6 +- .../drag/use-drop-commit-context.ts | 2 + .../worktree-list/drag/use-header-drag.ts | 25 +- .../drag/use-lineage-drop-commit.ts | 58 +++- .../drag/use-native-autoscroll.ts | 6 + .../worktree-list/drag/use-native-drag.ts | 14 +- .../drag/use-pointer-autoscroll.ts | 6 + .../worktree-list/drag/use-pointer-drag.ts | 6 +- .../sidebar/worktree-list/drag/use-session.ts | 26 +- .../worktree-drag-preview-offsets.test.ts | 21 ++ .../drag/worktree-drag-preview-offsets.ts | 10 +- .../drag/worktree-lineage-drag-drop.test.ts | 32 +- .../drag/worktree-lineage-drag-drop.ts | 27 +- .../drag/worktree-sidebar-drag-autoscroll.ts | 1 + .../drag/worktree-sidebar-drop-preview.ts | 42 +-- ....lineage-agent-expansion-coupling.test.tsx | 17 +- .../listing/lineage-row-projection.ts | 215 +++++++++++++ .../worktree-list/listing/render-row.ts | 11 +- .../worktree-list/listing/renderable-rows.ts | 57 +--- .../navigation/active-descendant-option.ts | 31 +- .../navigation/mounted-row-reveal.ts | 32 +- .../navigation/pending-reveal-inputs.ts | 30 ++ .../navigation/render-row-lookup.ts | 21 +- .../navigation/use-pending-reveal.ts | 66 ++-- .../navigation/worktree-sidebar-reveal.ts | 20 +- .../sidebar/worktree-list/rows/folder-row.tsx | 18 +- .../sidebar/worktree-list/rows/item-row.tsx | 97 +----- .../rows/virtual-row-dispatch.tsx | 166 +++++----- .../viewport/VirtualizedWorktreeViewport.tsx | 92 +++--- .../viewport/sidebar-smooth-reveal.test.tsx | 170 ++++++++++ .../viewport/sidebar-smooth-reveal.ts | 90 ++++++ .../viewport/use-keyboard-navigation.ts | 30 ++ .../viewport/use-row-geometry.test.tsx | 159 ++++++++++ .../viewport/use-row-geometry.ts | 205 ++++++++++++ .../use-row-measurement.scroll.test.tsx | 291 ++++++++++++++++++ .../viewport/use-row-measurement.ts | 31 +- .../viewport/use-row-removal-animation.ts | 124 -------- .../viewport/use-scroll-suppression.ts | 77 +---- .../viewport/use-scroll-to-top.ts | 6 + .../worktree-list/viewport/use-virtualizer.ts | 64 ++-- .../viewport/use-visible-review-refresh.ts | 4 +- .../viewport/virtual-row-context.ts | 9 + .../worktree-list/viewport/virtual-rows.ts | 49 +-- .../requestVirtualizedScrollAnchorRecord.ts | 29 +- .../src/hooks/useVirtualizedScrollAnchor.ts | 18 +- 56 files changed, 1821 insertions(+), 857 deletions(-) create mode 100644 src/renderer/src/components/sidebar/workspace-card/identity/WorktreeLineageToggle.test.tsx create mode 100644 src/renderer/src/components/sidebar/worktree-list/drag/worktree-drag-preview-offsets.test.ts create mode 100644 src/renderer/src/components/sidebar/worktree-list/listing/lineage-row-projection.ts create mode 100644 src/renderer/src/components/sidebar/worktree-list/viewport/sidebar-smooth-reveal.test.tsx create mode 100644 src/renderer/src/components/sidebar/worktree-list/viewport/sidebar-smooth-reveal.ts create mode 100644 src/renderer/src/components/sidebar/worktree-list/viewport/use-keyboard-navigation.ts create mode 100644 src/renderer/src/components/sidebar/worktree-list/viewport/use-row-geometry.test.tsx create mode 100644 src/renderer/src/components/sidebar/worktree-list/viewport/use-row-geometry.ts create mode 100644 src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.scroll.test.tsx delete mode 100644 src/renderer/src/components/sidebar/worktree-list/viewport/use-row-removal-animation.ts diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index 6100df6a3fb..4a0ccb646b0 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -1673,6 +1673,7 @@ html.native-shell .app-layout { /* Why: the brighter border carries the selected state; a translucent wash keeps card text legible instead of lifting the surface toward the foreground color. */ +[data-worktree-family-segment='primary'], [data-worktree-card-surface][data-worktree-card-active='primary'] { border-color: var( --worktree-sidebar-active-border, @@ -1685,6 +1686,7 @@ html.native-shell .app-layout { box-shadow: 0 1px 2px color-mix(in srgb, var(--worktree-sidebar-foreground) 4%, transparent); } +.dark [data-worktree-family-segment='primary'], .dark [data-worktree-card-surface][data-worktree-card-active='primary'] { border-color: var( --worktree-sidebar-active-border, @@ -1710,18 +1712,34 @@ html.native-shell .app-layout { } } +[data-worktree-family-segment='secondary'], [data-worktree-card-surface][data-worktree-card-active='secondary'] { border-color: color-mix(in srgb, var(--sidebar-ring) 25%, transparent); background: color-mix(in srgb, var(--sidebar-accent) 45%, transparent); box-shadow: 0 0 0 1px color-mix(in srgb, var(--sidebar-ring) 15%, transparent); } +.dark [data-worktree-family-segment='secondary'], .dark [data-worktree-card-surface][data-worktree-card-active='secondary'] { border-color: color-mix(in srgb, var(--sidebar-ring) 28%, transparent); background: color-mix(in srgb, var(--sidebar-accent) 34%, transparent); box-shadow: 0 0 0 1px color-mix(in srgb, var(--sidebar-ring) 18%, transparent); } +/* Family paint owns the surface in both themes, including secondary occurrences. */ +.worktree-sidebar-family-root [data-worktree-card-surface][data-worktree-card-active], +.dark .worktree-sidebar-family-root [data-worktree-card-surface][data-worktree-card-active] { + background: transparent; + border-color: transparent; + box-shadow: none; +} + +/* Continuous segments must not cast a horizontal shadow at every virtual boundary. */ +[data-worktree-family-segment], +.dark [data-worktree-family-segment] { + box-shadow: none; +} + /* #19624: mix text tokens toward sidebar in oklab for theme-stable perceptual dimming; preserve unread amber at full brightness. */ [data-worktree-sleeping-dim] { color: color-mix(in oklab, var(--worktree-sidebar-foreground) 52%, var(--worktree-sidebar)); diff --git a/src/renderer/src/components/sidebar/workspace-actions/context-menu/use-worktree-context-menu-commands.ts b/src/renderer/src/components/sidebar/workspace-actions/context-menu/use-worktree-context-menu-commands.ts index 82e0362fb2e..288728d5541 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/context-menu/use-worktree-context-menu-commands.ts +++ b/src/renderer/src/components/sidebar/workspace-actions/context-menu/use-worktree-context-menu-commands.ts @@ -15,7 +15,6 @@ import { activateAndRevealWorktree } from '@/lib/workspace/activation/worktree-a import { resolveWorktreeDisplayName } from '@/lib/workspace/display/worktree-default-display-name' import { formatClipboardFilePath } from '@/lib/clipboard/clipboard-file-path' import { getWorktreeRowLocalPathOpenOwner } from '@/lib/workspace/ownership/worktree-row-open-in-owner' -import { VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT } from '@/hooks/useVirtualizedScrollAnchor' import { canCreateChildWorktree, planWorkspaceStatusAssignment, @@ -206,9 +205,6 @@ export function useWorktreeContextMenuCommands(args: { }, [args.worktree, args.subtreeSleepableWorktrees, sleepWorktreesAfterMenuClose]) const handleDelete = useCallback(() => { const restoreSidebarPosition = preserveDeleteSiblingPosition(args.scopeRef.current) - args.scopeRef.current - ?.closest('[data-worktree-sidebar]') - ?.dispatchEvent(new Event(VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT)) const intent = createWorktreeContextMenuDeleteIntent({ worktree: args.worktree, batchDeleteWorktrees: args.batchDeleteWorktrees, diff --git a/src/renderer/src/components/sidebar/workspace-actions/context-menu/worktree-context-menu-policy.ts b/src/renderer/src/components/sidebar/workspace-actions/context-menu/worktree-context-menu-policy.ts index 17fef0348bc..30e4ee9b72d 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/context-menu/worktree-context-menu-policy.ts +++ b/src/renderer/src/components/sidebar/workspace-actions/context-menu/worktree-context-menu-policy.ts @@ -1,3 +1,7 @@ +import { + requestVirtualizedScrollAnchorRecord, + supersedeVirtualizedScrollAnchorRestore +} from '@/hooks/requestVirtualizedScrollAnchorRecord' import type { AppState } from '@/store/types' import type { Repo } from '../../../../../../shared/repo-types' import { isGitRepoKind } from '../../../../../../shared/repo-kind' @@ -203,6 +207,7 @@ export function preserveDeleteSiblingPosition(scope: HTMLElement | null): () => const previousScrollTop = sidebar.scrollTop const previousScrollHeight = sidebar.scrollHeight const desiredTop = row.getBoundingClientRect().top + supersedeVirtualizedScrollAnchorRestore(sidebar) return () => { let attempts = 0 @@ -216,6 +221,10 @@ export function preserveDeleteSiblingPosition(scope: HTMLElement | null): () => const currentAnchor = currentTarget ?? findSidebarVirtualRowByKey(currentSidebar, anchorKey) if (currentAnchor) { const delta = currentAnchor.getBoundingClientRect().top - desiredTop + supersedeVirtualizedScrollAnchorRestore( + currentSidebar, + Math.abs(delta) > 1 ? currentSidebar.scrollTop + delta : undefined + ) if (Math.abs(delta) > 1) { currentSidebar.scrollTop += delta stableFrames = 0 @@ -223,10 +232,12 @@ export function preserveDeleteSiblingPosition(scope: HTMLElement | null): () => stableFrames += 1 } } else { - currentSidebar.scrollTop = Math.max( + const target = Math.max( 0, previousScrollTop + currentSidebar.scrollHeight - previousScrollHeight ) + supersedeVirtualizedScrollAnchorRestore(currentSidebar, target) + currentSidebar.scrollTop = target stableFrames = 0 } attempts += 1 @@ -237,6 +248,8 @@ export function preserveDeleteSiblingPosition(scope: HTMLElement | null): () => }) ) { window.requestAnimationFrame(restore) + } else { + requestVirtualizedScrollAnchorRecord(currentSidebar) } } restore() diff --git a/src/renderer/src/components/sidebar/workspace-actions/sleep/sleep-worktree-sidebar-position.ts b/src/renderer/src/components/sidebar/workspace-actions/sleep/sleep-worktree-sidebar-position.ts index d8e49a3526c..22cc64823df 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/sleep/sleep-worktree-sidebar-position.ts +++ b/src/renderer/src/components/sidebar/workspace-actions/sleep/sleep-worktree-sidebar-position.ts @@ -1,4 +1,7 @@ -import { VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT } from '@/hooks/useVirtualizedScrollAnchor' +import { + requestVirtualizedScrollAnchorRecord, + supersedeVirtualizedScrollAnchorRestore +} from '@/hooks/requestVirtualizedScrollAnchorRecord' function getSidebarWorktreeOptions(worktreeId: string): HTMLElement[] { return Array.from(document.querySelectorAll('[data-worktree-id]')).filter( @@ -44,10 +47,10 @@ export function preserveSidebarWorktreePosition(worktreeId: string): () => void if (!scroller || !row) { return () => {} } - scroller.dispatchEvent(new Event(VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT)) const previousScrollTop = scroller.scrollTop const previousScrollHeight = scroller.scrollHeight const previousTop = row.getBoundingClientRect().top + supersedeVirtualizedScrollAnchorRestore(scroller) return () => { let attempts = 0 @@ -63,12 +66,18 @@ export function preserveSidebarWorktreePosition(worktreeId: string): () => void const nextRow = findSidebarWorktreeRow(worktreeId, activeRowKey) if (!nextRow) { // After remount or height changes, approximate the old viewport so the row mounts before correcting its DOM position. - currentScroller.scrollTop = Math.max( + const target = Math.max( 0, previousScrollTop + currentScroller.scrollHeight - previousScrollHeight ) + supersedeVirtualizedScrollAnchorRestore(currentScroller, target) + currentScroller.scrollTop = target } else { const delta = nextRow.getBoundingClientRect().top - previousTop + supersedeVirtualizedScrollAnchorRestore( + currentScroller, + Math.abs(delta) > 1 ? currentScroller.scrollTop + delta : undefined + ) if (Math.abs(delta) > 1) { currentScroller.scrollTop += delta } @@ -76,6 +85,8 @@ export function preserveSidebarWorktreePosition(worktreeId: string): () => void attempts += 1 if (attempts < 12) { window.requestAnimationFrame(restore) + } else { + requestVirtualizedScrollAnchorRecord(currentScroller) } } window.requestAnimationFrame(restore) diff --git a/src/renderer/src/components/sidebar/workspace-card/activity/WorktreeCardAgents.tsx b/src/renderer/src/components/sidebar/workspace-card/activity/WorktreeCardAgents.tsx index 65f32fb5759..6120278a81e 100644 --- a/src/renderer/src/components/sidebar/workspace-card/activity/WorktreeCardAgents.tsx +++ b/src/renderer/src/components/sidebar/workspace-card/activity/WorktreeCardAgents.tsx @@ -32,13 +32,6 @@ import { } from './worktree-card-idle-child-agents' import { recomputeOn } from '@/lib/recompute-on' -export const SUPPRESS_WORKTREE_LIST_SCROLL_ADJUSTMENT_EVENT = - 'orca-suppress-worktree-list-scroll-adjustment' - -const dispatchSuppressScrollAdjustment = () => { - window.dispatchEvent(new CustomEvent(SUPPRESS_WORKTREE_LIST_SCROLL_ADJUSTMENT_EVENT)) -} - type Props = { worktreeId: string agents?: DashboardAgentRowData[] @@ -173,8 +166,8 @@ const WorktreeCardAgentsBody = React.memo(function WorktreeCardAgentsBody({ expandedLineageParents, expandedIdleChildGroups, compactRootListExpanded, - toggleLineageParent: toggleLineageParentState, - toggleIdleChildGroup: toggleIdleChildGroupState, + toggleLineageParent, + toggleIdleChildGroup, toggleCompactRootList } = useWorktreeAgentExpansionState(worktreeId) @@ -184,7 +177,6 @@ const WorktreeCardAgentsBody = React.memo(function WorktreeCardAgentsBody({ const wasExpanded = previousCompactExpandedRef.current previousCompactExpandedRef.current = compactRootListExpanded if (!wasExpanded && compactRootListExpanded && agentActivityDisplayMode === 'compact') { - dispatchSuppressScrollAdjustment() // Defer reveal one frame to avoid synchronous sidebar layout during the opening animation. const handle = requestAnimationFrame(() => { revealCompactAgentCard(compactAgentListRootRef.current) @@ -193,20 +185,6 @@ const WorktreeCardAgentsBody = React.memo(function WorktreeCardAgentsBody({ } return undefined }, [agentActivityDisplayMode, compactRootListExpanded]) - const toggleLineageParent = useCallback( - (paneKey: string) => { - dispatchSuppressScrollAdjustment() - toggleLineageParentState(paneKey) - }, - [toggleLineageParentState] - ) - const toggleIdleChildGroup = useCallback( - (parentPaneKey: string) => { - dispatchSuppressScrollAdjustment() - toggleIdleChildGroupState(parentPaneKey) - }, - [toggleIdleChildGroupState] - ) const sendTargetFor = (paneKey: string) => isAgentSendTargetModeActive @@ -402,10 +380,7 @@ const WorktreeCardAgentsBody = React.memo(function WorktreeCardAgentsBody({ agents={summaryAgents} subjectLabel={subjectLabel} expanded={compactRootListExpanded} - onToggle={() => { - dispatchSuppressScrollAdjustment() - toggleCompactRootList() - }} + onToggle={toggleCompactRootList} /> {rootAgents.map((rootAgent) => diff --git a/src/renderer/src/components/sidebar/workspace-card/identity/WorktreeLineageToggle.test.tsx b/src/renderer/src/components/sidebar/workspace-card/identity/WorktreeLineageToggle.test.tsx new file mode 100644 index 00000000000..73355c73026 --- /dev/null +++ b/src/renderer/src/components/sidebar/workspace-card/identity/WorktreeLineageToggle.test.tsx @@ -0,0 +1,52 @@ +// @vitest-environment happy-dom +import '@testing-library/jest-dom/vitest' +import { useMemo, useState } from 'react' +import { cleanup, render, screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { afterEach, expect, it, vi } from 'vitest' +import { TooltipProvider } from '@/components/ui/tooltip' +import { WorktreeLineageToggle } from './WorktreeLineageToggle' +import { createLineageToggleHandlerCache } from './worktree-lineage-toggle-handler-cache' + +vi.mock('../activity/use-worktree-activity-statuses', () => ({ + useWorktreeActivityStatuses: () => new Map() +})) + +afterEach(cleanup) + +it('dismisses the disclosure tooltip on keyboard activation without selecting the card or losing focus', async () => { + const activateCard = vi.fn() + function Card() { + const [collapsed, setCollapsed] = useState(false) + const toggle = useMemo( + () => createLineageToggleHandlerCache(() => setCollapsed((value) => !value))('parent'), + [] + ) + return ( + +
+ + {!collapsed && Child workspace} +
+
+ ) + } + const user = userEvent.setup() + render() + const toggle = screen.getByRole('button', { name: 'Toggle child workspaces' }) + + await user.tab() + expect(await screen.findByRole('tooltip')).toBeInTheDocument() + await user.keyboard('{Enter}') + + expect(screen.queryByText('Child workspace')).not.toBeInTheDocument() + await waitFor(() => expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()) + expect(toggle).toHaveFocus() + expect(activateCard).not.toHaveBeenCalled() +}) diff --git a/src/renderer/src/components/sidebar/workspace-card/identity/worktree-lineage-toggle-handler-cache.ts b/src/renderer/src/components/sidebar/workspace-card/identity/worktree-lineage-toggle-handler-cache.ts index 7188ad9065f..55caa323c5a 100644 --- a/src/renderer/src/components/sidebar/workspace-card/identity/worktree-lineage-toggle-handler-cache.ts +++ b/src/renderer/src/components/sidebar/workspace-card/identity/worktree-lineage-toggle-handler-cache.ts @@ -13,7 +13,6 @@ export const createLineageToggleHandlerCache = ( return cached } const handler: LineageToggleHandler = (event) => { - event.preventDefault() event.stopPropagation() toggleGroup(groupKey) } diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/drop-commit-context.ts b/src/renderer/src/components/sidebar/worktree-list/drag/drop-commit-context.ts index c73b8cc1666..28eb06ef19b 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/drop-commit-context.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/drop-commit-context.ts @@ -54,6 +54,11 @@ export type WorktreeDropCommitContext = { pickedCopies: DraggedWorktreeCopies, parentIdentity: string ) => boolean + captureFolderRetention?: ( + point: WorktreeReorderDropPoint | undefined, + pickedCopies: DraggedWorktreeCopies, + sourceGroupKey: string + ) => WorktreeReorderDropPoint | undefined clearReorderedWorktreeParents: (args: { draggedIds: readonly string[] sourceGroupKey: string diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/pointer-commit.ts b/src/renderer/src/components/sidebar/worktree-list/drag/pointer-commit.ts index 993dc6e1842..abb0dd66589 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/pointer-commit.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/pointer-commit.ts @@ -117,6 +117,10 @@ export function commitWorktreePointerDrop(args: PointerDropCommitArgs): void { } const drop = ctx.computeWorktreeDrop(event.clientY) if (drop) { + const rawDropPoint = getWorktreeReorderDropPoint(event, drop, ctx.scrollRef.current) + const dropPoint = + ctx.captureFolderRetention?.(rawDropPoint, drag.pickedCopies, drag.sourceGroupKey) ?? + rawDropPoint ctx.onReorderWorktrees({ groups: ctx.worktreeDragGroups, sourceGroupKey: drag.sourceGroupKey, @@ -131,7 +135,7 @@ export function commitWorktreePointerDrop(args: PointerDropCommitArgs): void { draggedIds: drag.draggedIds, sourceGroupKey: drag.sourceGroupKey, pickedCopies: drag.pickedCopies, - dropPoint: getWorktreeReorderDropPoint(event, drop, ctx.scrollRef.current) + dropPoint }) } else if (ctx.scrollRef.current) { const currentPreview = preferredStatusTarget.status diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/project-group-header-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/project-group-header-drag.ts index 5f975395774..37ffd4d3547 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/project-group-header-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/project-group-header-drag.ts @@ -1,3 +1,7 @@ +import { + requestVirtualizedScrollAnchorRecord, + supersedeVirtualizedScrollAnchorRestore +} from '@/hooks/requestVirtualizedScrollAnchorRecord' import { measureRootProjectHeaderDragRects } from './root-project-header-drop' import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' @@ -176,7 +180,9 @@ export function useProjectGroupHeaderDrag({ elapsedMs: frameTime - previousFrameTime }) if (autoscroll) { + supersedeVirtualizedScrollAnchorRestore(container, autoscroll.scrollTop) container.scrollTop = autoscroll.scrollTop + requestVirtualizedScrollAnchorRecord(container) refreshHeaderRects() } @@ -285,8 +291,7 @@ export function useProjectGroupHeaderDrag({ return } const body = document.body - const prevCursor = body.style.cursor - const prevUserSelect = body.style.userSelect + const { cursor: prevCursor, userSelect: prevUserSelect } = body.style body.style.cursor = 'grabbing' body.style.userSelect = 'none' return () => { diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/project-header-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/project-header-drag.ts index bdddbe863c9..63a10e4bc39 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/project-header-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/project-header-drag.ts @@ -1,3 +1,7 @@ +import { + requestVirtualizedScrollAnchorRecord, + supersedeVirtualizedScrollAnchorRestore +} from '@/hooks/requestVirtualizedScrollAnchorRecord' import { measureRootProjectHeaderDragRects } from './root-project-header-drop' import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' @@ -189,7 +193,9 @@ export function useRepoHeaderDrag({ elapsedMs: frameTime - previousFrameTime }) if (autoscroll) { + supersedeVirtualizedScrollAnchorRestore(container, autoscroll.scrollTop) container.scrollTop = autoscroll.scrollTop + requestVirtualizedScrollAnchorRecord(container) refreshHeaderRects() } @@ -290,8 +296,7 @@ export function useRepoHeaderDrag({ return } const body = document.body - const prevCursor = body.style.cursor - const prevUserSelect = body.style.userSelect + const { cursor: prevCursor, userSelect: prevUserSelect } = body.style body.style.cursor = 'grabbing' body.style.userSelect = 'none' return () => { diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-document-drop.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-document-drop.ts index 04a4eab1159..e32c742a196 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-document-drop.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-document-drop.ts @@ -71,6 +71,10 @@ export function useWorktreeDocumentDrop(args: { // Why: pointer still inside the source group means reorder, not status move; commit here and stop the capture handler. event.preventDefault() event.stopPropagation() + const rawDropPoint = getWorktreeReorderDropPoint(event, drop, ctx.scrollRef.current) + const dropPoint = + ctx.captureFolderRetention?.(rawDropPoint, session.pickedCopies, session.sourceGroupKey) ?? + rawDropPoint ctx.onReorderWorktrees({ groups: ctx.worktreeDragGroups, sourceGroupKey: session.sourceGroupKey, @@ -85,7 +89,7 @@ export function useWorktreeDocumentDrop(args: { draggedIds: session.draggedIds, sourceGroupKey: session.sourceGroupKey, pickedCopies: session.pickedCopies, - dropPoint: getWorktreeReorderDropPoint(event, drop, ctx.scrollRef.current) + dropPoint }) clearWorktreeDrag() } diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-drop-commit-context.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-drop-commit-context.ts index b362901d6dc..8d5c3942481 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-drop-commit-context.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-drop-commit-context.ts @@ -39,6 +39,7 @@ export function useWorktreeDropCommitContext(args: { refreshWorktreeDragSession: session.refreshWorktreeDragSession, getEligibleLineageDropTarget: lineageDrop.getEligibleLineageDropTarget, commitWorktreeLineageParentDrop: lineageDrop.commitWorktreeLineageParentDrop, + captureFolderRetention: lineageDrop.captureFolderRetention, clearReorderedWorktreeParents: lineageDrop.clearReorderedWorktreeParents, clearWorktreeDrag: runtime.clearWorktreeDrag, onMoveWorktreesToStatus: (worktreeIds, status) => @@ -59,6 +60,7 @@ export function useWorktreeDropCommitContext(args: { onReorderWorktrees, runtime.clearWorktreeDrag, scrollRef, + lineageDrop.captureFolderRetention, session.computeWorktreeDrop, session.computeWorktreeStatusDrop, session.refreshWorktreeDragSession, diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-header-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-header-drag.ts index 0e8312bfafb..6d102996a91 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-header-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-header-drag.ts @@ -24,7 +24,6 @@ import { useRepoHeaderDrag } from './project-header-drag' import { getSidebarOrderedRepoHeaderIdsByBucket } from './project-header-drop' import { useProjectGroupHeaderDrag } from './project-group-header-drag' import { getSidebarOrderedProjectGroupHeaderIdsByBucket } from './project-group-header-drop' -import { USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS } from '../viewport/use-scroll-suppression' import { getRootProjectHeaderCountByOccurrenceKey, getSidebarRootProjectHeaderKeys @@ -68,8 +67,7 @@ export function useWorktreeSidebarHeaderDrag(args: { scrollRef: React.RefObject onReorderHostSections: (orderedHostIds: ExecutionHostId[]) => void onHostDragActiveChange: (active: boolean) => void - suppressMeasurementAdjustmentUntilRef: React.MutableRefObject - directScrollInputUntilRef: React.MutableRefObject + onScrollIntent: () => void }) { const { rows, @@ -83,8 +81,7 @@ export function useWorktreeSidebarHeaderDrag(args: { scrollRef, onReorderHostSections, onHostDragActiveChange, - suppressMeasurementAdjustmentUntilRef, - directScrollInputUntilRef + onScrollIntent } = args const reorderRepos = useAppStore((s) => s.reorderRepos) const moveProjectToGroup = useAppStore((s) => s.moveProjectToGroup) @@ -124,18 +121,12 @@ export function useWorktreeSidebarHeaderDrag(args: { return byGroupId }, [projectGroups]) - // Suppress anchor correction after manual header moves so it cannot chase the reordered row. - const suppressScrollCorrectionForHeaderCommit = useCallback(() => { - const suppressUntil = window.performance.now() + USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS - suppressMeasurementAdjustmentUntilRef.current = suppressUntil - directScrollInputUntilRef.current = suppressUntil - }, [directScrollInputUntilRef, suppressMeasurementAdjustmentUntilRef]) const commitRepoReorder = useCallback( (orderedIds: string[]) => { - suppressScrollCorrectionForHeaderCommit() + onScrollIntent() reorderRepos(orderedIds) }, - [reorderRepos, suppressScrollCorrectionForHeaderCommit] + [reorderRepos, onScrollIntent] ) const orderedHostIds = useMemo( () => @@ -196,12 +187,12 @@ export function useWorktreeSidebarHeaderDrag(args: { if (!Number.isFinite(tabOrder)) { return } - suppressScrollCorrectionForHeaderCommit() + onScrollIntent() // Why: manual order persists on the group's own host; the focused host may not hold this row. const ownerHostId = projectGroupOwnerHostIdByGroupId.get(groupId) void updateProjectGroup(groupId, { tabOrder }, { hostId: ownerHostId ?? undefined }) }, - [projectGroupOwnerHostIdByGroupId, suppressScrollCorrectionForHeaderCommit, updateProjectGroup] + [projectGroupOwnerHostIdByGroupId, onScrollIntent, updateProjectGroup] ) const commitRootProjectOrder = useCallback( (visibleKeys: readonly string[], draggedKey: string, dropIndex: number) => { @@ -211,7 +202,7 @@ export function useWorktreeSidebarHeaderDrag(args: { visibleKeys, draggedKey, dropIndex, - onCommit: suppressScrollCorrectionForHeaderCommit + onCommit: onScrollIntent }) void save() .catch(() => false) @@ -226,7 +217,7 @@ export function useWorktreeSidebarHeaderDrag(args: { } }) }, - [suppressScrollCorrectionForHeaderCommit] + [onScrollIntent] ) // Drag applies only in manual order; still construct the controller inert for stable hook order. const repoDrag = useRepoHeaderDrag({ diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-lineage-drop-commit.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-lineage-drop-commit.ts index 32a1678d3de..f9327e0e7cf 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-lineage-drop-commit.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-lineage-drop-commit.ts @@ -15,7 +15,6 @@ import { getCyclicProjectedWorktreeLineageIds } from '../../../../../../shared/s import { getReorderedWorktreeIdsToUnnest, getReorderUnnestRows, - isReorderInsideFolderRow, type WorktreeReorderDropPoint } from './worktree-lineage-drag-drop' import { unnestWorktrees } from '../../workspace-actions/parenting/worktree-unnest' @@ -35,10 +34,51 @@ export function useWorktreeLineageDropCommit(args: { worktreeMap: Map worktreeLineageById: Record worktreeDragGroups: readonly WorktreeDragGroup[] + keepsFolderParent: (identity: string, point: WorktreeReorderDropPoint | undefined) => boolean + captureFolderRetention: ( + point: WorktreeReorderDropPoint | undefined, + folders: ReadonlySet + ) => WorktreeReorderDropPoint | undefined }) { - const { repoMap, worktreeMap, worktreeLineageById, worktreeDragGroups } = args + const { repoMap, worktreeMap, worktreeLineageById, worktreeDragGroups, keepsFolderParent } = args const worktrees = useAllWorktrees() const parentIdentities = useAppStore(selectSidebarLineageParentIdentities) + const folderParentOf = useCallback( + (identity: string) => { + const parent = parentIdentities?.get(identity) + return parent !== undefined && + parseWorkspaceKey(getWorktreeIdFromHostIdentity(parent))?.type === 'folder' + ? parent + : undefined + }, + [parentIdentities] + ) + const { captureFolderRetention: captureGeometry } = args + const captureFolderRetention = useCallback( + ( + point: WorktreeReorderDropPoint | undefined, + pickedCopies: DraggedWorktreeCopies, + sourceGroupKey: string + ) => { + const folders = new Set() + let members: readonly string[] | undefined + for (const copy of pickedCopies) { + const identity = getWorktreeHostIdentity(copy) + const parent = folderParentOf(identity) + if (parent) { + if (members === undefined) { + members = + worktreeDragGroups.find((group) => group.key === sourceGroupKey)?.worktreeIds ?? [] + } + if (members.includes(identity)) { + folders.add(parent) + } + } + } + return captureGeometry(point, folders) + }, + [captureGeometry, folderParentOf, worktreeDragGroups] + ) const assignWorktreeParent = useAppStore((s) => s.assignWorktreeParent) const assignCrossStoreWorktreeParent = useAppStore((s) => s.assignCrossStoreWorktreeParent) const updateWorktreeLineage = useAppStore((s) => s.updateWorktreeLineage) @@ -184,14 +224,8 @@ export function useWorktreeLineageDropCommit(args: { legacyIds, pickedCopies: unnestArgs.pickedCopies ?? [], sourceGroupIdentities: sourceGroup.worktreeIds, - folderParentOf: (identity) => { - const parent = parentIdentities?.get(identity) - return parent !== undefined && - parseWorkspaceKey(getWorktreeIdFromHostIdentity(parent))?.type === 'folder' - ? parent - : undefined - }, - keepsFolderParent: (folder) => isReorderInsideFolderRow(folder, unnestArgs.dropPoint) + folderParentOf, + keepsFolderParent: (folder) => keepsFolderParent(folder, unnestArgs.dropPoint) }) if (rows.length === 0) { return @@ -201,7 +235,8 @@ export function useWorktreeLineageDropCommit(args: { }, [ cyclicLineageIds, - parentIdentities, + keepsFolderParent, + folderParentOf, updateWorktreeLineage, worktreeDragGroups, worktreeLineageById, @@ -212,6 +247,7 @@ export function useWorktreeLineageDropCommit(args: { return { getEligibleLineageDropTarget, commitWorktreeLineageParentDrop, + captureFolderRetention, clearReorderedWorktreeParents } } diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-native-autoscroll.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-native-autoscroll.ts index 7820367d283..81b0d69bbc2 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-native-autoscroll.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-native-autoscroll.ts @@ -1,3 +1,7 @@ +import { + requestVirtualizedScrollAnchorRecord, + supersedeVirtualizedScrollAnchorRestore +} from '@/hooks/requestVirtualizedScrollAnchorRecord' import { useCallback } from 'react' import type React from 'react' import { getWorktreeSidebarDragAutoscroll } from './worktree-sidebar-drag-autoscroll' @@ -48,7 +52,9 @@ export function useWorktreeNativeDragAutoscroll(args: { }) if (autoscroll) { markScrollMovement() + supersedeVirtualizedScrollAnchorRestore(container, autoscroll.scrollTop) container.scrollTop = autoscroll.scrollTop + requestVirtualizedScrollAnchorRecord(container) if (!session.refreshWorktreeDragSession()) { clearWorktreeDrag() return diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts index 2949666f5fe..92b070c5f25 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts @@ -8,7 +8,6 @@ import { useCallback } from 'react' import type React from 'react' import { getWorkspaceKanbanSidebarDropTarget } from '../../workspace-board/drag/workspace-kanban-sidebar-drop' import { getFullDropIndexForWorktreeDragUnit } from './worktree-drag-units' -import { getWorktreeSidebarDragRectsForGroup } from './worktree-sidebar-drag-autoscroll' import { getWorktreeSidebarDragGrab } from './worktree-sidebar-drag-geometry' import { getPointerDropStatusTarget } from './status-target' import type { WorktreeDropCommitContext } from './drop-commit-context' @@ -72,9 +71,7 @@ export function useWorktreeNativeDrag(args: { sourceGroupKey, reorderDraggedKeys ) - const rects = scrollRef.current - ? getWorktreeSidebarDragRectsForGroup(scrollRef.current, sourceGroupKey) - : [] + const rects = scrollRef.current ? session.getRects(sourceGroupKey) : [] const sourceRect = event.currentTarget.getBoundingClientRect() session.worktreeDragSessionRef.current = { draggingWorktreeId: worktreeKey, @@ -232,6 +229,13 @@ export function useWorktreeNativeDrag(args: { return } event.preventDefault() + const rawDropPoint = getWorktreeReorderDropPoint(event, drop, ctx.scrollRef.current) + const dropPoint = + ctx.captureFolderRetention?.( + rawDropPoint, + dragSession.pickedCopies, + dragSession.sourceGroupKey + ) ?? rawDropPoint ctx.onReorderWorktrees({ groups: ctx.worktreeDragGroups, sourceGroupKey: dragSession.sourceGroupKey, @@ -246,7 +250,7 @@ export function useWorktreeNativeDrag(args: { draggedIds: dragSession.draggedIds, sourceGroupKey: dragSession.sourceGroupKey, pickedCopies: dragSession.pickedCopies, - dropPoint: getWorktreeReorderDropPoint(event, drop, ctx.scrollRef.current) + dropPoint }) clearWorktreeDrag() }, diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-autoscroll.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-autoscroll.ts index 2f608818add..a62486325e9 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-autoscroll.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-autoscroll.ts @@ -1,3 +1,7 @@ +import { + requestVirtualizedScrollAnchorRecord, + supersedeVirtualizedScrollAnchorRestore +} from '@/hooks/requestVirtualizedScrollAnchorRecord' import { useCallback } from 'react' import type React from 'react' import { getWorktreeSidebarDragAutoscroll } from './worktree-sidebar-drag-autoscroll' @@ -45,7 +49,9 @@ export function useWorktreePointerDragAutoscroll(args: { }) if (autoscroll) { markScrollMovement() + supersedeVirtualizedScrollAnchorRestore(container, autoscroll.scrollTop) container.scrollTop = autoscroll.scrollTop + requestVirtualizedScrollAnchorRecord(container) if (!session.refreshWorktreeDragSession()) { clearWorktreeDrag() return diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts index c07f17a7315..91a89f13fcb 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts @@ -8,7 +8,6 @@ import { isSidebarPointerDragBlocked, setSidebarPointerDragDocumentStyles } from './worktree-sidebar-pointer-drag-dom' -import { getWorktreeSidebarDragRectsForGroup } from './worktree-sidebar-drag-autoscroll' import { getWorktreeSidebarDragGrab } from './worktree-sidebar-drag-geometry' import { NOOP_WORKSPACE_BOARD_DRAG_PREVIEW_CALLBACK } from './drop-commit-context' import type { @@ -174,12 +173,13 @@ export function useWorktreePointerDrag(args: { if (!sourceGroupKey || !container) { return } - const rects = getWorktreeSidebarDragRectsForGroup(container, sourceGroupKey) + const rects = session.getRects(sourceGroupKey) const canPreviewWorkspaceBoardOnDrag = !workspaceBoardOpen && onWorkspaceBoardDragPreviewStart !== NOOP_WORKSPACE_BOARD_DRAG_PREVIEW_CALLBACK if ( - rects.length <= 1 && + (session.worktreeDragGroups.find((group) => group.key === sourceGroupKey)?.worktreeIds + .length ?? 0) <= 1 && !hasWorkspaceKanbanSidebarDropBoard() && !canPreviewWorkspaceBoardOnDrag ) { diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-session.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-session.ts index 0b7281d6653..19d08ca2f08 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-session.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-session.ts @@ -7,9 +7,8 @@ import { WORKTREE_SIDEBAR_VIRTUAL_ROW_GAP } from '../viewport/virtual-rows' import '../../workspace-status/presentation/workspace-status' import { getWorkspaceStatusGroupKey } from '../../../../../../shared/workspace-statuses' import { expandDraggedWorktreeIdsForVisibleLineage } from './worktree-manual-order' -import { getWorktreeDragUnitGroups } from './worktree-drag-units' +import type { WorktreeRowGeometry } from '../viewport/use-row-geometry' import { - getWorktreeSidebarDragRectsForGroup, refreshWorktreeSidebarDragSession, type WorktreeSidebarDragRect, type WorktreeSidebarDragSession @@ -23,7 +22,7 @@ import { computeWorktreeSidebarDropPreview, type WorktreeSidebarDropPreview } from './worktree-sidebar-drop-preview' -import { getWorktreeDragGroups, getWorktreeDragIndexes } from './groups' +import { getWorktreeDragGroups } from './groups' import type { WorktreeItemRow } from '../listing/renderable-rows' import { getNaturalWorktreeKeys } from '../listing/natural-worktree-keys' import { @@ -46,14 +45,16 @@ export type WorktreeDragSession = ReturnType export function useWorktreeDragSession(args: { rows: HostSectionRow[] scrollRef: React.RefObject + geometry: WorktreeRowGeometry }) { - const { rows, scrollRef } = args + const { rows, scrollRef, geometry } = args + const { getRects } = geometry const worktreeDragSessionRef = useRef(null) // Hold cross-group drop anchors separately so expanding target cards cannot move the line under a still pointer. const statusDropAnchorsRef = useRef>(new Map()) const worktreeDragGroups = useMemo(() => getWorktreeDragGroups(rows), [rows]) - const worktreeDragUnitGroups = useMemo(() => getWorktreeDragUnitGroups(rows), [rows]) + const worktreeDragUnitGroups = geometry.units const naturalDragWorktreeKeys = useMemo(() => getNaturalWorktreeKeys(rows), [rows]) const worktreeLineageDragRows = useMemo( () => @@ -103,10 +104,7 @@ export function useWorktreeDragSession(args: { }, [worktreeDragUnitGroups] ) - const { groupKeyByRowKey, groupIndexByRowKey } = useMemo( - () => getWorktreeDragIndexes(rows), - [rows] - ) + const { groupKeyByRowKey, groupIndexByRowKey } = geometry.indexes const refreshWorktreeDragSession = useCallback((): boolean => { const session = worktreeDragSessionRef.current const container = scrollRef.current @@ -118,11 +116,11 @@ export function useWorktreeDragSession(args: { session, groups: worktreeDragGroups, unitGroups: worktreeDragUnitGroups, - rects: getWorktreeSidebarDragRectsForGroup(container, session.sourceGroupKey) + rects: getRects(session.sourceGroupKey, session.reorderUnitDraggedKeys) }) worktreeDragSessionRef.current = refreshedSession return refreshedSession !== null - }, [scrollRef, worktreeDragGroups, worktreeDragUnitGroups]) + }, [scrollRef, worktreeDragGroups, worktreeDragUnitGroups, getRects]) const computeWorktreeDropForGroup = useCallback( (dropArgs: { pointerY: number @@ -210,7 +208,7 @@ export function useWorktreeDragSession(args: { const preview = computeWorktreeDropForGroup({ pointerY: request.pointerY, groupKey, - rects: getWorktreeSidebarDragRectsForGroup(container, groupKey), + rects: getRects(groupKey), draggedIds: request.draggedKeys, draggingWorktreeId: session?.draggingWorktreeId ?? null, grab: session?.grab ?? null, @@ -227,12 +225,13 @@ export function useWorktreeDragSession(args: { } return preview }, - [computeWorktreeDropForGroup, scrollRef] + [computeWorktreeDropForGroup, scrollRef, getRects] ) // Keep session callback identity stable for memoized cards. return useMemo( () => ({ + getRects, worktreeDragSessionRef, statusDropAnchorsRef, worktreeDragGroups, @@ -247,6 +246,7 @@ export function useWorktreeDragSession(args: { computeWorktreeStatusDrop }), [ + getRects, computeWorktreeDrop, computeWorktreeStatusDrop, getReorderDraggedCopies, diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-drag-preview-offsets.test.ts b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-drag-preview-offsets.test.ts new file mode 100644 index 00000000000..906c085fb44 --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-drag-preview-offsets.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest' +import { buildWorktreeDragPreviewOffsets } from './worktree-drag-preview-offsets' + +describe('virtualized workspace drag preview', () => { + it('opens the full insertion space when the source and target have unmounted units between them', () => { + const preview = buildWorktreeDragPreviewOffsets({ + groupIds: ['first', 'offscreen', 'picked'], + draggedIds: ['picked'], + draggingWorktreeId: 'picked', + draggedPreviewHeight: 40, + fallbackGap: 0, + dropIndex: 0, + rects: [ + { worktreeId: 'first', groupIndex: 0, top: 0, bottom: 100, layoutBottom: 106 }, + { worktreeId: 'picked', groupIndex: 2, top: 212, bottom: 312, layoutBottom: 318 } + ] + }) + expect(preview.offsets.get('first')).toBe(106) + expect(preview.placeholderTop).toBe(0) + }) +}) diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-drag-preview-offsets.ts b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-drag-preview-offsets.ts index b334e7ceb96..fa345e3f908 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-drag-preview-offsets.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-drag-preview-offsets.ts @@ -5,6 +5,7 @@ export type WorktreeDragPreviewRect = { groupIndex: number top: number bottom: number + layoutBottom?: number } function arraysEqual(a: readonly string[], b: readonly string[]): boolean { @@ -82,6 +83,7 @@ export function buildWorktreeDragPreviewOffsets(args: { dropIndex: number rects: readonly WorktreeDragPreviewRect[] }): WorktreeDragPreviewLayout { + const rects = args.rects.map((rect) => ({ ...rect, bottom: rect.layoutBottom ?? rect.bottom })) const committedNextIds = moveWorktreeIdsWithinGroup( args.groupIds, args.draggedIds, @@ -108,7 +110,7 @@ export function buildWorktreeDragPreviewOffsets(args: { const groupIdSet = new Set(args.groupIds) const rectById = new Map() - for (const rect of args.rects) { + for (const rect of rects) { if (groupIdSet.has(rect.worktreeId)) { rectById.set(rect.worktreeId, rect) } @@ -120,8 +122,8 @@ export function buildWorktreeDragPreviewOffsets(args: { args.fallbackGap >= 0 ? args.fallbackGap : 0 - const fallbackStride = getFallbackStride(args.rects, defaultGap) - const fallbackGap = getFallbackGap(args.rects, defaultGap) + const fallbackStride = getFallbackStride(rects, defaultGap) + const fallbackGap = getFallbackGap(rects, defaultGap) const groupRects = args.groupIds.flatMap((id) => { const rect = rectById.get(id) return rect ? [rect] : [] @@ -179,7 +181,7 @@ export function buildWorktreeDragPreviewOffsets(args: { } const offsets = new Map() - for (const rect of args.rects) { + for (const rect of rects) { if (draggedSet.has(rect.worktreeId)) { continue } diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-lineage-drag-drop.test.ts b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-lineage-drag-drop.test.ts index 85da2eba217..26cc5dcb855 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-lineage-drag-drop.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-lineage-drag-drop.test.ts @@ -5,7 +5,7 @@ import { describe, expect, it } from 'vitest' import { getReorderUnnestRows, getWorktreeLineageDropTargetIdentity, - isReorderInsideFolderRow + isReorderInsideFolderBounds } from './worktree-lineage-drag-drop' describe('getWorktreeLineageDropTargetIdentity', () => { @@ -64,26 +64,20 @@ describe('getWorktreeLineageDropTargetIdentity', () => { }) }) -describe('isReorderInsideFolderRow', () => { - it('keeps a reorder inside the folder card only when the pointer and the insertion line are in it', () => { - const folder = document.createElement('div') - folder.setAttribute('data-worktree-lineage-drop-row', '') - folder.setAttribute('data-worktree-host-identity', 'local|folder:f-1') - folder.getBoundingClientRect = () => new DOMRect(0, 100, 290, 200) - const card = document.createElement('div') - card.setAttribute('data-worktree-card-surface', 'true') - card.getBoundingClientRect = () => new DOMRect(15, 100, 265, 200) - folder.append(card) - document.body.append(folder) +describe('isReorderInsideFolderBounds', () => { + it('keeps first and last insertion gutters inside an offscreen folder, with an outer unnest escape', () => { + const bounds = { left: 15, right: 280, top: -100, bottom: 300 } const inside = { clientX: 140, clientY: 200, lineClientY: 180 } - expect(isReorderInsideFolderRow('local|folder:f-1', inside)).toBe(true) - expect(isReorderInsideFolderRow('local|folder:f-1', { ...inside, clientX: 900 })).toBe(false) - expect(isReorderInsideFolderRow('local|folder:f-1', { ...inside, clientX: 4 })).toBe(false) - expect(isReorderInsideFolderRow('local|folder:f-1', { ...inside, lineClientY: 320 })).toBe( - false + expect(isReorderInsideFolderBounds(inside, bounds)).toBe(true) + expect(isReorderInsideFolderBounds({ ...inside, clientY: 2, lineClientY: -3 }, bounds)).toBe( + true ) - expect(isReorderInsideFolderRow('local|folder:f-1', { ...inside, clientY: 340 })).toBe(false) - folder.remove() + expect(isReorderInsideFolderBounds({ ...inside, clientY: 295, lineClientY: 297 }, bounds)).toBe( + true + ) + expect(isReorderInsideFolderBounds({ ...inside, clientX: 4 }, bounds)).toBe(false) + expect(isReorderInsideFolderBounds({ ...inside, lineClientY: 303 }, bounds)).toBe(false) + expect(isReorderInsideFolderBounds({ ...inside, clientY: 340 }, bounds)).toBe(false) }) }) diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-lineage-drag-drop.ts b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-lineage-drag-drop.ts index a055dacc7cc..567ade41906 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-lineage-drag-drop.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-lineage-drag-drop.ts @@ -91,7 +91,12 @@ export function getReorderedWorktreeIdsToUnnest(args: { } /** Where a reorder dropped: the pointer, and the insertion line it committed to, in client pixels. */ -export type WorktreeReorderDropPoint = { clientX: number; clientY: number; lineClientY: number } +export type WorktreeReorderDropPoint = { + clientX: number + clientY: number + lineClientY: number + retainedFolderIdentities?: ReadonlySet +} export function getWorktreeReorderDropPoint( pointer: { clientX: number; clientY: number }, @@ -109,23 +114,13 @@ export function getWorktreeReorderDropPoint( } } -/** Whether a reorder landed inside a folder workspace's card, which holds its nested rows: the - * pointer and the committed insertion line both within it. */ -export function isReorderInsideFolderRow( - folderIdentity: string, - point: WorktreeReorderDropPoint | undefined +/** The full logical folder surface, including unmounted descendants and closing padding. */ +export function isReorderInsideFolderBounds( + point: WorktreeReorderDropPoint | undefined, + rect: Pick | undefined ): boolean { - if (!point) { - return false - } - const row = document.querySelector( - `[data-worktree-lineage-drop-row][data-worktree-host-identity="${CSS.escape(folderIdentity)}"]` - ) - // The visible card, not the full-width row around it. - const rect = ( - row?.querySelector('[data-worktree-card-surface]') ?? row - )?.getBoundingClientRect() return ( + point !== undefined && rect !== undefined && point.clientX >= rect.left && point.clientX <= rect.right && diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-sidebar-drag-autoscroll.ts b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-sidebar-drag-autoscroll.ts index 905beeaad03..587157bb3e0 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-sidebar-drag-autoscroll.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-sidebar-drag-autoscroll.ts @@ -23,6 +23,7 @@ export type WorktreeSidebarDragRect = { groupIndex: number top: number bottom: number + layoutBottom?: number } export type WorktreeSidebarDragSession = { diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-sidebar-drop-preview.ts b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-sidebar-drop-preview.ts index 2419d7eabc8..d503602ea91 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/worktree-sidebar-drop-preview.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/worktree-sidebar-drop-preview.ts @@ -34,43 +34,6 @@ export type WorktreeSidebarTrackedStatusDropTarget = { const STATUS_DROP_TARGET_FALLBACK_TOLERANCE_PX = 6 -function getWorktreeSidebarDragUnitRects(args: { - rects: readonly WorktreeSidebarDragRect[] - groupIds: readonly string[] -}): WorktreeSidebarDragRect[] { - // Expanded lineage renders several child elements but moves as one parent drag unit. - const sortedRects = [...args.rects].sort((a, b) => a.top - b.top) - const rectByWorktreeId = new Map(sortedRects.map((rect) => [rect.worktreeId, rect])) - - return args.groupIds.flatMap((worktreeId, unitIndex) => { - const rootRect = rectByWorktreeId.get(worktreeId) - if (!rootRect) { - return [] - } - const nextRootTop = - args.groupIds - .slice(unitIndex + 1) - .flatMap((nextId) => { - const nextRect = rectByWorktreeId.get(nextId) - return nextRect ? [nextRect.top] : [] - }) - .at(0) ?? Number.POSITIVE_INFINITY - const unitBottom = sortedRects.reduce( - (bottom, rect) => - rect.top >= rootRect.top && rect.top < nextRootTop ? Math.max(bottom, rect.bottom) : bottom, - rootRect.bottom - ) - return [ - { - worktreeId, - groupIndex: unitIndex, - top: rootRect.top, - bottom: unitBottom - } - ] - }) -} - function hasWorktreeSidebarStatusDropTarget( target: WorktreeSidebarStatusDropTarget & { lineageParentIdentity?: string | null } ): boolean { @@ -169,10 +132,7 @@ export function computeWorktreeSidebarDropPreview(args: { // Hold the destination while the pointer stays still so card resizing cannot change the target. anchor?: WorktreeSidebarDropAnchor | null }): WorktreeSidebarDropPreview | null { - const rects = getWorktreeSidebarDragUnitRects({ - rects: args.rects, - groupIds: args.groupIds - }) + const rects = args.rects if (rects.length === 0 || args.groupIds.length === 0) { return null } diff --git a/src/renderer/src/components/sidebar/worktree-list/listing/WorktreeList.lineage-agent-expansion-coupling.test.tsx b/src/renderer/src/components/sidebar/worktree-list/listing/WorktreeList.lineage-agent-expansion-coupling.test.tsx index 082b64de180..a2e52f2294a 100644 --- a/src/renderer/src/components/sidebar/worktree-list/listing/WorktreeList.lineage-agent-expansion-coupling.test.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/listing/WorktreeList.lineage-agent-expansion-coupling.test.tsx @@ -47,7 +47,7 @@ vi.mock('@/store', () => ({ ) })) -// Honor getItemKey so toggling standalone/lineage-group rows changes React keys and reproduces the card remount. +// Use the virtualizer keys when rendering the visible workspace rows. vi.mock('@tanstack/react-virtual', () => ({ defaultRangeExtractor: ({ startIndex, endIndex }: { startIndex: number; endIndex: number }) => Array.from({ length: endIndex - startIndex + 1 }, (_, index) => startIndex + index), @@ -413,15 +413,6 @@ function childWorktreeCardPresent(container: HTMLElement): boolean { return container.querySelector('[id="worktree-list-option-all%3A%7Cchild"]') !== null } -function parentVirtualRowKey(container: HTMLElement): string | null { - return ( - container - .querySelector('[data-worktree-id="parent"]') - ?.closest('[data-worktree-virtual-row]') - ?.getAttribute('data-worktree-virtual-row-key') ?? null - ) -} - async function click(el: Element): Promise { await act(async () => { el.dispatchEvent(new MouseEvent('click', { bubbles: true })) @@ -451,12 +442,10 @@ describe('WorktreeCard agent-list <-> child-worktrees expansion coupling', () => clearWorktreeAgentExpansionStateForTests() }) - it('[full mode] toggling CHILD WORKTREES still remounts the card but PRESERVES agent expansion (regression)', async () => { + it('[full mode] collapsing child worktrees preserves agent expansion', async () => { setAgentLineageState({ agentActivityDisplayMode: 'full' }) const { container, root } = await renderWorktreeList() - expect(parentVirtualRowKey(container)).toBe('lineage-group:all:lineage:parent') - await click(agentChildDisclosure(container)!) expect(agentChildDisclosure(container)!.getAttribute('aria-expanded')).toBe('true') @@ -464,8 +453,6 @@ describe('WorktreeCard agent-list <-> child-worktrees expansion coupling', () => expect(mockStore.state.toggleCollapsedGroup).toHaveBeenCalledWith('lineage:parent') await rerender(root) - // Verify the parent actually remounted under a different row key; otherwise persistence would pass without exercising the bug. - expect(parentVirtualRowKey(container)).toBe('wt:all:|parent') expect(childWorktreeCardPresent(container)).toBe(false) expect(agentChildDisclosure(container)!.getAttribute('aria-expanded')).toBe('true') diff --git a/src/renderer/src/components/sidebar/worktree-list/listing/lineage-row-projection.ts b/src/renderer/src/components/sidebar/worktree-list/listing/lineage-row-projection.ts new file mode 100644 index 00000000000..1372c510946 --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-list/listing/lineage-row-projection.ts @@ -0,0 +1,215 @@ +import { useAppStore } from '@/store' +import { useMemo } from 'react' +import type { ExecutionHostId } from '../../../../../../shared/execution-host' +import { getRenderRowKey, type RenderRow } from './render-row' +import { + composeWorktreeHostIdentity, + getWorktreeHostIdentity, + getWorktreeIdFromHostIdentity +} from '../../../../../../shared/worktree/host-qualified-identity' +import { getFolderWorkspaceHostIdentity } from '../../../../../../shared/folder-workspaces/folder-workspace-worktree' +import type { WorktreeGroupBy } from '../../../../../../shared/sidebar-list/row-types' +import { + getFolderWorkspaceRowGeometry, + getWorktreeCardContentIndent, + getWorktreeCardSurfaceInset, + FLUSH_CARD_CONTENT_PULLBACK, + NEW_CARD_STYLE_STATUS_LANE_EXTRA_PULLBACK, + FLUSH_CARD_MIN_CONTENT_INSET, + WORKTREE_CARD_SURFACE_MARGIN, + LINEAGE_CHILDREN_INLINE_OFFSET +} from '../rows/indentation' + +export type LineageRowFrame = { + parent: number | null + root: number + end: number + left: number + right: number + contentIndent: number + before: number + after: number + closing: number +} + +export type LineageRowProjection = ReturnType + +export function buildLineageRowProjection( + rows: readonly RenderRow[], + groupBy: WorktreeGroupBy, + newCardStyle: boolean, + statusLane = newCardStyle +) { + const rowIndexByKey = new Map() + const rowKeys: string[] = [] + const frames: LineageRowFrame[] = [] + const occurrencesByIdentity = new Map() + const stack: number[] = [] + const close = (next: number) => { + const index = stack.pop()! + frames[index].end = next - 1 + } + rows.forEach((row, index) => { + const key = getRenderRowKey(row) + rowKeys.push(key) + rowIndexByKey.set(key, index) + const isWorkspace = row.type === 'item' || row.type === 'folder-workspace' + while (stack.length > 0) { + const parent = rows[stack.at(-1)!] + if ( + isWorkspace && + (parent.type === 'item' || parent.type === 'folder-workspace') && + parent.depth < row.depth + ) { + break + } + close(index) + } + const parent = isWorkspace ? (stack.at(-1) ?? null) : null + const frame: LineageRowFrame = { + parent, + root: parent === null ? index : frames[parent].root, + end: index, + left: 0, + right: 0, + contentIndent: 0, + before: 0, + after: 6, + closing: 0 + } + if (isWorkspace) { + const geometry = + row.type === 'folder-workspace' + ? getFolderWorkspaceRowGeometry({ + experimentalNewWorktreeCardStyle: newCardStyle, + isFolderBackedWorkspaceChild: + groupBy === 'repo' && row.projectGroup.createdFrom === 'folder-scan', + isGrouped: groupBy !== 'none', + groupDepth: row.groupDepth, + lineageDepth: row.depth + }) + : { + surfaceInset: getWorktreeCardSurfaceInset({ + isGrouped: groupBy !== 'none', + groupDepth: row.groupDepth + }), + cardContentIndent: + getWorktreeCardContentIndent({ + isGrouped: groupBy !== 'none', + groupDepth: row.groupDepth, + lineageDepth: row.depth + }) - + getWorktreeCardSurfaceInset({ + isGrouped: groupBy !== 'none', + groupDepth: row.groupDepth + }) + } + frame.left = geometry.surfaceInset + frame.contentIndent = Math.max(0, geometry.cardContentIndent) + if (parent !== null) { + const ancestor = frames[parent] + const padding = Math.max( + FLUSH_CARD_MIN_CONTENT_INSET, + ancestor.contentIndent - + FLUSH_CARD_CONTENT_PULLBACK - + (statusLane ? NEW_CARD_STYLE_STATUS_LANE_EXTRA_PULLBACK : 0) + ) + frame.left = + ancestor.left + + WORKTREE_CARD_SURFACE_MARGIN + + 1 + + padding + + LINEAGE_CHILDREN_INLINE_OFFSET + frame.right = ancestor.right + 7 + frame.contentIndent = 0 + frame.before = parent === index - 1 ? (newCardStyle ? 6 : 12) : 4 + } + const identity = + row.type === 'item' + ? getWorktreeHostIdentity(row.worktree) + : getFolderWorkspaceHostIdentity(row.folderWorkspace) + const occurrences = occurrencesByIdentity.get(identity) + if (occurrences) { + occurrences.push(index) + } else { + occurrencesByIdentity.set(identity, [index]) + } + } + frames.push(frame) + if (isWorkspace) { + stack.push(index) + } + }) + while (stack.length > 0) { + close(rows.length) + } + frames.forEach((frame, index) => { + if (frame.end > index) { + frames[frame.end].closing += 7 + } + const next = frames[index + 1] + frame.after = next?.root === frame.root ? 0 : 6 + }) + return { frames, occurrencesByIdentity, rowIndexByKey, restoreSignal: JSON.stringify(rowKeys) } +} + +export function useDraggedSubtreeRanges( + projection: LineageRowProjection, + identity: string | null +): readonly { start: number; end: number }[] { + return useMemo( + () => + (identity ? (projection.occurrencesByIdentity.get(identity) ?? []) : []).map((start) => ({ + start, + end: projection.frames[start].end + })), + [projection, identity] + ) +} + +export function useLineageRowProjection(args: { + rows: RenderRow[] + groupBy: WorktreeGroupBy + newCardStyle: boolean + activeWorktreeId: string | null + activeWorkspaceExecutionHostId: ExecutionHostId | null +}) { + const { rows, groupBy, newCardStyle, activeWorktreeId, activeWorkspaceExecutionHostId } = args + const statusLane = useAppStore((s) => s.worktreeCardProperties.includes('status')) && newCardStyle + const projection = useMemo( + () => buildLineageRowProjection(rows, groupBy, newCardStyle, statusLane), + [rows, groupBy, newCardStyle, statusLane] + ) + const firstHeaderIndex = useMemo( + () => rows.findIndex((row) => row.type === 'header' || row.type === 'host-header'), + [rows] + ) + const activeFamilyRoots = useMemo(() => { + const roots = new Set() + if (activeWorktreeId) { + const addRoots = (occurrences: readonly number[]) => { + for (const index of occurrences) { + const root = projection.frames[index].root + if (projection.frames[root].end > root) { + roots.add(root) + } + } + } + if (activeWorkspaceExecutionHostId) { + addRoots( + projection.occurrencesByIdentity.get( + composeWorktreeHostIdentity(activeWorkspaceExecutionHostId, activeWorktreeId) + ) ?? [] + ) + } else { + for (const [identity, occurrences] of projection.occurrencesByIdentity) { + if (getWorktreeIdFromHostIdentity(identity) === activeWorktreeId) { + addRoots(occurrences) + } + } + } + } + return roots + }, [activeWorktreeId, activeWorkspaceExecutionHostId, projection]) + return { projection, activeFamilyRoots, firstHeaderIndex } +} diff --git a/src/renderer/src/components/sidebar/worktree-list/listing/render-row.ts b/src/renderer/src/components/sidebar/worktree-list/listing/render-row.ts index 003318e9e29..03f59c77c23 100644 --- a/src/renderer/src/components/sidebar/worktree-list/listing/render-row.ts +++ b/src/renderer/src/components/sidebar/worktree-list/listing/render-row.ts @@ -1,12 +1,6 @@ import type { HostSectionRow } from '../../../../../../shared/sidebar-list/host-section-rows' -type WorktreeItemRow = Extract -type FolderWorkspaceItemRow = Extract -export type RenderRow = - | HostSectionRow - /** A parent card with its visible descendants. With `folder`, the folder is the parent and `rows` - * are only its descendants; otherwise `rows[0]` is the parent. */ - | { type: 'lineage-group'; key: string; rows: WorktreeItemRow[]; folder?: FolderWorkspaceItemRow } +export type RenderRow = HostSectionRow export function getRenderRowKey(row: RenderRow): string { if (row.type === 'host-header') { @@ -15,9 +9,6 @@ export function getRenderRowKey(row: RenderRow): string { if (row.type === 'header') { return row.hostId ? `hdr:${row.hostId}:${row.key}` : `hdr:${row.key}` } - if (row.type === 'lineage-group') { - return `lineage-group:${row.key}` - } if (row.type === 'imported-worktrees-card') { return `imported:${row.key}` } diff --git a/src/renderer/src/components/sidebar/worktree-list/listing/renderable-rows.ts b/src/renderer/src/components/sidebar/worktree-list/listing/renderable-rows.ts index 7f3140394a7..93e34f26670 100644 --- a/src/renderer/src/components/sidebar/worktree-list/listing/renderable-rows.ts +++ b/src/renderer/src/components/sidebar/worktree-list/listing/renderable-rows.ts @@ -1,7 +1,4 @@ -import { - PINNED_GROUP_KEY, - getWorktreeLineageGroupKey -} from '../../../../../../shared/sidebar-list/group-keys' +import { PINNED_GROUP_KEY } from '../../../../../../shared/sidebar-list/group-keys' import type { HostSectionRow } from '../../../../../../shared/sidebar-list/host-section-rows' import type { RenderRow } from './render-row' @@ -16,55 +13,7 @@ export function isPinnedWorktreeRow(row: WorktreeItemRow): boolean { return row.sectionKey === PINNED_GROUP_KEY } -// Collapse a parent and its visible lineage descendants into one virtual row so the card renders them inline. +// Each visible workspace owns a measured virtual slot, including expanded descendants. export function buildRenderableRows(rows: HostSectionRow[]): RenderRow[] { - const renderRows: RenderRow[] = [] - for (let index = 0; index < rows.length; index++) { - const row = rows[index] - if (row.type === 'folder-workspace') { - const descendants = getDescendantItemRows(rows, index, row.depth) - if (descendants.length > 0 && row.lineageGroupKey && !row.lineageCollapsed) { - renderRows.push({ type: 'lineage-group', key: row.key, rows: descendants, folder: row }) - index += descendants.length - } else { - renderRows.push(row) - } - continue - } - if ( - !isWorktreeItemRow(row) || - row.lineageChildCount === 0 || - row.lineageCollapsed || - rows[index + 1]?.type !== 'item' || - (rows[index + 1] as WorktreeItemRow).depth <= row.depth - ) { - renderRows.push(row) - continue - } - - const groupRows = [row, ...getDescendantItemRows(rows, index, row.depth)] - renderRows.push({ - type: 'lineage-group', - key: `${row.sectionKey}:${getWorktreeLineageGroupKey(row.worktree)}`, - rows: groupRows - }) - index += groupRows.length - 1 - } - return renderRows -} - -function getDescendantItemRows( - rows: readonly HostSectionRow[], - parentIndex: number, - parentDepth: number -): WorktreeItemRow[] { - const descendants: WorktreeItemRow[] = [] - for (let cursor = parentIndex + 1; cursor < rows.length; cursor++) { - const child = rows[cursor] - if (!isWorktreeItemRow(child) || child.depth <= parentDepth) { - break - } - descendants.push(child) - } - return descendants + return rows } diff --git a/src/renderer/src/components/sidebar/worktree-list/navigation/active-descendant-option.ts b/src/renderer/src/components/sidebar/worktree-list/navigation/active-descendant-option.ts index abc94161714..9ee53b713ae 100644 --- a/src/renderer/src/components/sidebar/worktree-list/navigation/active-descendant-option.ts +++ b/src/renderer/src/components/sidebar/worktree-list/navigation/active-descendant-option.ts @@ -1,9 +1,6 @@ import { getFolderWorkspaceHostIdentity } from '../../../../../../shared/folder-workspaces/folder-workspace-worktree' import type { RenderRow } from '../listing/render-row' -import { - getWorktreeExecutionHostId, - type ExecutionHostId -} from '../../../../../../shared/execution-host' +import type { ExecutionHostId } from '../../../../../../shared/execution-host' import type { PinnedWorktreeDisplayPolicy } from '../../../../../../shared/sidebar-list/row-types' import { isPinnedWorktreeRow } from '../listing/renderable-rows' import { getRenderRowWorktreeItem, renderRowContainsWorktree } from './render-row-lookup' @@ -11,34 +8,12 @@ import { getWorktreeOptionId } from '../rows/option-dom' export function getRenderRowOptionId( row: RenderRow | undefined, - worktreeId?: string | null, - executionHostId?: ExecutionHostId + _worktreeId?: string | null, + _executionHostId?: ExecutionHostId ): string | undefined { if (!row) { return undefined } - if (row.type === 'lineage-group' && row.folder) { - const childOption = getRenderRowOptionId( - { ...row, folder: undefined }, - worktreeId, - executionHostId - ) - return row.rows.some((item) => item.worktree.id === worktreeId) - ? childOption - : getRenderRowOptionId(row.folder, worktreeId, executionHostId) - } - if (row.type === 'lineage-group') { - // Hostless legacy state cannot disambiguate, but an aria pointer should still target a row. - const targetRow = worktreeId - ? row.rows.find( - (item) => - item.worktree.id === worktreeId && - (executionHostId === undefined || - getWorktreeExecutionHostId(item.worktree, item.repo) === executionHostId) - ) - : null - return getWorktreeOptionId((targetRow ?? row.rows[0])?.rowKey ?? row.key) - } if (row.type === 'item') { return getWorktreeOptionId(row.rowKey) } diff --git a/src/renderer/src/components/sidebar/worktree-list/navigation/mounted-row-reveal.ts b/src/renderer/src/components/sidebar/worktree-list/navigation/mounted-row-reveal.ts index 59773a6daec..de21a0b6175 100644 --- a/src/renderer/src/components/sidebar/worktree-list/navigation/mounted-row-reveal.ts +++ b/src/renderer/src/components/sidebar/worktree-list/navigation/mounted-row-reveal.ts @@ -3,9 +3,13 @@ import { composeWorktreeHostIdentity } from '../../../../../../shared/worktree/h import { getRevealBoundsVisibility, getRevealTargetBounds, - revealElementInScrollContainer + revealElementInScrollContainer, + type SidebarRevealScroll } from './worktree-sidebar-reveal' import { getMountedWorktreeOptions, getWorktreeOptionId } from '../rows/option-dom' +import { getRenderRowOptionId } from './active-descendant-option' +import type { RenderRow } from '../listing/render-row' +import type { PendingSidebarWorktreeReveal } from '@/store/slices/ui/ui-slice-contract-core' // Pinned duplicates render twice; an on-screen copy beats jumping to its off-screen twin (#24852). export function findOnScreenWorktreeOption( @@ -35,12 +39,29 @@ export function findOnScreenWorktreeOption( return fullyVisible ?? partlyVisible } +export function getPreferredMountedWorktreeOptionId( + container: HTMLElement, + targetRow: RenderRow, + request: PendingSidebarWorktreeReveal +): string | undefined { + if (request.rowKey) { + return getWorktreeOptionId(request.rowKey) + } + const { worktreeId, executionHostId } = request + const targetOptionId = getRenderRowOptionId(targetRow, worktreeId, executionHostId) + return ( + findOnScreenWorktreeOption(container, worktreeId, executionHostId, targetOptionId)?.id ?? + targetOptionId + ) +} + export function revealMountedWorktreeElement( container: HTMLElement, worktreeId: string, behavior: ScrollBehavior, optionId?: string, - onScrollIssued?: (targetTop: number) => void + onScrollIssued?: (targetTop: number) => void, + scrollTo?: SidebarRevealScroll ): HTMLElement | null { const element = optionId ? document.getElementById(optionId) @@ -48,7 +69,7 @@ export function revealMountedWorktreeElement( if (!element || !container.contains(element)) { return null } - return revealElementInScrollContainer(container, element, behavior, onScrollIssued) + return revealElementInScrollContainer(container, element, behavior, onScrollIssued, scrollTo) ? element : null } @@ -57,13 +78,14 @@ export function revealMountedSidebarRowElement( container: HTMLElement, rowKey: string, behavior: ScrollBehavior, - onScrollIssued?: (targetTop: number) => void + onScrollIssued?: (targetTop: number) => void, + scrollTo?: SidebarRevealScroll ): HTMLElement | null { const element = document.getElementById(getWorktreeOptionId(rowKey)) if (!element || !container.contains(element)) { return null } - return revealElementInScrollContainer(container, element, behavior, onScrollIssued) + return revealElementInScrollContainer(container, element, behavior, onScrollIssued, scrollTo) ? element : null } diff --git a/src/renderer/src/components/sidebar/worktree-list/navigation/pending-reveal-inputs.ts b/src/renderer/src/components/sidebar/worktree-list/navigation/pending-reveal-inputs.ts index bae4eb2cd8d..678c6c59e91 100644 --- a/src/renderer/src/components/sidebar/worktree-list/navigation/pending-reveal-inputs.ts +++ b/src/renderer/src/components/sidebar/worktree-list/navigation/pending-reveal-inputs.ts @@ -1,3 +1,5 @@ +import { supersedeVirtualizedScrollAnchorRestore } from '@/hooks/requestVirtualizedScrollAnchorRecord' +import { getSidebarRowRevealAncestorKeys } from './reveal-ancestors' import type { WorktreeOccurrencePlan } from '../../../../../../shared/worktree-occurrence-plan' import type React from 'react' import type { Virtualizer } from '@tanstack/react-virtual' @@ -177,3 +179,31 @@ export function resolvePendingWorktreeRevealTarget( }) return { targetIndex, outcome } } + +export function preparePendingSidebarRowReveal( + args: PendingSidebarRevealArgs, + request: PendingSidebarRowReveal +): boolean { + const isProjectHeaderTarget = + request.rowKey.startsWith('project-group:') || + request.rowKey.startsWith('project:') || + request.rowKey.startsWith('repo:') + if (isProjectHeaderTarget && args.groupBy !== 'repo') { + return false + } + + supersedeVirtualizedScrollAnchorRestore(args.scrollRef.current) + let toggledAncestor = false + for (const groupKey of getSidebarRowRevealAncestorKeys({ + rowKey: request.rowKey, + repoMap: args.repoMap, + projectGroups: args.projectGroups, + projectGrouping: args.projectGrouping + })) { + if (args.collapsedGroups.has(groupKey)) { + args.toggleGroup(groupKey) + toggledAncestor = true + } + } + return !toggledAncestor +} diff --git a/src/renderer/src/components/sidebar/worktree-list/navigation/render-row-lookup.ts b/src/renderer/src/components/sidebar/worktree-list/navigation/render-row-lookup.ts index aac307e610d..eb10d3b9c7a 100644 --- a/src/renderer/src/components/sidebar/worktree-list/navigation/render-row-lookup.ts +++ b/src/renderer/src/components/sidebar/worktree-list/navigation/render-row-lookup.ts @@ -40,12 +40,6 @@ export function getRenderRowSidebarKey(row: RenderRow): string | null { } export function rowKeyMatchesRenderRow(row: RenderRow, rowKey: string): boolean { - if (row.type === 'lineage-group') { - return ( - row.rows.some((item) => item.rowKey === rowKey) || - (row.folder !== undefined && rowKeyMatchesRenderRow(row.folder, rowKey)) - ) - } if (row.type === 'folder-workspace' && rowKey === folderWorkspaceKey(row.folderWorkspace.id)) { return true } @@ -81,13 +75,6 @@ export function renderRowContainsWorktree( composeWorktreeHostIdentity(executionHostId, worktreeId)) ) } - if (row.type === 'lineage-group') { - return ( - row.rows.some((item) => itemMatchesWorktree(item, worktreeId, executionHostId)) || - (row.folder !== undefined && - renderRowContainsWorktree(row.folder, worktreeId, executionHostId)) - ) - } return row.type === 'item' && itemMatchesWorktree(row, worktreeId, executionHostId) } @@ -96,9 +83,6 @@ export function getRenderRowWorktreeItem( worktreeId: string, executionHostId?: ExecutionHostId ): WorktreeItemRow | null { - if (row.type === 'lineage-group') { - return row.rows.find((item) => itemMatchesWorktree(item, worktreeId, executionHostId)) ?? null - } return row.type === 'item' && itemMatchesWorktree(row, worktreeId, executionHostId) ? row : null } @@ -135,8 +119,7 @@ export function findPreferredRenderRowIndexForWorktreeIdentity( for (let index = 0; index < renderRows.length; index++) { const row = renderRows[index] // Host-qualified reveals include folder rows, not just worktree item rows. - const folderRow = - row.type === 'folder-workspace' ? row : row.type === 'lineage-group' ? row.folder : undefined + const folderRow = row.type === 'folder-workspace' ? row : undefined if ( folderRow && folderWorkspaceKey(folderRow.folderWorkspace.id) === worktree.id && @@ -148,7 +131,7 @@ export function findPreferredRenderRowIndexForWorktreeIdentity( if (row.type === 'folder-workspace') { continue } - const itemRows = row.type === 'lineage-group' ? row.rows : row.type === 'item' ? [row] : [] + const itemRows = row.type === 'item' ? [row] : [] const itemRow = itemRows.find( (candidate) => getWorktreeHostIdentity(candidate.worktree) === identity ) diff --git a/src/renderer/src/components/sidebar/worktree-list/navigation/use-pending-reveal.ts b/src/renderer/src/components/sidebar/worktree-list/navigation/use-pending-reveal.ts index 30f6f9b7ec8..081d80daf65 100644 --- a/src/renderer/src/components/sidebar/worktree-list/navigation/use-pending-reveal.ts +++ b/src/renderer/src/components/sidebar/worktree-list/navigation/use-pending-reveal.ts @@ -2,18 +2,20 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react import { toast } from 'sonner' import { useAppStore } from '@/store' import { translate } from '@/i18n/i18n' -import { getWorktreeOptionId } from '../rows/option-dom' -import { getRenderRowOptionId } from './active-descendant-option' +import { + requestVirtualizedScrollAnchorRecord, + supersedeVirtualizedScrollAnchorRestore +} from '@/hooks/requestVirtualizedScrollAnchorRecord' import { getRenderRowSidebarKey, rowKeyMatchesRenderRow } from './render-row-lookup' import { - findOnScreenWorktreeOption, + getPreferredMountedWorktreeOptionId, revealMountedSidebarRowElement, revealMountedWorktreeElement } from './mounted-row-reveal' -import { getSidebarRowRevealAncestorKeys } from './reveal-ancestors' import { expandGroupsForWorktreeReveal, MAX_REVEAL_RETRIES, + preparePendingSidebarRowReveal, resolvePendingWorktreeRevealTarget, type PendingSidebarRevealArgs } from './pending-reveal-inputs' @@ -48,6 +50,12 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { pinnedDisplayPolicy } = args + const scrollToReveal = useCallback( + (top: number, behavior: ScrollBehavior) => + virtualizer.scrollToOffset(top, { behavior: behavior === 'smooth' ? 'smooth' : 'auto' }), + [virtualizer] + ) + const scheduleRetryTick = useCallback( (cancelled: () => boolean) => { schedulePendingRevealFrame(() => { @@ -67,6 +75,7 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { return } const current = argsRef.current + supersedeVirtualizedScrollAnchorRestore(current.scrollRef.current) const isCurrent = () => useAppStore.getState().pendingRevealWorktree === pendingRevealWorktree if ( !pendingRevealWorktree.rowKey && @@ -85,13 +94,13 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { return } const currentRows = argsRef.current.renderRows - const rowKey = pendingRevealWorktree.rowKey const { targetIndex, outcome } = resolvePendingWorktreeRevealTarget( argsRef.current, pendingRevealWorktree ) if (outcome === 'clear') { pendingRevealRetryRef.current = null + requestVirtualizedScrollAnchorRecord(argsRef.current.scrollRef.current) clearPendingRevealWorktreeId(pendingRevealWorktree) return } @@ -100,18 +109,14 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { } const targetRow = currentRows[targetIndex] const container = argsRef.current.scrollRef.current - const { worktreeId, executionHostId } = pendingRevealWorktree - const targetOptionId = getRenderRowOptionId(targetRow, worktreeId, executionHostId) const revealedOption = container ? revealMountedWorktreeElement( container, - worktreeId, + pendingRevealWorktree.worktreeId, pendingRevealWorktree.behavior, - rowKey - ? getWorktreeOptionId(rowKey) - : (findOnScreenWorktreeOption(container, worktreeId, executionHostId, targetOptionId) - ?.id ?? targetOptionId), - markRevealScroll + getPreferredMountedWorktreeOptionId(container, targetRow, pendingRevealWorktree), + markRevealScroll, + scrollToReveal ) : null if (revealedOption) { @@ -129,11 +134,13 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { }) } pendingRevealRetryRef.current = null + requestVirtualizedScrollAnchorRecord(argsRef.current.scrollRef.current) clearPendingRevealWorktreeId(pendingRevealWorktree) return } // Why: virtual indexing can leave the card edge clipped; stage it into the window, then retry the exact DOM reveal. + supersedeVirtualizedScrollAnchorRestore(argsRef.current.scrollRef.current) virtualizer.scrollToIndex(targetIndex, { align: 'auto', behavior: 'auto' }) if (!isCurrent()) { return @@ -150,6 +157,7 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { return } pendingRevealRetryRef.current = null + requestVirtualizedScrollAnchorRecord(argsRef.current.scrollRef.current) clearPendingRevealWorktreeId(pendingRevealWorktree) }) return () => { @@ -183,6 +191,7 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { flashRevealedRow, markRevealScroll, setRenamingWorktreeId, + scrollToReveal, schedulePendingRevealFrame, cancelPendingRevealFrames, scheduleRetryTick @@ -195,31 +204,10 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { ) { return } - const current = argsRef.current const isCurrent = () => useAppStore.getState().pendingRevealSidebarRow === pendingRevealSidebarRow - const isProjectHeaderTarget = - pendingRevealSidebarRow.rowKey.startsWith('project-group:') || - pendingRevealSidebarRow.rowKey.startsWith('project:') || - pendingRevealSidebarRow.rowKey.startsWith('repo:') - if (isProjectHeaderTarget && current.groupBy !== 'repo') { - return - } - - let toggledAncestor = false - for (const groupKey of getSidebarRowRevealAncestorKeys({ - rowKey: pendingRevealSidebarRow.rowKey, - repoMap: current.repoMap, - projectGroups: current.projectGroups, - projectGrouping: current.projectGrouping - })) { - if (current.collapsedGroups.has(groupKey)) { - current.toggleGroup(groupKey) - toggledAncestor = true - } - } - if (toggledAncestor) { + if (!preparePendingSidebarRowReveal(argsRef.current, pendingRevealSidebarRow)) { return } @@ -250,6 +238,7 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { return } pendingRowRevealRetryRef.current = null + requestVirtualizedScrollAnchorRecord(argsRef.current.scrollRef.current) clearPendingRevealSidebarRow(pendingRevealSidebarRow) toast.error( translate( @@ -266,7 +255,8 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { container, pendingRevealSidebarRow.rowKey, pendingRevealSidebarRow.behavior, - markRevealScroll + markRevealScroll, + scrollToReveal ) : null if (revealedElement) { @@ -274,10 +264,12 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { flashRevealedRow(pendingRevealSidebarRow.rowKey) } pendingRowRevealRetryRef.current = null + requestVirtualizedScrollAnchorRecord(argsRef.current.scrollRef.current) clearPendingRevealSidebarRow(pendingRevealSidebarRow) return } + supersedeVirtualizedScrollAnchorRestore(argsRef.current.scrollRef.current) virtualizer.scrollToIndex(targetIndex, { align: 'auto', behavior: 'auto' }) if (!isCurrent()) { return @@ -286,6 +278,7 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { return } pendingRowRevealRetryRef.current = null + requestVirtualizedScrollAnchorRecord(argsRef.current.scrollRef.current) clearPendingRevealSidebarRow(pendingRevealSidebarRow) }) @@ -306,6 +299,7 @@ export function usePendingSidebarReveal(args: PendingSidebarRevealArgs): void { pendingRevealRetryTick, flashRevealedRow, markRevealScroll, + scrollToReveal, clearPendingRevealSidebarRow, schedulePendingRevealFrame, cancelPendingRevealFrames, diff --git a/src/renderer/src/components/sidebar/worktree-list/navigation/worktree-sidebar-reveal.ts b/src/renderer/src/components/sidebar/worktree-list/navigation/worktree-sidebar-reveal.ts index 2ba77737a3f..e8e996b7cc3 100644 --- a/src/renderer/src/components/sidebar/worktree-list/navigation/worktree-sidebar-reveal.ts +++ b/src/renderer/src/components/sidebar/worktree-list/navigation/worktree-sidebar-reveal.ts @@ -1,10 +1,16 @@ import { GROUP_HEADER_ROW_HEIGHT } from '../viewport/virtual-rows' import { motionSafeScrollBehavior } from '@/hooks/usePrefersReducedMotion' +import { + requestVirtualizedScrollAnchorRecord, + supersedeVirtualizedScrollAnchorRestore +} from '@/hooks/requestVirtualizedScrollAnchorRecord' const WORKTREE_REVEAL_TOP_CLEARANCE = 6 export const WORKTREE_SIDEBAR_REVEAL_TOP_INSET = GROUP_HEADER_ROW_HEIGHT + WORKTREE_REVEAL_TOP_CLEARANCE +export type SidebarRevealScroll = (top: number, behavior: ScrollBehavior) => void + type SidebarRevealBounds = { start: number end: number @@ -89,7 +95,8 @@ export function revealElementInScrollContainer( element: Element, behavior: ScrollBehavior, // Notify guards to defer competing scrollTop writes until this smooth reveal finishes. - onScrollIssued?: (targetTop: number) => void + onScrollIssued?: (targetTop: number) => void, + scrollTo?: SidebarRevealScroll ): boolean { if (!container.contains(element)) { return false @@ -104,11 +111,20 @@ export function revealElementInScrollContainer( WORKTREE_SIDEBAR_REVEAL_TOP_INSET ) if (nextScrollTop === null) { + supersedeVirtualizedScrollAnchorRestore(container) + requestVirtualizedScrollAnchorRecord(container) return true } const targetTop = Math.max(0, nextScrollTop) + supersedeVirtualizedScrollAnchorRestore(container, scrollTo ? undefined : targetTop) onScrollIssued?.(targetTop) - container.scrollTo({ top: targetTop, behavior: motionSafeScrollBehavior(behavior) }) + const scrollBehavior = motionSafeScrollBehavior(behavior) + if (scrollTo) { + scrollTo(targetTop, scrollBehavior) + } else { + container.scrollTo({ top: targetTop, behavior: scrollBehavior }) + } + requestVirtualizedScrollAnchorRecord(container) return true } diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/folder-row.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/folder-row.tsx index a0460ed7e55..1435ff21573 100644 --- a/src/renderer/src/components/sidebar/worktree-list/rows/folder-row.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/folder-row.tsx @@ -28,6 +28,7 @@ import { getFolderWorkspaceCardPrDisplay } from '../../workspace-card/review/fol import { FolderPathStatusIndicator } from './FolderPathStatusIndicator' import type { WorktreeItemRowContext } from './item-row' import type { FolderWorkspaceItemRow } from '../listing/renderable-rows' +import type { LineageRowFrame } from '../listing/lineage-row-projection' import { forwardOptionPressToCardSurface, getWorktreeOptionId } from './option-dom' export type FolderWorkspaceRowContext = { @@ -85,10 +86,11 @@ export function renderFolderWorkspaceOption(args: { ctx: FolderWorkspaceRowContext row: FolderWorkspaceItemRow vItem?: VirtualItem + frame?: LineageRowFrame measureVirtualRowElement?: (element: HTMLDivElement | null) => void lineage?: FolderRowLineage }): React.JSX.Element { - const { ctx, row, vItem, lineage } = args + const { ctx, row, vItem, lineage, frame } = args const folderWorktree = folderWorkspaceToWorktree(row.folderWorkspace) const folderWorktreeIdentity = getWorktreeHostIdentity(folderWorktree) const isActiveWorktree = isActiveSidebarWorkspace(folderWorktree, { @@ -113,7 +115,7 @@ export function renderFolderWorkspaceOption(args: { prCache: ctx.prCache, settings: ctx.settings }) - const { surfaceInset, cardContentIndent } = getFolderWorkspaceRowGeometry({ + const geometry = getFolderWorkspaceRowGeometry({ experimentalNewWorktreeCardStyle: ctx.newCardStyle, isFolderBackedWorkspaceChild: ctx.groupBy === 'repo' && row.projectGroup.createdFrom === 'folder-scan', @@ -121,6 +123,8 @@ export function renderFolderWorkspaceOption(args: { groupDepth: row.groupDepth, lineageDepth: row.depth }) + const surfaceInset = frame?.left ?? geometry.surfaceInset + const cardContentIndent = frame?.contentIndent ?? geometry.cardContentIndent return (
ctx.onRowPointerDown(event, folderWorktree, folderWorktreeIdentity)} > -
0 ? { paddingLeft: surfaceInset } : undefined} - > +
-
+
diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx index 7cd22f008d3..e3e8706004e 100644 --- a/src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx @@ -10,15 +10,8 @@ import WorktreeCard from '../../workspace-card/surface/WorktreeCard' import type { ActiveSurfaceVariant } from '../../workspace-card/controller/worktree-card-model' import { PINNED_GROUP_KEY } from '../../../../../../shared/sidebar-list/group-keys' import type { WorktreeGroupBy } from '../../../../../../shared/sidebar-list/row-types' -import { - getLineageChildrenInlineStyle, - getWorktreeCardContentIndent, - getWorktreeCardSurfaceInset, - LINEAGE_CHILDREN_INLINE_OFFSET, - LINEAGE_NESTED_ROW_SURFACE_INSET -} from './indentation' import type { LineageToggleHandler } from '../../workspace-card/identity/worktree-lineage-toggle-handler-cache' -import { stopNestedWorktreeCardBubble } from './header-event-guards' +import type { LineageRowFrame } from '../listing/lineage-row-projection' import type { WorktreeItemRow } from '../listing/renderable-rows' import { forwardOptionPressToCardSurface, getWorktreeOptionId } from './option-dom' import type { WorktreeRowDragState } from '../drag/row-state' @@ -60,41 +53,13 @@ export type WorktreeItemRowContext = { onCardDragEnd: () => void } -// Handle plain rows and inherited lineage offsets separately. -function getWorktreeItemRowGeometry( - ctx: WorktreeItemRowContext, - itemRow: WorktreeItemRow, - nested: boolean -): { surfaceInset: number; cardContentIndent: number } { - // Why: the shared child wrapper already inherits the parent's group and content offsets. - if (nested) { - return { surfaceInset: LINEAGE_NESTED_ROW_SURFACE_INSET, cardContentIndent: 0 } - } - - const isGrouped = ctx.groupBy !== 'none' - const paddingLeft = getWorktreeCardContentIndent({ - isGrouped, - groupDepth: itemRow.groupDepth, - lineageDepth: itemRow.depth - }) - const surfaceInset = getWorktreeCardSurfaceInset({ isGrouped, groupDepth: itemRow.groupDepth }) - return { - surfaceInset, - cardContentIndent: Math.max(0, paddingLeft - surfaceInset) - } -} - export function renderWorktreeItemRow( ctx: WorktreeItemRowContext, itemRow: WorktreeItemRow, - nested: boolean, - lineageChildren?: React.ReactNode, + frame: LineageRowFrame, forceActiveSurface = false ): React.JSX.Element { - const { surfaceInset, cardContentIndent } = getWorktreeItemRowGeometry(ctx, itemRow, nested) - const lineageChildrenStyle = lineageChildren - ? getLineageChildrenInlineStyle(LINEAGE_CHILDREN_INLINE_OFFSET) - : undefined + const { left: surfaceInset, right, contentIndent: cardContentIndent } = frame const worktreeDragGroupKey = ctx.groupKeyByRowKey.get(itemRow.rowKey) const worktreeIdentity = getWorktreeHostIdentity(itemRow.worktree) const isMultiSelected = ctx.selectedWorktreeIds.has(worktreeIdentity) @@ -130,24 +95,14 @@ export function renderWorktreeItemRow( data-scroll-reveal-highlight={ ctx.highlightedRevealRowKey === itemRow.rowKey ? 'true' : undefined } - onClick={(event) => { - forwardOptionPressToCardSurface(event) - // Why: nested child cards live inside the parent's clickable body; bubbling would activate/edit the parent too. - if (nested) { - event.stopPropagation() - } - }} + onClick={forwardOptionPressToCardSurface} onClickCapture={ctx.onRowClickCapture} - onDoubleClick={nested ? stopNestedWorktreeCardBubble : undefined} - onDragStart={nested ? stopNestedWorktreeCardBubble : undefined} onPointerDown={(event) => { - if (nested) { - event.stopPropagation() - } ctx.onRowPointerDown(event, itemRow.worktree, itemRow.rowKey) }} style={{ - paddingLeft: surfaceInset > 0 ? `${surfaceInset}px` : undefined + paddingLeft: surfaceInset > 0 ? `${surfaceInset}px` : undefined, + paddingRight: right || undefined }} > ) } - -// Rebuild the visible lineage subtree so each card renders its own children inline. -export function renderWorktreeLineageDescendants( - ctx: WorktreeItemRowContext, - parent: { depth: number }, - descendants: readonly WorktreeItemRow[] -): React.ReactNode | undefined { - const childNodes: React.ReactNode[] = [] - let cursor = 0 - while (cursor < descendants.length) { - const child = descendants[cursor] - if (!child || child.depth !== parent.depth + 1) { - cursor++ - continue - } - - let nextSiblingIndex = cursor + 1 - while ( - nextSiblingIndex < descendants.length && - descendants[nextSiblingIndex]!.depth > child.depth - ) { - nextSiblingIndex++ - } - - const childLineageChildren = renderWorktreeLineageDescendants( - ctx, - child, - descendants.slice(cursor + 1, nextSiblingIndex) - ) - childNodes.push(renderWorktreeItemRow(ctx, child, true, childLineageChildren)) - cursor = nextSiblingIndex - } - return childNodes.length > 0 ? childNodes : undefined -} diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx index 47b694954a7..048321be415 100644 --- a/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx @@ -7,6 +7,7 @@ import type { } from '../../../../../../shared/worktree/types' import '../../workspace-status/presentation/workspace-status' import { getWorkspaceStatus } from '../../../../../../shared/workspace-statuses' +import { isActiveSidebarWorkspace } from '../../../../../../shared/sidebar-list/active-workspace' import type { WorktreeGroupBy } from '../../../../../../shared/sidebar-list/row-types' import { getWorktreeHostIdentity } from '../../../../../../shared/worktree/host-qualified-identity' import { @@ -14,33 +15,30 @@ import { getWorktreeVirtualRowTransform, shouldUseHeaderTopSpacing } from '../viewport/virtual-rows' +import type { LineageRowProjection } from '../listing/lineage-row-projection' import type { RenderRow } from '../listing/render-row' import type { ImportedWorktreeCardActionState } from '../listing/imported-worktrees-card-actions' import type { NewExternalWorktreesInboxActionState } from '../listing/new-external-worktrees-inbox-actions' import type { useHostHeaderDrag } from '../drag/host-header-drag' import { HostSectionHeader } from './HostSectionHeader' -import { - renderFolderWorkspaceOption, - renderFolderWorkspaceVirtualRow, - type FolderWorkspaceRowContext -} from './folder-row' +import { renderFolderWorkspaceOption, type FolderWorkspaceRowContext } from './folder-row' import type { LineageToggleHandler } from '../../workspace-card/identity/worktree-lineage-toggle-handler-cache' import { renderImportedWorktreesVirtualRow, renderNewExternalWorktreesInboxVirtualRow, renderPendingCreationVirtualRow } from './notice-rows' -import { - renderWorktreeItemRow, - renderWorktreeLineageDescendants, - type WorktreeItemRowContext -} from './item-row' +import { renderWorktreeItemRow, type WorktreeItemRowContext } from './item-row' import { renderWorktreeSectionHeaderRow } from './SectionHeader' import type { SectionHeaderRowContext } from './section-header-row-context' import type { WorktreeRowDragState } from '../drag/row-state' import type { Translate } from '@/i18n/use-translate' export type WorktreeVirtualRowContext = { + dragUnitsByRow: ReadonlyMap + hiddenDragRanges: readonly { start: number; end: number }[] + projection: LineageRowProjection + activeFamilyRoots: ReadonlySet renderRows: RenderRow[] firstHeaderIndex: number activeStickyHeaderIndexRef: React.MutableRefObject @@ -119,63 +117,7 @@ function folderLineageToggle( return row.lineageGroupKey ? ctx.item.getLineageToggleHandler(row.lineageGroupKey) : undefined } -function renderLineageGroupVirtualRow( - ctx: WorktreeVirtualRowContext, - row: Extract, - vItem: VirtualItem -): React.JSX.Element { - const parent = row.folder ? undefined : row.rows[0] - const children = row.folder ? row.rows : row.rows.slice(1) - const childIsActive = children.some((child) => child.worktree.id === ctx.activeWorktreeId) - const parentKey = parent ? getWorktreeHostIdentity(parent.worktree) : null - const parentPreviewOffset = parentKey - ? (ctx.worktreeDragState.previewOffsetsByWorktreeId.get(parentKey) ?? 0) - : 0 - return ( -
-
- {row.folder - ? renderFolderWorkspaceOption({ - ctx: ctx.folderWorkspace, - row: row.folder, - lineage: { - onToggle: folderLineageToggle(ctx, row.folder), - children: renderWorktreeLineageDescendants(ctx.item, row.folder, children), - childIsActive - } - }) - : parent - ? renderWorktreeItemRow( - ctx.item, - parent, - false, - renderWorktreeLineageDescendants(ctx.item, parent, children), - childIsActive - ) - : null} -
-
- ) -} - -// Each row renderer owns its frame because sticky headers, lineage groups and plain cards position differently. +// Header frames retain sticky positioning; each workspace owns its measured slot. export function renderWorktreeVirtualRow( ctx: WorktreeVirtualRowContext, row: RenderRow, @@ -205,10 +147,6 @@ export function renderWorktreeVirtualRow( ) } - if (row.type === 'lineage-group') { - return renderLineageGroupVirtualRow(ctx, row, vItem) - } - if (row.type === 'imported-worktrees-card') { return renderImportedWorktreesVirtualRow({ row, @@ -239,22 +177,37 @@ export function renderWorktreeVirtualRow( }) } - if (row.type === 'folder-workspace') { - return renderFolderWorkspaceVirtualRow({ - ctx: ctx.folderWorkspace, - row, - vItem, - measureVirtualRowElement: ctx.measureVirtualRowElement, - lineage: { onToggle: folderLineageToggle(ctx, row) } - }) - } - + const frame = ctx.projection.frames[vItem.index] + const folder = row.type === 'folder-workspace' const itemWorkspaceStatus = - ctx.groupBy === 'workspace-status' + !folder && ctx.groupBy === 'workspace-status' ? getWorkspaceStatus(row.worktree, ctx.workspaceStatuses) : null - const itemKey = getWorktreeHostIdentity(row.worktree) - const itemPreviewOffset = ctx.worktreeDragState.previewOffsetsByWorktreeId.get(itemKey) ?? 0 + const itemKey = folder ? null : getWorktreeHostIdentity(row.worktree) + const sourceHidden = + itemKey !== null && + ctx.worktreeDragState.draggingWorktreeId !== null && + ctx.hiddenDragRanges.some(({ start, end }) => vItem.index >= start && vItem.index <= end) + const itemPreviewOffset = + (itemKey + ? ctx.worktreeDragState.previewOffsetsByWorktreeId.get( + ctx.dragUnitsByRow.get(vItem.index)?.identity ?? itemKey + ) + : 0) ?? 0 + const activeFamily = ctx.activeFamilyRoots.has(frame.root) + const rootFrame = ctx.projection.frames[frame.root] + const rootRow = ctx.renderRows[frame.root] + const familyHidden = ctx.hiddenDragRanges.some( + ({ start, end }) => frame.root >= start && frame.root <= end + ) + const familyVariant = + rootRow.type === 'item' && + isActiveSidebarWorkspace(rootRow.worktree, { + worktreeId: ctx.item.activeWorktreeId, + hostId: ctx.item.activeWorkspaceExecutionHostId + }) + ? ctx.item.getActiveSurfaceVariant(rootRow) + : 'primary' return (
ctx.onWorkspaceStatusDragOver(event, itemWorkspaceStatus) @@ -286,7 +241,44 @@ export function renderWorktreeVirtualRow( : undefined } > - {renderWorktreeItemRow(ctx.item, row, false)} +
+ {activeFamily && ( +
) } diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/VirtualizedWorktreeViewport.tsx b/src/renderer/src/components/sidebar/worktree-list/viewport/VirtualizedWorktreeViewport.tsx index 892f2e88d49..d9bfbdd7123 100644 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/VirtualizedWorktreeViewport.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/VirtualizedWorktreeViewport.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useMemo, useRef, useState } from 'react' import { useAppStore } from '@/store' +import { createProgrammaticScrollMarks } from '@/hooks/programmatic-scroll-marks' import { selectSidebarLineageParentIdentities } from '@/store/slices/worktrees/metadata/cross-store-lineage-parents' import { useProjectHeaderHostContexts } from './use-project-header-host-contexts' import { useTranslate } from '@/i18n/use-translate' @@ -8,7 +9,7 @@ import { WorktreeListScrollToTopButton } from './WorktreeListScrollToTopButton' import { renderWorktreeSidebarDropIndicators } from './drop-indicators' import { useWorktreeListScrollToTop } from './use-scroll-to-top' import { getActiveDescendantOptionId } from '../navigation/active-descendant-option' -import { buildRenderableRows } from '../listing/renderable-rows' +import { useDraggedSubtreeRanges, useLineageRowProjection } from '../listing/lineage-row-projection' import { useFolderWorkspacePathStatusRows } from '../listing/use-folder-path-statuses' import { useGroupToggleWithScrollAnchor } from './use-group-toggle' import { usePendingSidebarReveal } from '../navigation/use-pending-reveal' @@ -21,8 +22,9 @@ import { useWorktreeDragRuntime } from '../drag/use-runtime' import { useWorktreeDragSession } from '../drag/use-session' import { useWorktreeDocumentDrop } from '../drag/use-document-drop' import { useWorktreeLineageDropCommit } from '../drag/use-lineage-drop-commit' -import { resolveSidebarWorkspaceDeleteIntent } from '../navigation/sidebar-workspace-delete' -import { useWorktreeListKeyboardNavigation, withKeyboardPreview } from '../navigation/use-keyboard' +import { withKeyboardPreview } from '../navigation/use-keyboard' +import { useWorktreeViewportKeyboardNavigation } from './use-keyboard-navigation' +import { useWorktreeRowGeometry } from './use-row-geometry' import { useWorktreeListVirtualizer } from './use-virtualizer' import { useWorktreeNativeDrag } from '../drag/use-native-drag' import { useWorktreePointerDrag } from '../drag/use-pointer-drag' @@ -113,14 +115,23 @@ export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktr const reveal = useSidebarRevealHighlight() const scrollSuppression = useWorktreeSidebarScrollSuppression(scrollRef) - const { markDirectScrollInput, markScrollMovement } = scrollSuppression + const { markDirectScrollInput } = scrollSuppression + const programmaticScrollMarks = useMemo(() => createProgrammaticScrollMarks(), []) - const renderRows = useMemo(() => buildRenderableRows(rows), [rows]) - const firstHeaderIndex = useMemo( - () => renderRows.findIndex((row) => row.type === 'header' || row.type === 'host-header'), - [renderRows] + const { handleContainerKeyDown, handleContainerKeyUp, handleContainerBlur, keyboardPreview } = + useWorktreeViewportKeyboardNavigation({ props, scrollRef, markDirectScrollInput }) + + const rowProps = useMemo( + () => withKeyboardPreview(props, keyboardPreview), + [keyboardPreview, props] ) + const renderRows = rows + const { projection, activeFamilyRoots, firstHeaderIndex } = useLineageRowProjection({ + ...rowProps, + newCardStyle + }) + const headerDrag = useWorktreeSidebarHeaderDrag({ rows, renderRows, @@ -133,16 +144,27 @@ export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktr scrollRef, onReorderHostSections: props.onReorderHostSections, onHostDragActiveChange: props.onHostDragActiveChange, - suppressMeasurementAdjustmentUntilRef: scrollSuppression.suppressMeasurementAdjustmentUntilRef, - directScrollInputUntilRef: scrollSuppression.directScrollInputUntilRef + onScrollIntent: markDirectScrollInput }) - const session = useWorktreeDragSession({ rows, scrollRef }) + const virtualization = useWorktreeListVirtualizer({ + renderRows, + projection, + firstHeaderIndex, + scrollRef, + scrollOffsetRef, + programmaticScrollMarks + }) + const rowGeometry = useWorktreeRowGeometry({ rows, projection, virtualization, scrollRef }) + + const session = useWorktreeDragSession({ rows, scrollRef, geometry: rowGeometry }) const lineageDrop = useWorktreeLineageDropCommit({ repoMap, worktreeMap, worktreeLineageById, - worktreeDragGroups: session.worktreeDragGroups + worktreeDragGroups: session.worktreeDragGroups, + keepsFolderParent: rowGeometry.keepsFolderParent, + captureFolderRetention: rowGeometry.captureFolderRetention }) const runtime = useWorktreeDragRuntime({ worktreeDragSessionRef: session.worktreeDragSessionRef, @@ -170,14 +192,6 @@ export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktr onUserScrollIntent: markDirectScrollInput }) - const virtualization = useWorktreeListVirtualizer({ - renderRows, - firstHeaderIndex, - scrollRef, - scrollOffsetRef, - suppressMeasurementAdjustmentUntilRef: scrollSuppression.suppressMeasurementAdjustmentUntilRef - }) - usePendingSidebarReveal({ occurrencePlan: props.occurrencePlan, pendingRevealWorktree: props.pendingRevealWorktree, @@ -216,7 +230,6 @@ export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktr scrollRef, scrollOffsetRef, scrollAnchorRef, - hasDirectScrollInput: scrollSuppression.hasDirectScrollInput, shouldSkipScrollAnchorRestore: scrollSuppression.shouldSkipScrollAnchorRestore }) @@ -225,26 +238,6 @@ export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktr toggleGroup }) - const { handleContainerKeyDown, handleContainerKeyUp, handleContainerBlur, keyboardPreview } = - useWorktreeListKeyboardNavigation({ - rows, - activeWorktreeId, - activeWorkspaceExecutionHostId: props.activeWorkspaceExecutionHostId, - pinnedDisplayPolicy, - scrollRef, - activeModal: props.activeModal, - markDirectScrollInput, - onKeyboardSelect: props.onKeyboardSelect, - focusedWorktree: props.focusedWorktree, - focusedWorkspaceExecutionHostId: props.focusedWorkspaceExecutionHostId, - resolveDeleteIntent: () => - resolveSidebarWorkspaceDeleteIntent({ - selectedWorktrees: props.selectedWorktrees, - focusedWorktree: props.focusedWorktree ?? null, - repoMap - }) - }) - const dropCtx = useWorktreeDropCommitContext({ scrollRef, workspaceStatuses, @@ -262,7 +255,7 @@ export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktr session, runtime, scrollRef, - markScrollMovement, + markScrollMovement: scrollSuppression.cancelRevealScroll, selectedWorktreeIds: props.selectedWorktreeIds, selectedWorktrees: props.selectedWorktrees, workspaceBoardOpen: props.workspaceBoardOpen, @@ -276,7 +269,7 @@ export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktr session, runtime, scrollRef, - markScrollMovement + markScrollMovement: scrollSuppression.cancelRevealScroll }) useWorktreeDocumentDrop({ ctx: dropCtx, @@ -340,12 +333,14 @@ export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktr [markDirectScrollInput] ) - const rowProps = useMemo( - () => withKeyboardPreview(props, keyboardPreview), - [keyboardPreview, props] - ) + const draggingIdentity = runtime.worktreeDragState.draggingWorktreeId + const hiddenDragRanges = useDraggedSubtreeRanges(projection, draggingIdentity) const rowContext = buildWorktreeVirtualRowContext({ + dragUnitsByRow: rowGeometry.ranges.byRow, + hiddenDragRanges, props: rowProps, + projection, + activeFamilyRoots, renderRows, firstHeaderIndex, virtualization, @@ -396,8 +391,6 @@ export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktr onKeyDown={handleContainerKeyDown} onKeyUp={handleContainerKeyUp} onBlur={handleContainerBlur} - // Why: trackpad momentum fires sparse scroll events after the input stream quiets; suppress correction until the viewport stops. - onScroll={markScrollMovement} onPointerDown={handleScrollPointerDown} onTouchMove={markDirectScrollInput} onWheel={markDirectScrollInput} @@ -408,6 +401,7 @@ export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktr >
diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/sidebar-smooth-reveal.test.tsx b/src/renderer/src/components/sidebar/worktree-list/viewport/sidebar-smooth-reveal.test.tsx new file mode 100644 index 00000000000..b51299619e2 --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/sidebar-smooth-reveal.test.tsx @@ -0,0 +1,170 @@ +// @vitest-environment happy-dom +import { act, useMemo } from 'react' +import { cleanup, renderHook } from '@testing-library/react' +import { afterEach, beforeEach, expect, it, vi } from 'vitest' +import { createProgrammaticScrollMarks } from '@/hooks/programmatic-scroll-marks' +import type { VirtualizedScrollAnchor } from '@/hooks/useVirtualizedScrollAnchor' +import { supersedeVirtualizedScrollAnchorRestore } from '@/hooks/requestVirtualizedScrollAnchorRecord' +import { buildLineageRowProjection } from '../listing/lineage-row-projection' +import type { RenderRow } from '../listing/render-row' +import { repo, worktree } from '../grouping/worktree-list-groups-test-fixtures' +import { revealElementInScrollContainer } from '../navigation/worktree-sidebar-reveal' +import { useWorktreeListVirtualizer } from './use-virtualizer' +import { useVirtualRowMeasurementSync } from './use-row-measurement' +import { useWorktreeSidebarScrollSuppression } from './use-scroll-suppression' + +const caches = vi.hoisted(() => ({ prCache: {}, issueCache: {} })) +vi.mock('@/store', () => ({ + useAppStore: (selector: (state: typeof caches) => unknown) => selector(caches) +})) +beforeEach(() => { + vi.useFakeTimers() + vi.spyOn(window, 'requestAnimationFrame').mockReturnValue(1) + vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => {}) +}) +afterEach(() => { + cleanup() + document.body.replaceChildren() + vi.restoreAllMocks() + vi.useRealTimers() +}) + +function setup() { + const scroller = document.createElement('div') + document.body.append(scroller) + Object.defineProperties(scroller, { + clientHeight: { value: 300 }, + offsetHeight: { value: 300 }, + offsetWidth: { value: 300 }, + scrollHeight: { value: 10000 } + }) + scroller.getBoundingClientRect = () => new DOMRect(0, 0, 300, 300) + scroller.scrollTo = (options?: ScrollToOptions | number) => { + if (typeof options === 'object' && options.behavior !== 'smooth') { + scroller.scrollTop = options.top ?? scroller.scrollTop + } + } + const rows: RenderRow[] = Array.from({ length: 60 }, (_, index) => ({ + type: 'item', + rowKey: String(index), + sectionKey: 'all', + repo, + worktree: { ...worktree, id: String(index) }, + depth: 0, + groupDepth: 0, + isLastLineageChild: false, + lineageChildCount: 0 + })) + const scrollRef = { current: scroller } + const scrollOffsetRef = { current: 0 } + const scrollAnchorRef: { current: VirtualizedScrollAnchor } = { current: null } + const marks = createProgrammaticScrollMarks() + const view = renderHook(() => { + const projection = useMemo(() => buildLineageRowProjection(rows, 'none', false), []) + const virtualization = useWorktreeListVirtualizer({ + renderRows: rows, + projection, + firstHeaderIndex: -1, + scrollRef, + scrollOffsetRef, + programmaticScrollMarks: marks + }) + const input = useWorktreeSidebarScrollSuppression(scrollRef) + useVirtualRowMeasurementSync({ + renderRows: rows, + virtualization, + scrollRef, + scrollOffsetRef, + scrollAnchorRef, + shouldSkipScrollAnchorRestore: input.shouldSkipScrollAnchorRestore + }) + return { ...virtualization, input } + }) + const mount = (index: number, height: number) => { + const element = document.createElement('div') + element.setAttribute('data-index', String(index)) + element.setAttribute('data-worktree-virtual-row', '') + element.setAttribute('data-worktree-virtual-row-key', `wt:${index}`) + Object.defineProperty(element, 'offsetHeight', { value: height }) + scroller.append(element) + act(() => view.result.current.virtualizer.measureElement(element)) + return element + } + return { scroller, view, mount, scrollAnchorRef } +} + +it('measures skipped mounted rows and new rows after late direct takeover without rewinding a stale offset', () => { + const { scroller, view, mount } = setup() + act(() => { + view.result.current.input.markRevealScroll(3000) + view.result.current.virtualizer.scrollToOffset(3000, { behavior: 'smooth' }) + }) + mount(0, 93) + vi.spyOn(window.performance, 'now').mockReturnValue(10000) + expect(view.result.current.input.shouldSkipScrollAnchorRestore()).toBe(false) + scroller.scrollTop = 50 + act(() => view.result.current.input.markDirectScrollInput()) + expect(scroller.scrollTop).toBe(50) + expect(view.result.current.virtualizer.measurementsCache[0].size).toBe(93) + mount(1, 115) + expect(view.result.current.virtualizer.measurementsCache[2].start).toBe(208) +}) + +it.each(['arrival', 'idle', 'replacement', 'no-op'] as const)( + 'releases measurement after %s instead of leaving the smooth target active', + (completion) => { + const { scroller, view, mount } = setup() + act(() => view.result.current.virtualizer.scrollToOffset(3000, { behavior: 'smooth' })) + mount(0, 93) + act(() => { + if (completion === 'replacement') { + view.result.current.virtualizer.scrollToIndex(0, { behavior: 'auto' }) + } else if (completion === 'no-op') { + view.result.current.virtualizer.scrollToOffset(0, { behavior: 'smooth' }) + } else { + scroller.scrollTop = completion === 'arrival' ? 3000 : 80 + scroller.dispatchEvent(new Event('scroll')) + if (completion === 'idle') { + vi.advanceTimersByTime(250) + } + } + }) + expect(view.result.current.virtualizer.measurementsCache[0].size).toBe(93) + mount(1, 115) + expect(view.result.current.virtualizer.measurementsCache[2].start).toBe(208) + view.unmount() + scroller.scrollTop = 37 + supersedeVirtualizedScrollAnchorRestore(scroller) + expect(scroller.scrollTop).toBe(37) + } +) + +it.each(['virtualizer', 'native'] as const)( + 'records later user movement back to a completed %s reveal destination', + (writer) => { + const { scroller, view, scrollAnchorRef } = setup() + const target = document.createElement('div') + target.getBoundingClientRect = () => new DOMRect(0, 350 - scroller.scrollTop, 300, 50) + scroller.append(target) + act(() => { + revealElementInScrollContainer( + scroller, + target, + 'auto', + undefined, + writer === 'virtualizer' + ? (top) => view.result.current.virtualizer.scrollToOffset(top, { behavior: 'auto' }) + : undefined + ) + scroller.dispatchEvent(new Event('scroll')) + }) + expect(scroller.scrollTop).toBe(100) + act(() => { + scroller.scrollTop = 20 + scroller.dispatchEvent(new Event('scroll')) + scroller.scrollTop = 100 + scroller.dispatchEvent(new Event('scroll')) + }) + expect(scrollAnchorRef.current?.offset).toBe(100) + } +) diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/sidebar-smooth-reveal.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/sidebar-smooth-reveal.ts new file mode 100644 index 00000000000..eda84f33e23 --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/sidebar-smooth-reveal.ts @@ -0,0 +1,90 @@ +import { elementScroll, observeElementOffset, type Virtualizer } from '@tanstack/react-virtual' +import type { ProgrammaticScrollMarks } from '@/hooks/programmatic-scroll-marks' +import { VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT } from '@/hooks/useVirtualizedScrollAnchor' + +type SidebarVirtualizer = Virtualizer +type ScrollOptions = Parameters[1] + +export function createSidebarSmoothReveal(args: { + marks: ProgrammaticScrollMarks + onRetired: () => void +}) { + let owned: { target: number } | null = null + let retirement: 'abort' | 'settled' | null = null + + const retire = (instance: SidebarVirtualizer, offset: number, abort: boolean) => { + if (!owned) { + return + } + owned = null + retirement = abort ? 'abort' : 'settled' + try { + // Replace the library operation at the live position without clamp/layout reads. + instance.scrollBy(offset - (instance.scrollOffset ?? offset), { behavior: 'auto' }) + } finally { + retirement = null + } + args.onRetired() + } + + const scrollTo = (offset: number, options: ScrollOptions, instance: SidebarVirtualizer) => { + if (retirement) { + if (retirement === 'abort') { + elementScroll(offset, options, instance) + } + return + } + const element = instance.scrollElement + const target = offset + (options.adjustments ?? 0) + if (options.behavior === 'smooth' && element) { + owned = { target } + if (target === element.scrollTop) { + retire(instance, target, false) + return + } + } else if (options.behavior !== undefined && owned) { + owned = null + args.onRetired() + } + if (element?.scrollTop !== target) { + args.marks.mark(target) + } + elementScroll(offset, options, instance) + } + + const observeOffset = ( + instance: SidebarVirtualizer, + callback: (offset: number, isScrolling: boolean) => void + ) => { + const element = instance.scrollElement + const onTakeover = (event: Event) => { + if ( + owned && + event instanceof CustomEvent && + event.detail?.cancelRestore === true && + element + ) { + retire(instance, element.scrollTop, true) + } + } + element?.addEventListener(VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT, onTakeover) + const cleanup = observeElementOffset(instance, (offset, isScrolling) => { + const observed = owned + callback(offset, isScrolling) + if ( + observed && + owned === observed && + (!isScrolling || Math.abs(offset - observed.target) < 1) + ) { + retire(instance, offset, false) + } + }) + return () => { + owned = null + element?.removeEventListener(VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT, onTakeover) + cleanup?.() + } + } + + return { scrollTo, observeOffset } +} diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/use-keyboard-navigation.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-keyboard-navigation.ts new file mode 100644 index 00000000000..ff51d14bad3 --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-keyboard-navigation.ts @@ -0,0 +1,30 @@ +import type React from 'react' +import { useWorktreeListKeyboardNavigation } from '../navigation/use-keyboard' +import { resolveSidebarWorkspaceDeleteIntent } from '../navigation/sidebar-workspace-delete' +import type { VirtualizedWorktreeViewportProps } from './viewport-props' + +export function useWorktreeViewportKeyboardNavigation(args: { + props: VirtualizedWorktreeViewportProps + scrollRef: React.RefObject + markDirectScrollInput: () => void +}) { + const { props, scrollRef, markDirectScrollInput } = args + return useWorktreeListKeyboardNavigation({ + rows: props.rows, + activeWorktreeId: props.activeWorktreeId, + activeWorkspaceExecutionHostId: props.activeWorkspaceExecutionHostId, + pinnedDisplayPolicy: props.pinnedDisplayPolicy, + scrollRef, + activeModal: props.activeModal, + markDirectScrollInput, + onKeyboardSelect: props.onKeyboardSelect, + focusedWorktree: props.focusedWorktree, + focusedWorkspaceExecutionHostId: props.focusedWorkspaceExecutionHostId, + resolveDeleteIntent: () => + resolveSidebarWorkspaceDeleteIntent({ + selectedWorktrees: props.selectedWorktrees, + focusedWorktree: props.focusedWorktree ?? null, + repoMap: props.repoMap + }) + }) +} diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-geometry.test.tsx b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-geometry.test.tsx new file mode 100644 index 00000000000..e9f8e7667d2 --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-geometry.test.tsx @@ -0,0 +1,159 @@ +// @vitest-environment happy-dom +import { cleanup, renderHook } from '@testing-library/react' +import { afterEach, expect, it, vi } from 'vitest' +import type { RenderRow } from '../listing/render-row' +import { buildLineageRowProjection } from '../listing/lineage-row-projection' +import { getReorderUnnestRows } from '../drag/worktree-lineage-drag-drop' +import { useWorktreeRowGeometry } from './use-row-geometry' +import type { WorktreeListVirtualizer } from './use-virtualizer' + +vi.mock('@/store', () => ({ useAppStore: vi.fn() })) +afterEach(cleanup) + +it('unnests only the picked host copy outside the padded folder surface, even with its header offscreen', () => { + const child = { id: 'r::/child', hostId: 'ssh:box' as const } + const twin = { id: 'r::/child', hostId: 'local' as const } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Geometry reads only the identity, depth and grouping fields supplied here. + const rows = [ + { + type: 'folder-workspace', + key: 'folder', + depth: 0, + groupDepth: 0, + folderWorkspace: { id: 'f', executionHostId: 'ssh:box' }, + projectGroup: {} + }, + { + type: 'item', + rowKey: 'child', + sectionKey: 'all', + depth: 1, + groupDepth: 0, + worktree: child, + repo: { id: 'r' } + } + ] as unknown as RenderRow[] + const container = document.createElement('div') + container.scrollTop = 200 + container.getBoundingClientRect = () => new DOMRect(100, 100, 200, 400) + const content = document.createElement('div') + content.setAttribute('data-worktree-sidebar-content', '') + content.getBoundingClientRect = () => new DOMRect(104, -99, 188, 1000) + container.append(content) + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Folder retention reads cached endpoints; drag-window methods are unused in this test. + const virtualization = { + virtualizer: { + measurementsCache: [ + { start: 0, end: 64 }, + { start: 64, end: 260 } + ] + } + } as unknown as WorktreeListVirtualizer + const { result } = renderHook(() => + useWorktreeRowGeometry({ + rows, + projection: buildLineageRowProjection(rows, 'none', false), + virtualization, + scrollRef: { current: container } + }) + ) + const folder = 'ssh:box|folder:f' + for (const [clientX, expected] of [ + [106, [child]], + [110, []], + [295, [child]] + ] as const) { + const snapshot = result.current.captureFolderRetention( + { clientX, clientY: 50, lineClientY: 48 }, + new Set([folder]) + ) + expect( + getReorderUnnestRows({ + legacyIds: [], + pickedCopies: [child, twin], + sourceGroupIdentities: ['ssh:box|r::/child', 'local|r::/child'], + folderParentOf: (identity) => (identity === 'ssh:box|r::/child' ? folder : undefined), + keepsFolderParent: (identity) => result.current.keepsFolderParent(identity, snapshot) + }) + ).toEqual(expected) + } +}) + +it('excludes a nested folder’s preceding gap and ancestor closing edge from its saved-parent surface', () => { + const child = { id: 'r::/child', hostId: 'ssh:box' as const } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: The fixture supplies only the geometry and host-qualified identity fields read by the production projection and retention boundary. + const rows = [ + { + type: 'item', + rowKey: 'root', + sectionKey: 'all', + worktree: { id: 'root' }, + repo: { id: 'r' }, + depth: 0, + groupDepth: 0 + }, + { + type: 'folder-workspace', + key: 'nested-folder', + depth: 1, + groupDepth: 0, + folderWorkspace: { id: 'f', executionHostId: 'ssh:box' }, + projectGroup: {} + }, + { + type: 'item', + rowKey: 'child', + sectionKey: 'all', + worktree: child, + repo: { id: 'r' }, + depth: 2, + groupDepth: 0 + } + ] as unknown as RenderRow[] + const content = document.createElement('div') + content.setAttribute('data-worktree-sidebar-content', '') + content.getBoundingClientRect = () => new DOMRect(0, 0, 300, 400) + const container = document.createElement('div') + container.append(content) + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: This boundary reads only cached virtual endpoints; no mounted DOM or drag-window operation is used. + const virtualization = { + virtualizer: { + measurementsCache: [ + { start: 0, end: 100 }, + { start: 100, end: 200 }, + { start: 200, end: 320 } + ] + } + } as unknown as WorktreeListVirtualizer + const { result } = renderHook(() => + useWorktreeRowGeometry({ + rows, + projection: buildLineageRowProjection(rows, 'none', false), + virtualization, + scrollRef: { current: container } + }) + ) + const folder = 'ssh:box|folder:f' + // Surface is x25..293, y112..307: its own closing7 is included, ancestor7 and outer6 are excluded. + for (const [clientX, clientY, lineClientY, expected] of [ + [30, 105, 150, [child]], + [30, 120, 150, []], + [30, 310, 300, [child]], + [30, 305, 300, []], + [20, 120, 150, [child]] + ] as const) { + const snapshot = result.current.captureFolderRetention( + { clientX, clientY, lineClientY }, + new Set([folder]) + ) + expect( + getReorderUnnestRows({ + legacyIds: [], + pickedCopies: [child], + sourceGroupIdentities: ['ssh:box|r::/child'], + folderParentOf: () => folder, + keepsFolderParent: (identity) => result.current.keepsFolderParent(identity, snapshot) + }) + ).toEqual(expected) + } +}) diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-geometry.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-geometry.ts new file mode 100644 index 00000000000..c8666129c04 --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-geometry.ts @@ -0,0 +1,205 @@ +import { useCallback, useMemo } from 'react' +import type React from 'react' +import type { RenderRow } from '../listing/render-row' +import type { LineageRowProjection } from '../listing/lineage-row-projection' +import type { WorktreeListVirtualizer } from './use-virtualizer' +import { getWorktreeHostIdentity } from '../../../../../../shared/worktree/host-qualified-identity' +import { getWorktreeDragUnitGroups } from '../drag/worktree-drag-units' +import { getWorktreeDragIndexes } from '../drag/groups' +import type { WorktreeSidebarDragRect } from '../drag/worktree-sidebar-drag-autoscroll' +import { + isReorderInsideFolderBounds, + type WorktreeReorderDropPoint +} from '../drag/worktree-lineage-drag-drop' + +export type WorktreeRowGeometry = ReturnType + +export function useWorktreeRowGeometry(args: { + rows: RenderRow[] + projection: LineageRowProjection + virtualization: WorktreeListVirtualizer + scrollRef: React.RefObject +}) { + const { rows, projection, virtualization, scrollRef } = args + const units = useMemo(() => getWorktreeDragUnitGroups(rows), [rows]) + const indexes = useMemo(() => getWorktreeDragIndexes(rows), [rows]) + const ranges = useMemo(() => { + const rowByGroupIdentity = new Map>() + rows.forEach((row, index) => { + if (row.type !== 'item') { + return + } + const group = indexes.groupKeyByRowKey.get(row.rowKey) + if (!group) { + return + } + let members = rowByGroupIdentity.get(group) + if (!members) { + rowByGroupIdentity.set(group, (members = new Map())) + } + members.set(getWorktreeHostIdentity(row.worktree), index) + }) + const byRow = new Map< + number, + { + group: string + identity: string + groupIndex: number + first: number + last: number + trailing: number + next: number | undefined + } + >() + const byGroup = new Map>>>() + for (const group of units) { + const members = rowByGroupIdentity.get(group.key) + const ranges = new Map>>() + group.units.forEach((unit, groupIndex) => { + const first = members?.get(unit.worktreeId) + const last = members?.get(unit.worktreeIds.at(-1)!) + if (first === undefined || last === undefined) { + return + } + let trailing = 0 + let parent = projection.frames[first].parent + while (parent !== null) { + if (projection.frames[parent].end === last) { + trailing += 7 + } + parent = projection.frames[parent].parent + } + const range = { + group: group.key, + identity: unit.worktreeId, + groupIndex, + first, + last, + trailing, + next: members?.get(group.units[groupIndex + 1]?.worktreeId ?? '') + } + ranges.set(unit.worktreeId, range) + for (const identity of unit.worktreeIds) { + const index = members?.get(identity) + if (index !== undefined) { + byRow.set(index, range) + } + } + }) + byGroup.set(group.key, ranges) + } + return { byRow, byGroup } + }, [indexes, projection, rows, units]) + const getRects = useCallback( + (group: string, retained: readonly string[] = []): WorktreeSidebarDragRect[] => { + const selected = new Map>>() + for (const item of virtualization.virtualizer.getVirtualItems()) { + const range = ranges.byRow.get(item.index) + if (range?.group === group) { + selected.set(range.identity, range) + } + } + for (const identity of retained) { + const range = ranges.byGroup.get(group)?.get(identity) + if (range) { + selected.set(identity, range) + } + } + const measurements = virtualization.virtualizer.measurementsCache + return [...selected.values()] + .flatMap((range) => { + const first = measurements[range.first] + const last = measurements[range.last] + if (!first || !last) { + return [] + } + const frame = projection.frames[range.last] + const bottom = last.end - frame.after - range.trailing + const next = range.next === undefined ? undefined : measurements[range.next] + const gap = + next && range.next !== undefined + ? Math.max(0, next.start + projection.frames[range.next].before - bottom) + : projection.frames[range.first].parent === null + ? 6 + : 4 + return [ + { + worktreeId: range.identity, + groupIndex: range.groupIndex, + top: first.start + projection.frames[range.first].before, + bottom, + layoutBottom: bottom + gap + } + ] + }) + .sort((a, b) => a.top - b.top) + }, + [projection, ranges, virtualization.virtualizer] + ) + const keepsFolderParent = useCallback( + (identity: string, point: WorktreeReorderDropPoint | undefined, contentRect?: DOMRect) => { + if (point?.retainedFolderIdentities) { + return point.retainedFolderIdentities.has(identity) + } + const rect = + contentRect ?? + scrollRef.current?.querySelector('[data-worktree-sidebar-content]')?.getBoundingClientRect() + if (!point || !rect) { + return false + } + const measurements = virtualization.virtualizer.measurementsCache + return (projection.occurrencesByIdentity.get(identity) ?? []).some((index) => { + if (rows[index]?.type !== 'folder-workspace') { + return false + } + const frame = projection.frames[index] + const first = measurements[index] + const last = measurements[frame.end] + if (!first || !last) { + return false + } + const top = rect.top + first.start + frame.before + let trailing = 0 + let parent = frame.parent + while (parent !== null) { + const ancestor = projection.frames[parent] + if (ancestor.end === frame.end) { + trailing += 7 + } + parent = ancestor.parent + } + const bottom = rect.top + last.end - projection.frames[frame.end].after - trailing + return isReorderInsideFolderBounds(point, { + left: rect.left + frame.left + 4, + right: rect.right - frame.right, + top, + bottom + }) + }) + }, + [projection, rows, scrollRef, virtualization.virtualizer] + ) + const captureFolderRetention = useCallback( + (point: WorktreeReorderDropPoint | undefined, folderIdentities: ReadonlySet) => { + if (!point) { + return point + } + const retainedFolderIdentities = new Set() + if (folderIdentities.size > 0) { + const rect = scrollRef.current + ?.querySelector('[data-worktree-sidebar-content]') + ?.getBoundingClientRect() + if (rect) { + for (const identity of folderIdentities) { + if (keepsFolderParent(identity, point, rect)) { + retainedFolderIdentities.add(identity) + } + } + } + } + return { ...point, retainedFolderIdentities } + }, + [keepsFolderParent, scrollRef] + ) + return { units, indexes, ranges, getRects, keepsFolderParent, captureFolderRetention } +} diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.scroll.test.tsx b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.scroll.test.tsx new file mode 100644 index 00000000000..c85326b2cfe --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.scroll.test.tsx @@ -0,0 +1,291 @@ +// @vitest-environment happy-dom +import { act, useMemo } from 'react' +import { cleanup, renderHook } from '@testing-library/react' +import { afterEach, beforeEach, expect, it, vi } from 'vitest' +import { createProgrammaticScrollMarks } from '@/hooks/programmatic-scroll-marks' +import { requestVirtualizedScrollAnchorRecord } from '@/hooks/requestVirtualizedScrollAnchorRecord' +import type { VirtualizedScrollAnchor } from '@/hooks/useVirtualizedScrollAnchor' +import { buildLineageRowProjection } from '../listing/lineage-row-projection' +import type { RenderRow } from '../listing/render-row' +import { repo, worktree } from '../grouping/worktree-list-groups-test-fixtures' +import { revealElementInScrollContainer } from '../navigation/worktree-sidebar-reveal' +import { useVirtualRowMeasurementSync } from './use-row-measurement' +import { useWorktreeSidebarScrollSuppression } from './use-scroll-suppression' +import { preserveDeleteSiblingPosition } from '../../workspace-actions/context-menu/worktree-context-menu-policy' +import type { WorktreeListVirtualizer } from './use-virtualizer' + +const caches = vi.hoisted(() => ({ prCache: {}, issueCache: {} })) +vi.mock('@/store', () => ({ + useAppStore: (selector: (state: typeof caches) => unknown) => selector(caches) +})) + +const frames = new Map() +let nextFrame = 0 +beforeEach(() => { + vi.spyOn(window, 'requestAnimationFrame').mockImplementation((callback) => { + frames.set(++nextFrame, callback) + return nextFrame + }) + vi.spyOn(window, 'cancelAnimationFrame').mockImplementation((id) => frames.delete(id)) +}) +afterEach(() => { + cleanup() + frames.clear() + document.body.replaceChildren() + vi.restoreAllMocks() +}) + +function row(id: string): RenderRow { + return { + type: 'item', + rowKey: id, + sectionKey: 'all', + repo, + worktree: { ...worktree, id }, + depth: 0, + groupDepth: 0, + isLastLineageChild: false, + lineageChildCount: 0 + } +} + +function setup(interior = false) { + const scroller = document.createElement('div') + scroller.setAttribute('data-worktree-sidebar', '') + document.body.append(scroller) + Object.defineProperties(scroller, { + clientHeight: { value: 400 }, + scrollHeight: { value: 4000 } + }) + scroller.getBoundingClientRect = () => new DOMRect(0, 0, 300, 400) + scroller.scrollTop = 1000 + scroller.scrollTo = (options?: ScrollToOptions | number) => { + if (typeof options === 'object') { + scroller.scrollTop = options.top ?? 0 + } + } + const model = { + sizes: new Map(), + starts: new Map([ + ['a', 0], + ['b', 1000], + ['c', 2000], + ['d', 3000] + ]), + items: interior ? ['a', 'b', 'c', 'd'] : ['a', 'b', 'c'], + totalSize: 4000 + } + const mount = (id: string) => { + const element = document.createElement('div') + element.setAttribute('data-worktree-virtual-row', '') + element.setAttribute('data-worktree-virtual-row-key', `wt:${id}`) + element.getBoundingClientRect = () => + new DOMRect( + 0, + (model.starts.get(id) ?? 0) - scroller.scrollTop, + 300, + model.sizes.get(id) ?? 1000 + ) + scroller.append(element) + return element + } + const mounted = mount('b') + const virtualizer = { + elementsCache: new Map(), + getTotalSize: () => model.totalSize, + getVirtualItems: () => + model.items.map((id, index) => ({ + index, + key: `wt:${id}`, + start: model.starts.get(id) ?? 0, + end: (model.starts.get(id) ?? 0) + (model.sizes.get(id) ?? 1000), + size: model.sizes.get(id) ?? 1000, + lane: 0 + })), + measureElement: () => {}, + isScrolling: false, + scrollOffset: null, + scrollToIndex: () => { + scroller.scrollTop = 500 + } + } + const scrollRef = { current: scroller } + const scrollOffsetRef = { current: 1000 } + const scrollAnchorRef: { current: VirtualizedScrollAnchor } = { current: null } + const marks = createProgrammaticScrollMarks() + const view = renderHook( + ({ rows }: { rows: RenderRow[] }) => { + const projection = useMemo(() => buildLineageRowProjection(rows, 'none', false), [rows]) + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: The fixture supplies the geometry and virtualizer methods consumed by the production measurement/anchor integration. + const virtualization = { + virtualizer, + isCurrentVirtualRowElement: () => true, + stickyHeaderIndexes: [], + activeStickyHeaderIndexRef: { current: null }, + activeStickyHostIndexRef: { current: null }, + stickyRangeStartIndexRef: { current: 0 }, + rowIndexByKey: projection.rowIndexByKey, + restoreSignal: projection.restoreSignal, + programmaticScrollMarks: marks + } as unknown as WorktreeListVirtualizer + const input = useWorktreeSidebarScrollSuppression(scrollRef) + useVirtualRowMeasurementSync({ + renderRows: rows, + virtualization, + scrollRef, + scrollOffsetRef, + scrollAnchorRef, + shouldSkipScrollAnchorRestore: input.shouldSkipScrollAnchorRestore + }) + return input + }, + { + initialProps: { + rows: interior + ? [row('a'), { ...row('b'), depth: 1 }, { ...row('c'), depth: 1 }, row('d')] + : [row('a'), row('b'), row('c')] + } + } + ) + act(() => requestVirtualizedScrollAnchorRecord(scroller)) + return { scroller, model, mount, mounted, view } +} + +it('leaves height-only scroll correction to the virtualizer instead of restoring an older DOM position', () => { + const { scroller, model, view } = setup() + act(() => { + scroller.scrollTop = 1200 + scroller.dispatchEvent(new Event('scroll')) + }) + model.starts.set('b', 950) + model.totalSize = 3950 + view.rerender({ rows: [row('a'), row('b'), row('c')] }) + expect(scroller.scrollTop).toBe(1200) +}) + +it('preserves the visible occurrence when rows before it disappear', () => { + const { scroller, model, mounted, view } = setup() + act(() => { + scroller.scrollTop = 1020 + scroller.dispatchEvent(new Event('scroll')) + }) + model.starts.set('b', 0) + model.starts.set('c', 1000) + model.items = ['b', 'c'] + model.totalSize = 3000 + view.rerender({ rows: [row('b'), row('c')] }) + expect(scroller.scrollTop).toBe(20) + expect(mounted.getBoundingClientRect().top).toBe(-20) +}) + +it('lets an already-visible explicit reveal supersede a pending offscreen restore', () => { + const { scroller, model, mounted, mount, view } = setup() + mounted.remove() + model.items = [] + view.rerender({ rows: [row('b'), row('c')] }) + expect(scroller.scrollTop).toBe(500) + model.starts.set('c', 550) + const destination = mount('c') + destination.getBoundingClientRect = () => new DOMRect(0, 550 - scroller.scrollTop, 300, 100) + model.starts.set('b', 400) + model.sizes.set('b', 100) + const oldTarget = mount('b') + oldTarget.getBoundingClientRect = () => new DOMRect(0, 400 - scroller.scrollTop, 300, 100) + model.items = ['b', 'c'] + act(() => { + expect(revealElementInScrollContainer(scroller, destination, 'auto')).toBe(true) + const pendingFrames = [...frames] + for (const [id, callback] of pendingFrames) { + frames.delete(id) + callback(0) + } + }) + expect(scroller.scrollTop).toBe(500) + expect(destination.getBoundingClientRect().top).toBe(50) +}) + +function finishPositioning() { + act(() => { + for (let turn = 0; turn < 12 && frames.size; turn++) { + const pendingFrames = [...frames] + for (const [id, callback] of pendingFrames) { + frames.delete(id) + callback(0) + } + } + }) +} + +it('lets no-op direct input retire an old target and preserve position on a later insertion', () => { + const { scroller, model, mounted, mount, view } = setup() + mounted.remove() + model.items = [] + view.rerender({ rows: [row('b'), row('c')] }) + model.starts.set('b', 400) + model.sizes.set('b', 100) + const oldTarget = mount('b') + oldTarget.getBoundingClientRect = () => new DOMRect(0, 400 - scroller.scrollTop, 300, 100) + model.starts.set('c', 480) + const destination = mount('c') + model.items = ['b', 'c'] + act(() => view.result.current.markDirectScrollInput()) + finishPositioning() + expect(scroller.scrollTop).toBe(500) + expect(destination.getBoundingClientRect().top).toBe(-20) + model.starts.set('c', 680) + model.items = ['b', 'new', 'c'] + view.rerender({ rows: [row('b'), row('new'), row('c')] }) + expect(scroller.scrollTop).toBe(700) + expect(destination.getBoundingClientRect().top).toBe(-20) +}) + +it('keeps an interior descendant successor at the removed card position after action takeover', () => { + const { scroller, model, mounted, mount, view } = setup(true) + model.sizes.set('b', 200) + model.starts.set('c', 1200) + model.starts.set('d', 2200) + model.totalSize = 3200 + const successor = mount('c') + act(() => { + scroller.scrollTop = 1020 + scroller.dispatchEvent(new Event('scroll')) + }) + const position = preserveDeleteSiblingPosition(mounted) + // The real teardown boundary records the live viewport immediately before its row mutation. + act(() => requestVirtualizedScrollAnchorRecord(scroller)) + mounted.remove() + model.starts.set('c', 1000) + model.starts.set('d', 2000) + model.items = ['a', 'c', 'd'] + model.totalSize = 3000 + view.rerender({ rows: [row('a'), { ...row('c'), depth: 1 }, row('d')] }) + expect(scroller.scrollTop).toBe(1000) + act(position) + view.rerender({ rows: [row('a'), { ...row('c'), depth: 1 }, row('d')] }) + expect(scroller.scrollTop).toBe(1020) + finishPositioning() + expect(scroller.scrollTop).toBe(1020) + expect(successor.getBoundingClientRect().top).toBe(-20) +}) + +it('preserves the live viewport when delayed removal follows completed positioning and user scrolling', () => { + const { scroller, model, mounted, mount, view } = setup(true) + const successor = mount('c') + const position = preserveDeleteSiblingPosition(mounted) + act(position) + finishPositioning() + act(() => { + scroller.scrollTop = 2100 + scroller.dispatchEvent(new Event('scroll')) + requestVirtualizedScrollAnchorRecord(scroller) + }) + mounted.remove() + model.starts.set('c', 1000) + model.starts.set('d', 2000) + model.items = ['a', 'c', 'd'] + model.totalSize = 3000 + view.rerender({ rows: [row('a'), { ...row('c'), depth: 1 }, row('d')] }) + finishPositioning() + expect(scroller.scrollTop).toBe(1100) + expect(successor.getBoundingClientRect().top).toBe(-100) +}) diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.ts index 02889ddad96..171ae6e272b 100644 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.ts @@ -6,7 +6,6 @@ import { type VirtualizedScrollAnchor } from '@/hooks/useVirtualizedScrollAnchor' import { - buildLineageRowRekeyMap, getActiveStickyIndexesForScroll, getVirtualRowKey, pruneStaleVirtualRowElementCache @@ -14,7 +13,6 @@ import { import { getRenderRowKey } from '../listing/render-row' import type { RenderRow } from '../listing/render-row' import type { WorktreeListVirtualizer } from './use-virtualizer' -import { useVirtualRowRemovalAnimation } from './use-row-removal-animation' const recordKeyCountCache = new WeakMap, number>() @@ -35,7 +33,6 @@ export function useVirtualRowMeasurementSync(args: { scrollRef: React.RefObject scrollOffsetRef: React.MutableRefObject scrollAnchorRef: React.MutableRefObject - hasDirectScrollInput: () => boolean shouldSkipScrollAnchorRestore: () => boolean }) { const { @@ -44,14 +41,15 @@ export function useVirtualRowMeasurementSync(args: { scrollRef, scrollOffsetRef, scrollAnchorRef, - hasDirectScrollInput, shouldSkipScrollAnchorRestore } = args const { virtualizer, isCurrentVirtualRowElement } = virtualization const prCacheLen = useAppStore((s) => countRecordKeysByReference(s.prCache)) const issueCacheLen = useAppStore((s) => countRecordKeysByReference(s.issueCache)) - const activeRenderRowKeys = useMemo(() => new Set(renderRows.map(getRenderRowKey)), [renderRows]) - const lineageRowRekeys = useMemo(() => buildLineageRowRekeyMap(renderRows), [renderRows]) + const activeRenderRowKeys = useMemo( + () => new Set(virtualization.rowIndexByKey.keys()), + [virtualization.rowIndexByKey] + ) const totalSize = virtualizer.getTotalSize() const virtualItems = virtualizer.getVirtualItems() const activeStickyIndexes = getActiveStickyIndexesForScroll({ @@ -95,29 +93,30 @@ export function useVirtualRowMeasurementSync(args: { measureMountedRows() const frameId = window.requestAnimationFrame(measureMountedRows) return () => window.cancelAnimationFrame(frameId) - }, [activeRenderRowKeys, prCacheLen, issueCacheLen, measureMountedRows, virtualizer]) + }, [ + activeRenderRowKeys, + prCacheLen, + issueCacheLen, + measureMountedRows, + virtualizer, + virtualization.smoothScrollRevision + ]) useVirtualizedScrollAnchor({ anchorRef: scrollAnchorRef, getItemElementKey: getVirtualRowKey, getRowKey: getRenderRowKey, itemElementSelector: '[data-worktree-virtual-row]', - rekeyedRowKeys: lineageRowRekeys, rows: renderRows, + rowIndexByKey: virtualization.rowIndexByKey, + restoreSignal: virtualization.restoreSignal, + programmaticScrollMarks: virtualization.programmaticScrollMarks, scrollElementRef: scrollRef, scrollOffsetRef, - hasDirectScrollInput, shouldSkipRestore: shouldSkipScrollAnchorRestore, totalSize, virtualizer }) - useVirtualRowRemovalAnimation({ - renderRows, - rekeyedRowKeys: lineageRowRekeys, - scrollRef, - virtualItems - }) - return { virtualItems, measureVirtualRowElement } } diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-removal-animation.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-removal-animation.ts deleted file mode 100644 index 59fb6468810..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-removal-animation.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { useLayoutEffect, useMemo, useRef } from 'react' -import type React from 'react' -import type { VirtualItem } from '@tanstack/react-virtual' -import type { RenderRow } from '../listing/render-row' -import { getRenderRowKey } from '../listing/render-row' - -export const WORKTREE_ROW_REMOVAL_ANIMATION_MS = 180 - -export type VirtualRowLayoutSnapshot = { - rowIdentityKeys: ReadonlySet - scrollTop: number - startsByKey: ReadonlyMap -} - -export type VirtualRowRemovalMotion = { - deltaY: number - key: string -} - -export function getSidebarRowIdentityKeys(rows: readonly RenderRow[]): ReadonlySet { - const keys = new Set() - for (const row of rows) { - if (row.type === 'lineage-group') { - row.rows.forEach((member) => keys.add(`wt:${member.rowKey}`)) - if (row.folder) { - keys.add(getRenderRowKey(row.folder)) - } - } else { - keys.add(getRenderRowKey(row)) - } - } - return keys -} - -export function buildVirtualRowRemovalMotions(args: { - previous: VirtualRowLayoutSnapshot | null - current: VirtualRowLayoutSnapshot - rekeyedRowKeys: ReadonlyMap -}): VirtualRowRemovalMotion[] { - const { previous, current, rekeyedRowKeys } = args - if (previous === null || previous.rowIdentityKeys === current.rowIdentityKeys) { - return [] - } - let removedRow = false - for (const key of previous.rowIdentityKeys) { - if (!current.rowIdentityKeys.has(key)) { - removedRow = true - break - } - } - if (!removedRow) { - return [] - } - - const previousKeyByCurrentKey = new Map() - rekeyedRowKeys.forEach((currentKey, previousKey) => { - previousKeyByCurrentKey.set(currentKey, previousKey) - }) - const motions: VirtualRowRemovalMotion[] = [] - current.startsByKey.forEach((currentStart, key) => { - const previousKey = previous.startsByKey.has(key) ? key : previousKeyByCurrentKey.get(key) - const previousStart = previousKey ? previous.startsByKey.get(previousKey) : undefined - if (previousStart === undefined) { - return - } - const deltaY = previousStart - previous.scrollTop - (currentStart - current.scrollTop) - if (Math.abs(deltaY) > 0.5) { - motions.push({ deltaY, key }) - } - }) - return motions -} - -export function useVirtualRowRemovalAnimation(args: { - renderRows: readonly RenderRow[] - rekeyedRowKeys: ReadonlyMap - scrollRef: React.RefObject - virtualItems: readonly VirtualItem[] -}): void { - const { renderRows, rekeyedRowKeys, scrollRef, virtualItems } = args - const previousSnapshotRef = useRef(null) - const rowIdentityKeys = useMemo(() => getSidebarRowIdentityKeys(renderRows), [renderRows]) - - useLayoutEffect(() => { - const scrollElement = scrollRef.current - if (!scrollElement) { - return - } - const current: VirtualRowLayoutSnapshot = { - rowIdentityKeys, - scrollTop: scrollElement.scrollTop, - startsByKey: new Map(virtualItems.map((item) => [String(item.key), item.start])) - } - const motions = buildVirtualRowRemovalMotions({ - previous: previousSnapshotRef.current, - current, - rekeyedRowKeys - }) - previousSnapshotRef.current = current - if (motions.length === 0 || window.matchMedia('(prefers-reduced-motion: reduce)').matches) { - return - } - - const elementsByKey = new Map( - Array.from( - scrollElement.querySelectorAll('[data-worktree-virtual-row-key]') - ).map((element) => [element.dataset.worktreeVirtualRowKey ?? '', element]) - ) - for (const motion of motions) { - const element = elementsByKey.get(motion.key) - if (!element || element.hasAttribute('data-worktree-sticky-header-active')) { - continue - } - const content = element.firstElementChild - if (!(content instanceof HTMLElement)) { - continue - } - content.animate([{ translate: `0 ${motion.deltaY}px` }, { translate: '0 0' }], { - duration: WORKTREE_ROW_REMOVAL_ANIMATION_MS, - easing: 'cubic-bezier(0.16, 1, 0.3, 1)' - }) - } - }, [rekeyedRowKeys, rowIdentityKeys, scrollRef, virtualItems]) -} diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/use-scroll-suppression.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-scroll-suppression.ts index 1a88429c1d9..7f3f08600da 100644 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/use-scroll-suppression.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-scroll-suppression.ts @@ -1,59 +1,33 @@ -import { useCallback, useEffect, useRef } from 'react' +import { + requestVirtualizedScrollAnchorRecord, + supersedeVirtualizedScrollAnchorRestore +} from '@/hooks/requestVirtualizedScrollAnchorRecord' +import { useCallback, useRef } from 'react' import type React from 'react' -import { SUPPRESS_WORKTREE_LIST_SCROLL_ADJUSTMENT_EVENT } from '../../workspace-card/activity/WorktreeCardAgents' import { createPendingRevealScroll, isRevealScrollSettling, type PendingRevealScroll } from '../navigation/worktree-sidebar-reveal-scroll-settle' -export const USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS = 500 -export const EXPANDING_CARD_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS = 300 - -export function shouldAdjustWorktreeSidebarMeasuredRowScroll(args: { - isScrolling: boolean - now: number - suppressUntil: number - itemStart: number - itemEnd: number - scrollOffset: number - isFirstMeasurement: boolean - scrollDirection: 'forward' | 'backward' | null -}): boolean { - if (args.isScrolling || args.now < args.suppressUntil) { - return false - } - // Preserve TanStack's fold rule so below-anchor growth cannot invent a scroll offset. - return args.isFirstMeasurement - ? args.itemStart < args.scrollOffset - : args.itemEnd <= args.scrollOffset && args.scrollDirection !== 'backward' -} - export type WorktreeSidebarScrollSuppression = ReturnType< typeof useWorktreeSidebarScrollSuppression > -// Track measurement suppression for all scrolling and anchor suppression for direct input or smooth reveals. +// Explicit input retires the previous target; smooth reveal owns its settling position. export function useWorktreeSidebarScrollSuppression( scrollRef: React.RefObject ) { - const suppressMeasurementAdjustmentUntilRef = useRef(0) - const directScrollInputUntilRef = useRef(0) const pendingRevealScrollRef = useRef(null) + const cancelRevealScroll = useCallback(() => { + pendingRevealScrollRef.current = null + }, []) - const markScrollMovement = useCallback(() => { - suppressMeasurementAdjustmentUntilRef.current = - window.performance.now() + USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS - }, []) const markDirectScrollInput = useCallback(() => { - const suppressUntil = window.performance.now() + USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS - suppressMeasurementAdjustmentUntilRef.current = suppressUntil - directScrollInputUntilRef.current = suppressUntil - }, []) - const hasDirectScrollInput = useCallback( - () => window.performance.now() < directScrollInputUntilRef.current, - [] - ) + supersedeVirtualizedScrollAnchorRestore(scrollRef.current) + cancelRevealScroll() + requestVirtualizedScrollAnchorRecord(scrollRef.current, { deferDomMeasurement: true }) + }, [cancelRevealScroll, scrollRef]) const markRevealScroll = useCallback((targetTop: number) => { pendingRevealScrollRef.current = createPendingRevealScroll(targetTop, window.performance.now()) }, []) @@ -68,32 +42,11 @@ export function useWorktreeSidebarScrollSuppression( } return settling }, [scrollRef]) - // Programmatic scrolling suppresses measurements; only direct input or an in-flight reveal blocks anchor restore. - const shouldSkipScrollAnchorRestore = useCallback( - () => - window.performance.now() < directScrollInputUntilRef.current || isRevealScrollSettlingNow(), - [isRevealScrollSettlingNow] - ) - - useEffect(() => { - const handleSuppress = () => { - // Why: let an expanding agent row grow in place instead of TanStack compensating scrollTop. - suppressMeasurementAdjustmentUntilRef.current = - window.performance.now() + EXPANDING_CARD_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS - } - window.addEventListener(SUPPRESS_WORKTREE_LIST_SCROLL_ADJUSTMENT_EVENT, handleSuppress) - return () => { - window.removeEventListener(SUPPRESS_WORKTREE_LIST_SCROLL_ADJUSTMENT_EVENT, handleSuppress) - } - }, []) return { - suppressMeasurementAdjustmentUntilRef, - directScrollInputUntilRef, - markScrollMovement, + cancelRevealScroll, markDirectScrollInput, - hasDirectScrollInput, markRevealScroll, - shouldSkipScrollAnchorRestore + shouldSkipScrollAnchorRestore: isRevealScrollSettlingNow } } diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/use-scroll-to-top.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-scroll-to-top.ts index 0df493210ab..93b77a4c4b7 100644 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/use-scroll-to-top.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-scroll-to-top.ts @@ -1,3 +1,7 @@ +import { + requestVirtualizedScrollAnchorRecord, + supersedeVirtualizedScrollAnchorRestore +} from '@/hooks/requestVirtualizedScrollAnchorRecord' import { useCallback, useEffect, useRef, useState } from 'react' import { createHardScrollUpDetectorState, @@ -214,7 +218,9 @@ export function useWorktreeListScrollToTop({ clearIdleTimer() suppressDetectionUntilRef.current = window.performance.now() + HARD_SCROLL_UP.suppressAfterJumpMs + supersedeVirtualizedScrollAnchorRestore(scrollElement, 0) scrollElement.scrollTo({ top: 0, behavior: 'auto' }) + requestVirtualizedScrollAnchorRecord(scrollElement) scrollElement.focus({ preventScroll: true }) }, [clearIdleTimer, onUserScrollIntent, publishVisible, scrollElement]) diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/use-virtualizer.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-virtualizer.ts index 03809ab4d27..14d9e3699dc 100644 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/use-virtualizer.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-virtualizer.ts @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useRef } from 'react' +import { useCallback, useMemo, useReducer, useRef } from 'react' import type React from 'react' import { measureElement as measureVirtualElementSize, @@ -13,22 +13,22 @@ import { WORKTREE_SIDEBAR_VIRTUAL_ROW_GAP } from './virtual-rows' import { getRenderRowKey } from '../listing/render-row' +import type { LineageRowProjection } from '../listing/lineage-row-projection' import type { RenderRow } from '../listing/render-row' import { WORKTREE_SIDEBAR_REVEAL_TOP_INSET } from '../navigation/worktree-sidebar-reveal' -import { - shouldAdjustWorktreeSidebarMeasuredRowScroll, - USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS -} from './use-scroll-suppression' +import type { ProgrammaticScrollMarks } from '@/hooks/programmatic-scroll-marks' +import { createSidebarSmoothReveal } from './sidebar-smooth-reveal' export type WorktreeListVirtualizer = ReturnType // Guard virtual row identity so recycled DOM nodes cannot overwrite another slot’s measured height. export function useWorktreeListVirtualizer(args: { + projection: LineageRowProjection renderRows: RenderRow[] firstHeaderIndex: number scrollRef: React.RefObject scrollOffsetRef: React.MutableRefObject - suppressMeasurementAdjustmentUntilRef: React.MutableRefObject + programmaticScrollMarks: ProgrammaticScrollMarks }) { const { renderRows, firstHeaderIndex, scrollRef, scrollOffsetRef } = args const stickyHeaderIndexes = useMemo(() => getStickyHeaderIndexes(renderRows), [renderRows]) @@ -97,21 +97,35 @@ export function useWorktreeListVirtualizer(args: { activeStickyHeaderIndexRef.current ) } - return measureVirtualElementSize(element, entry, instance) + const measured = measureVirtualElementSize(element, entry, instance) + const row = index === null ? undefined : renderRows[index] + return measured + (row?.type === 'item' || row?.type === 'folder-workspace' ? 0 : 6) }, [firstHeaderIndex, isCurrentVirtualRowElement, renderRows] ) + const [smoothScrollRevision, onRetired] = useReducer((revision: number) => revision + 1, 0) + const smoothReveal = useMemo( + () => createSidebarSmoothReveal({ marks: args.programmaticScrollMarks, onRetired }), + [args.programmaticScrollMarks, onRetired] + ) const virtualizer = useVirtualizer({ count: renderRows.length, getScrollElement: () => scrollRef.current, - estimateSize: (index) => - estimateRenderRowSize( - renderRows, - index, - firstHeaderIndex, - activeStickyHeaderIndexRef.current - ), + estimateSize: (index) => { + const frame = args.projection.frames[index] + return ( + estimateRenderRowSize( + renderRows, + index, + firstHeaderIndex, + activeStickyHeaderIndexRef.current + ) + + (renderRows[index]?.type === 'item' || renderRows[index]?.type === 'folder-workspace' + ? frame.before + frame.closing + frame.after + : 0) + ) + }, measureElement: measureCurrentVirtualRowElement, // Why: TanStack memoizes rangeExtractor by identity; header indexes must be deps or sticky slots go stale. rangeExtractor: useCallback( @@ -129,29 +143,19 @@ export function useWorktreeListVirtualizer(args: { gap: WORKTREE_SIDEBAR_VIRTUAL_ROW_GAP, // Why: the sticky group header lives inside the virtual list, so scroll math needs the same top inset as the DOM reveal. scrollPaddingStart: WORKTREE_SIDEBAR_REVEAL_TOP_INSET, - isScrollingResetDelay: USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS, - // Why: sync-flushing rich card renders in the scroll listener stalls wheel input; async + overscan keeps rows filled. - useFlushSync: false, // Why: seed scrollOffset from the ref (not 0) so the first getVirtualItems() after remount picks the right rows. initialOffset: () => scrollOffsetRef.current, + scrollToFn: smoothReveal.scrollTo, + observeElementOffset: smoothReveal.observeOffset, getItemKey: getVirtualItemKey }) - // Why: TanStack's default correction writes scrollTop while cards remeasure mid-wheel, which feels like rubber-banding. - // TODO(scroll-origin-migration): wall-clock suppression misclassifies under jank; migrate to programmaticScrollMarks + restoreSignal (see CombinedDiffViewer). - virtualizer.shouldAdjustScrollPositionOnItemSizeChange = (item, _delta, instance) => - shouldAdjustWorktreeSidebarMeasuredRowScroll({ - isScrolling: instance.isScrolling, - now: window.performance.now(), - suppressUntil: args.suppressMeasurementAdjustmentUntilRef.current, - itemStart: item.start, - itemEnd: item.end, - scrollOffset: (instance.scrollOffset ?? scrollOffsetRef.current) + instance.scrollAdjustments, - isFirstMeasurement: !instance.itemSizeCache.has(item.key), - scrollDirection: instance.scrollDirection - }) return { virtualizer, + smoothScrollRevision, + programmaticScrollMarks: args.programmaticScrollMarks, + rowIndexByKey: args.projection.rowIndexByKey, + restoreSignal: args.projection.restoreSignal, isCurrentVirtualRowElement, stickyHeaderIndexes, activeStickyHeaderIndexRef, diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/use-visible-review-refresh.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-visible-review-refresh.ts index 540cbedaaac..e78ccc189ef 100644 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/use-visible-review-refresh.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-visible-review-refresh.ts @@ -46,9 +46,7 @@ export function visibleReviewCardIds(args: { return args.virtualItems .filter((item) => item.start < bottom && item.end > args.viewportTop) .map((item) => args.renderRows[item.index]) - .flatMap((row): WorktreeItemRow[] => - row?.type === 'lineage-group' ? row.rows : row?.type === 'item' ? [row] : [] - ) + .flatMap((row): WorktreeItemRow[] => (row?.type === 'item' ? [row] : [])) .filter( (row) => (row.repo?.kind ?? 'git') === 'git' && diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-row-context.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-row-context.ts index 366b582aa67..60718f2d76c 100644 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-row-context.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-row-context.ts @@ -2,6 +2,7 @@ import type React from 'react' import type { AppState } from '@/store/types' import type { ProjectGroup } from '../../../../../../shared/project-group-types' import type { Worktree } from '../../../../../../shared/worktree/types' +import type { LineageRowProjection } from '../listing/lineage-row-projection' import type { RenderRow } from '../listing/render-row' import type { LineageToggleHandler } from '../../workspace-card/identity/worktree-lineage-toggle-handler-cache' import type { SidebarRevealHighlight } from '../navigation/use-reveal-highlight' @@ -22,6 +23,10 @@ import type { ProjectHeaderHostContext } from '../rows/project-header-host-conte type BuildArgs = { props: VirtualizedWorktreeViewportProps projectGroups: readonly ProjectGroup[] + dragUnitsByRow: ReadonlyMap + hiddenDragRanges: readonly { start: number; end: number }[] + projection: LineageRowProjection + activeFamilyRoots: ReadonlySet renderRows: RenderRow[] firstHeaderIndex: number virtualization: WorktreeListVirtualizer @@ -51,6 +56,10 @@ type BuildArgs = { export function buildWorktreeVirtualRowContext(args: BuildArgs): WorktreeVirtualRowContext { const { props, runtime, session, statusDrag, headerDrag, primaryActive, reveal } = args return { + dragUnitsByRow: args.dragUnitsByRow, + hiddenDragRanges: args.hiddenDragRanges, + projection: args.projection, + activeFamilyRoots: args.activeFamilyRoots, renderRows: args.renderRows, firstHeaderIndex: args.firstHeaderIndex, activeStickyHeaderIndexRef: args.virtualization.activeStickyHeaderIndexRef, diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.ts index ff132e3d13f..dd54bae3d98 100644 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.ts @@ -1,51 +1,17 @@ import { defaultRangeExtractor } from '@tanstack/react-virtual' import type { Range, VirtualItem } from '@tanstack/react-virtual' -import { - PINNED_GROUP_KEY, - getWorktreeLineageGroupKey -} from '../../../../../../shared/sidebar-list/group-keys' -import { getRenderRowKey } from '../listing/render-row' +import { PINNED_GROUP_KEY } from '../../../../../../shared/sidebar-list/group-keys' import type { RenderRow } from '../listing/render-row' export const GROUP_HEADER_ROW_HEIGHT = 28 // Why: HostSectionHeader wraps its h-8 (32px) card in pt-1 (4px), so the row is 36px. export const HOST_HEADER_ROW_HEIGHT = 36 -export const WORKTREE_SIDEBAR_VIRTUAL_ROW_GAP = 6 +export const WORKTREE_SIDEBAR_VIRTUAL_ROW_GAP = 0 const SECONDARY_GROUP_HEADER_TOP_MARGIN = 4 const IMPORTED_WORKTREES_LINE_ROW_HEIGHT = 36 const PENDING_CREATION_ROW_HEIGHT = 56 const FOLDER_WORKSPACE_ROW_HEIGHT = 64 -/** Remap anchors when first/last child changes flip a parent between plain and lineage-group keys without moving it. */ -export function buildLineageRowRekeyMap(rows: readonly RenderRow[]): ReadonlyMap { - const rekeyed = new Map() - for (const row of rows) { - if (row.type === 'lineage-group') { - const groupKey = getRenderRowKey(row) - for (const member of row.rows) { - rekeyed.set(`wt:${member.rowKey}`, groupKey) - } - if (row.folder) { - rekeyed.set(getRenderRowKey(row.folder), groupKey) - } - continue - } - if (row.type === 'folder-workspace') { - rekeyed.set(`lineage-group:${row.key}`, getRenderRowKey(row)) - continue - } - if (row.type !== 'item') { - continue - } - // Map dissolved groups even with zero children because old anchors still hold their group key. - rekeyed.set( - `lineage-group:${row.sectionKey}:${getWorktreeLineageGroupKey(row.worktree)}`, - getRenderRowKey(row) - ) - } - return rekeyed -} - export function shouldUseHeaderTopSpacing(args: { rows: readonly RenderRow[] index: number @@ -67,6 +33,7 @@ export function estimateRenderRowSize( if (row?.type === 'host-header') { return ( HOST_HEADER_ROW_HEIGHT + + 6 + (shouldUseHeaderTopSpacing({ rows, index, @@ -79,6 +46,7 @@ export function estimateRenderRowSize( if (row?.type === 'header') { return ( GROUP_HEADER_ROW_HEIGHT + + 6 + (shouldUseHeaderTopSpacing({ rows, index, @@ -88,16 +56,11 @@ export function estimateRenderRowSize( : 0) ) } - if (row?.type === 'lineage-group') { - return row.folder - ? FOLDER_WORKSPACE_ROW_HEIGHT + row.rows.length * 96 - : 100 + Math.max(0, row.rows.length - 1) * 96 - } if (row?.type === 'imported-worktrees-card' || row?.type === 'new-external-worktrees-inbox') { - return IMPORTED_WORKTREES_LINE_ROW_HEIGHT + return IMPORTED_WORKTREES_LINE_ROW_HEIGHT + 6 } if (row?.type === 'pending-creation') { - return PENDING_CREATION_ROW_HEIGHT + return PENDING_CREATION_ROW_HEIGHT + 6 } if (row?.type === 'folder-workspace') { return FOLDER_WORKSPACE_ROW_HEIGHT diff --git a/src/renderer/src/hooks/requestVirtualizedScrollAnchorRecord.ts b/src/renderer/src/hooks/requestVirtualizedScrollAnchorRecord.ts index c6f6df07e5f..c697ee2bd0b 100644 --- a/src/renderer/src/hooks/requestVirtualizedScrollAnchorRecord.ts +++ b/src/renderer/src/hooks/requestVirtualizedScrollAnchorRecord.ts @@ -1,11 +1,32 @@ import { VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT } from './useVirtualizedScrollAnchor' /** Snapshot a mounted scroller’s live top-row anchor in the same tick as external row mutation, avoiding stale click-time anchors. */ -export function requestVirtualizedScrollAnchorRecord(scrollElementSelector: string): void { +export function requestVirtualizedScrollAnchorRecord( + scrollElement: string | Element | null, + options?: { deferDomMeasurement: boolean } +): void { if (typeof document === 'undefined') { return } - document - .querySelector(scrollElementSelector) - ?.dispatchEvent(new Event(VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT)) + const element = + typeof scrollElement === 'string' ? document.querySelector(scrollElement) : scrollElement + element?.dispatchEvent( + options?.deferDomMeasurement + ? new CustomEvent(VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT, { + detail: { deferDomMeasurement: true } + }) + : new Event(VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT) + ) +} + +/** Explicit navigation replaces a pending saved/structural target, even when its scroll is a no-op. */ +export function supersedeVirtualizedScrollAnchorRestore( + scrollElement: Element | null, + targetOffset?: number +): void { + scrollElement?.dispatchEvent( + new CustomEvent(VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT, { + detail: { cancelRestore: true, targetOffset } + }) + ) } diff --git a/src/renderer/src/hooks/useVirtualizedScrollAnchor.ts b/src/renderer/src/hooks/useVirtualizedScrollAnchor.ts index 9015bfeda54..4ec0b62bbe1 100644 --- a/src/renderer/src/hooks/useVirtualizedScrollAnchor.ts +++ b/src/renderer/src/hooks/useVirtualizedScrollAnchor.ts @@ -213,9 +213,25 @@ export function useVirtualizedScrollAnchor< targetOffset: scrollOffsetRef.current, scrollOffsetRef }) - const recordRequestedAnchor = (): void => { + const recordRequestedAnchor = (event: Event): void => { cancelMountRestore() pendingRestoreRef.current = false + cancelScheduledRecord() + if (event instanceof CustomEvent && event.detail?.cancelRestore === true) { + anchorRef.current = null + scrollOffsetRef.current = el.scrollTop + const targetOffset: unknown = event.detail.targetOffset + if (typeof targetOffset === 'number' && targetOffset !== el.scrollTop) { + programmaticScrollMarksRef.current?.mark(targetOffset) + } + return + } + if (event instanceof CustomEvent && event.detail?.deferDomMeasurement === true) { + scrollOffsetRef.current = el.scrollTop + recordVirtualScrollAnchorRef.current(el.scrollTop) + scheduleRecordAnchor() + return + } recordCurrentAnchor() } From 96d2c0b0b6b5943a19096fb5a0653b6310d3cdfd Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 09:23:01 +0000 Subject: [PATCH 056/178] fix(opencode): attach @-mentioned files to OpenCode chat prompts (#41932) * fix(opencode): attach @-mentioned files to OpenCode chat prompts OpenCode's ACP agent reads a file only from a resource_link block and ignores @path text, so mentioned files never reached the model. Keep the text as typed and link each mentioned file inside the session folder. * fix(opencode): reuse the chat mention scanner and compare paths byte-exactly * fix(opencode): leave mentioned files over 20 MiB as text --- src/main/acp/acp-dialects/acp-dialect.ts | 2 + src/main/acp/acp-dialects/opencode-dialect.ts | 1 + .../acp/session/acp-structured-acquire.ts | 1 + .../acp-structured-session-contract.ts | 2 + .../session/children/acp-structured-child.ts | 1 + src/main/acp/turns/acp-file-mention-links.ts | 66 +++++++++++++++++++ src/main/acp/turns/acp-prompt-content.test.ts | 34 +++++++++- src/main/acp/turns/acp-prompt-content.ts | 28 ++++++-- 8 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 src/main/acp/turns/acp-file-mention-links.ts diff --git a/src/main/acp/acp-dialects/acp-dialect.ts b/src/main/acp/acp-dialects/acp-dialect.ts index 25412f7547c..e283e6a7188 100644 --- a/src/main/acp/acp-dialects/acp-dialect.ts +++ b/src/main/acp/acp-dialects/acp-dialect.ts @@ -185,6 +185,8 @@ export type AcpDialect = { text(value: string): string reply(text: string): AcpModelCommandReply } + /** The agent reads a mentioned file only from a resource link, not from `@path` text. */ + linksFileMentions?: true /** Hide these commands and reject command-only prompts before dispatch. */ withheldCommands?: readonly string[] /** Which sign-in a not-signed-in chat needs; absent is the agent's usual one. */ diff --git a/src/main/acp/acp-dialects/opencode-dialect.ts b/src/main/acp/acp-dialects/opencode-dialect.ts index 4b0ad20e23d..d177f690405 100644 --- a/src/main/acp/acp-dialects/opencode-dialect.ts +++ b/src/main/acp/acp-dialects/opencode-dialect.ts @@ -111,6 +111,7 @@ export const OPENCODE_ACP_DIALECT: AcpDialect = { supported: (initialized) => initialized.agentCapabilities?.sessionCapabilities?.close != null }, effortChangesModel: true, + linksFileMentions: true, normalizeToolUpdate, notification: openCodeChildUpdate, promptFailure diff --git a/src/main/acp/session/acp-structured-acquire.ts b/src/main/acp/session/acp-structured-acquire.ts index 4fecd8a131d..c69ac58a884 100644 --- a/src/main/acp/session/acp-structured-acquire.ts +++ b/src/main/acp/session/acp-structured-acquire.ts @@ -256,6 +256,7 @@ export async function acquireAcpStructuredSession(input: { fence: acquire.fence, acquisitionGeneration: generation, spec, + cwd: launch.cwd, permissionModes, connection, lane: null, diff --git a/src/main/acp/session/acp-structured-session-contract.ts b/src/main/acp/session/acp-structured-session-contract.ts index 5af81feb4e2..cad258b796d 100644 --- a/src/main/acp/session/acp-structured-session-contract.ts +++ b/src/main/acp/session/acp-structured-session-contract.ts @@ -15,6 +15,8 @@ export type AcpStructuredSession = { fence: number acquisitionGeneration: string spec: AcpLaunchSpec + /** The session's folder on the agent's host. */ + cwd: string /** Session modes this launch reports as Orca's permission modes. */ permissionModes: Readonly>> | undefined childStops?: AcpChildStops diff --git a/src/main/acp/session/children/acp-structured-child.ts b/src/main/acp/session/children/acp-structured-child.ts index 0f26e9ba4b2..7f340c68c97 100644 --- a/src/main/acp/session/children/acp-structured-child.ts +++ b/src/main/acp/session/children/acp-structured-child.ts @@ -12,6 +12,7 @@ export type AcpStructuredChild = Pick< | 'fence' | 'acquisitionGeneration' | 'spec' + | 'cwd' | 'permissionModes' | 'connection' | 'closeRequested' diff --git a/src/main/acp/turns/acp-file-mention-links.ts b/src/main/acp/turns/acp-file-mention-links.ts new file mode 100644 index 00000000000..eaad4abf79f --- /dev/null +++ b/src/main/acp/turns/acp-file-mention-links.ts @@ -0,0 +1,66 @@ +import { realpath, stat } from 'node:fs/promises' +import { isAbsolute, relative, resolve, sep } from 'node:path' +import { pathToFileURL } from 'node:url' +import { nativeChatTextAttachmentParts } from '../../../shared/native-chat-text-attachment-references' +import type { ContentBlock } from '../generated/acp-protocol.generated' + +const TRAILING_PUNCTUATION = /[.,;:!?)\]}`"'*]+$/ + +/** The real path of a file inside `realRoot`; symlinks and `..` cannot leave it. */ +async function fileInside( + realRoot: string, + path: string, + maxBytes: number +): Promise { + const target = await realpath(resolve(realRoot, path)).catch(() => null) + // Byte-exact: Unicode or case folding would admit a sibling folder on Linux. + const inside = target ? relative(realRoot, target) : '' + if ( + !target || + !inside || + inside === '..' || + inside.startsWith(`..${sep}`) || + isAbsolute(inside) + ) { + return null + } + return (await stat(target).then( + (info) => info.isFile() && info.size <= maxBytes, + () => false + )) + ? target + : null +} + +/** One link per `@path` naming a file inside `root`; larger files stay text for the agent's own tools. */ +export async function acpFileMentionLinks( + texts: readonly string[], + root: string, + maxBytes: number +): Promise { + const paths = new Set( + texts.flatMap((text) => + nativeChatTextAttachmentParts(text, true, () => true).flatMap((part) => + part.path ? [part.path] : [] + ) + ) + ) + const realRoot = paths.size > 0 ? await realpath(root).catch(() => null) : null + if (!realRoot) { + return [] + } + const links = new Map() + for (const path of paths) { + // Punctuation after a mention belongs to the sentence unless the name itself exists. + const trimmed = path.replace(TRAILING_PUNCTUATION, '') + const literal = await fileInside(realRoot, path, maxBytes) + const name = literal ? path : trimmed + const target = + literal ?? + (trimmed && trimmed !== path ? await fileInside(realRoot, trimmed, maxBytes) : null) + if (target && !links.has(target)) { + links.set(target, { type: 'resource_link', uri: pathToFileURL(target).href, name }) + } + } + return [...links.values()] +} diff --git a/src/main/acp/turns/acp-prompt-content.test.ts b/src/main/acp/turns/acp-prompt-content.test.ts index 1d48824649f..b948f0de45f 100644 --- a/src/main/acp/turns/acp-prompt-content.test.ts +++ b/src/main/acp/turns/acp-prompt-content.test.ts @@ -1,6 +1,7 @@ -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' +import { pathToFileURL } from 'node:url' import { afterAll, describe, expect, it } from 'vitest' import type { AgentJournalMessageItem } from '../../../shared/agent-session/journal/agent-session-journal-types' @@ -64,4 +65,35 @@ describe('ACP prompt content', () => { attachment: { reason: 'totalTooLarge' } }) }) + + it('links mentioned files inside the session folder and keeps the text as typed', async () => { + const root = join(dir, 'project') + mkdirSync(join(root, 'my dir'), { recursive: true }) + writeFileSync(join(root, 'a.txt'), 'a') + writeFileSync(join(root, 'my dir', 'b.txt'), 'b') + writeFileSync(join(root, "it's.txt"), 'c') + writeFileSync(join(root, 'report!'), 'd') + writeFileSync(join(root, 'report'), 'e') + // OpenCode 2 fails the whole turn for a linked file over 20 MiB. + writeFileSync(join(root, 'big.log'), Buffer.alloc(20 * 1024 * 1024 + 1)) + mkdirSync(join(dir, 'outside')) + writeFileSync(join(dir, 'outside', 'c.txt'), 'secret') + // A junction needs no privilege on Windows. + symlinkSync(join(dir, 'outside'), join(root, 'escape'), 'junction') + const text = + 'Read @a.txt. and @"my dir/b.txt" @it\'s.txt, see:@report! not @../outside/c.txt @escape/c.txt @missing.txt @big.log' + const real = realpathSync(root) + + expect(await acpPromptBlocks(message({ type: 'text', text }), true, root)).toEqual([ + { type: 'text', text }, + { type: 'resource_link', uri: pathToFileURL(join(real, 'a.txt')).href, name: 'a.txt' }, + { + type: 'resource_link', + uri: pathToFileURL(join(real, 'my dir', 'b.txt')).href, + name: 'my dir/b.txt' + }, + { type: 'resource_link', uri: pathToFileURL(join(real, "it's.txt")).href, name: "it's.txt" }, + { type: 'resource_link', uri: pathToFileURL(join(real, 'report!')).href, name: 'report!' } + ]) + }) }) diff --git a/src/main/acp/turns/acp-prompt-content.ts b/src/main/acp/turns/acp-prompt-content.ts index 81a2db01b67..069a223f48d 100644 --- a/src/main/acp/turns/acp-prompt-content.ts +++ b/src/main/acp/turns/acp-prompt-content.ts @@ -26,13 +26,14 @@ import { acpAgentName } from '../session/acp-structured-agent-definitions' import type { AcpStructuredConnection } from '../process/acp-structured-connection' import { acpSessionAcceptsImages } from '../protocol/acp-image-capability' import type { ContentBlock } from '../generated/acp-protocol.generated' +import { acpFileMentionLinks } from './acp-file-mention-links' // Reserve space for the JSON-RPC envelope around the prompt blocks. const ACP_PROMPT_LINE_BUDGET = ACP_MAX_LINE_BYTES - 64 * 1024 // Budget raw images for base64's 4:3 expansion and leave room for text. const ACP_PROMPT_TOTAL_IMAGE_MAX_BYTES = Math.floor((ACP_PROMPT_LINE_BUDGET * 3) / 4) - 1024 * 1024 -// OpenCode 2 fails the turn for any attachment over 20 MiB. -const ACP_PROMPT_PER_IMAGE_MAX_BYTES = 20 * 1024 * 1024 +// OpenCode 2 fails the turn for any image or linked file over 20 MiB. +const ACP_PROMPT_PER_ATTACHMENT_MAX_BYTES = 20 * 1024 * 1024 const MAX_IMAGE_COUNT = 20 const READ_TIMEOUT_MS = 15_000 @@ -74,8 +75,8 @@ async function imageBlock( if (bytes === 0) { throw attachmentProblem({ reason: 'empty' }) } - if (bytes > ACP_PROMPT_PER_IMAGE_MAX_BYTES) { - throw attachmentProblem({ reason: 'tooLarge', limit: ACP_PROMPT_PER_IMAGE_MAX_BYTES }) + if (bytes > ACP_PROMPT_PER_ATTACHMENT_MAX_BYTES) { + throw attachmentProblem({ reason: 'tooLarge', limit: ACP_PROMPT_PER_ATTACHMENT_MAX_BYTES }) } if (bytes > remainingBytes) { throw attachmentProblem({ reason: 'totalTooLarge', limit: ACP_PROMPT_TOTAL_IMAGE_MAX_BYTES }) @@ -91,7 +92,7 @@ async function imageBlock( block: { type: 'text' as const, text: formatNativeChatFileReference(path) }, bytes: 0 } - const maxBytes = Math.min(remainingBytes, ACP_PROMPT_PER_IMAGE_MAX_BYTES) + const maxBytes = Math.min(remainingBytes, ACP_PROMPT_PER_ATTACHMENT_MAX_BYTES) if (!mimeType || maxBytes <= 0) { return fallback } @@ -119,9 +120,11 @@ async function imageBlock( return { block: { type: 'image', mimeType, data: data.toString('base64') }, bytes: data.length } } +/** `mentionRoot` links each `@path` there, for agents that read only linked files. */ export async function acpPromptBlocks( body: AgentJournalMessageItem, - acceptsImages: boolean + acceptsImages: boolean, + mentionRoot?: string ): Promise { // Reserve later text and file references before allocating image capacity. const blocks: ContentBlock[] = body.blocks.map((block) => { @@ -131,6 +134,12 @@ export async function acpPromptBlocks( const path = block.type === 'image-ref' ? hostImagePath(block) : null return { type: 'text', text: path ? formatNativeChatFileReference(path) : '' } }) + if (mentionRoot !== undefined) { + const texts = body.blocks.flatMap((block) => (block.type === 'text' ? [block.text] : [])) + blocks.push( + ...(await acpFileMentionLinks(texts, mentionRoot, ACP_PROMPT_PER_ATTACHMENT_MAX_BYTES)) + ) + } const blockBytes = (block: ContentBlock): number => Buffer.byteLength(JSON.stringify(block)) let encodedBytes = 2 + Math.max(0, blocks.length - 1) + blocks.reduce((n, b) => n + blockBytes(b), 0) @@ -194,6 +203,7 @@ export async function acpDispatchPrompt( session: { spec: Pick connection: Pick + cwd: string } ): Promise { if (isWithheldCommand(body, session.spec.dialect.withheldCommands ?? [])) { @@ -203,7 +213,11 @@ export async function acpDispatchPrompt( }) } try { - return await acpPromptBlocks(body, await acpSessionAcceptsImages(session)) + return await acpPromptBlocks( + body, + await acpSessionAcceptsImages(session), + session.spec.dialect.linksFileMentions ? session.cwd : undefined + ) } catch (error) { if (error instanceof AcpPromptContentError) { return agentSessionFailureWords(error.failure, { From e4eecd847c598a887dfdc8885a2fd896dabbfafa Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 01:31:41 -0800 Subject: [PATCH 057/178] test: remove low-value workspace wrapper and notice cases (#41938) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 3 - .../appearance/notice-surface-tokens.test.ts | 47 +------ .../setup/FullDiskAccessSetupPrompt.test.ts | 1 - .../NewWorkspaceComposerNameSection.test.tsx | 104 --------------- .../ProjectCombobox.dialog-handoff.test.tsx | 120 ------------------ .../setup/DirectWorkItemSetupDialog.test.tsx | 36 ------ ...smart-workspace-field-availability.test.ts | 47 ------- 7 files changed, 1 insertion(+), 357 deletions(-) delete mode 100644 src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx delete mode 100644 src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx delete mode 100644 src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx delete mode 100644 src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 6e57540494d..a2b1f69743b 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -4755,9 +4755,6 @@ "src/renderer/src/components/native-chat/pane/actions/use-structured-native-chat-submit-reveal.test.ts": 2304, "src/renderer/src/components/network/CustomAddressDialog.test.tsx": 568, "src/renderer/src/components/new-workspace/branch-selection/ComposerParentWorktreePicker.test.tsx": 870, - "src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx": 5296, - "src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx": 2493, - "src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx": 1043, "src/renderer/src/components/new-workspace/RunTargetCombobox.host-identity.test.tsx": 856, "src/renderer/src/components/new-workspace/SetProjectLocationDialog.host-identity.test.tsx": 1094, "src/renderer/src/components/new-workspace/project-location/SetProjectLocationDialog.test.tsx": 1387, diff --git a/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts b/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts index 79a1923a60c..e9c4e7936e4 100644 --- a/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts +++ b/src/renderer/src/app-shell/appearance/notice-surface-tokens.test.ts @@ -1,8 +1,6 @@ -import { readFileSync } from 'node:fs' import { describe, expect, it } from 'vitest' -import { hexToRgb, rgbaToHex, type Rgba } from '../../../../shared/color/rgba-contrast' +import { hexToRgb, type Rgba } from '../../../../shared/color/rgba-contrast' import { noticeSurfaceTokens } from './notice-surface-tokens' -import { safePluginThemeTokens } from './plugin-theme-tokens' const DARK_BUILT_INS = { '--foreground': '#f0f0f0', @@ -71,16 +69,6 @@ function expectReadable(themeTokens: Record): void { } describe('noticeSurfaceTokens', () => { - it("paints Orca's dark notice colors for the built-in palette", () => { - expect(noticeSurfaceTokens({}, 'dark', DARK_BUILT_INS, probe)).toEqual({ - '--notice-surface': '#262626', - '--notice-secondary': '#3a3a3a', - '--notice-secondary-edge': 'transparent', - '--notice-muted': '#3a3a3a', - '--notice-muted-foreground': '#acacac' - }) - }) - // Popovers the theme validator accepts that once painted notice text under 4.5:1: #5a5a5a // through Chromium's color-mix rounding and an over-lifted 80% description, #383838 through hex // rounding, white-on-dark-base through dark muted panels, the rest unlifted. @@ -94,18 +82,6 @@ describe('noticeSurfaceTokens', () => { expectReadable(themeTokens) }) - it('keeps notice text 4.5:1 for every gray popover the validator accepts', () => { - const accepted = Array.from({ length: 256 }, (_, gray) => ({ - '--popover': rgbaToHex([gray, gray, gray, 1]) - })).filter( - (tokens) => safePluginThemeTokens(tokens, 'dark', DARK_BUILT_INS, probe)['--popover'] - ) - expect(accepted.length).toBeGreaterThan(50) - for (const tokens of accepted) { - expectReadable(tokens) - } - }) - it('outlines secondary buttons only when their fill is gone', () => { const tokensFor = (popoverForeground: string): Record => noticeSurfaceTokens( @@ -121,25 +97,4 @@ describe('noticeSurfaceTokens', () => { expect(filled['--notice-secondary']).not.toBe(filled['--notice-surface']) expect(filled['--notice-secondary-edge']).toBe('transparent') }) - - it('remaps the surface paired with every foreground the notice scope remaps', () => { - const css = readFileSync(new URL('../../assets/main.css', import.meta.url), 'utf8') - const scope = css.match( - /\[data-sonner-toaster\],\s*\.notification-card-stack\s*\{([^}]*)\}/ - )?.[1] - expect(scope).toBeDefined() - const remapped = new Set([...(scope ?? '').matchAll(/(--[\w-]+):/g)].map(([, name]) => name)) - // Why: a foreground remapped onto the popover's polarity over an unremapped surface (outline - // buttons: text-foreground on bg-background) paints light-on-light or dark-on-dark. - const pairs = [ - ['--foreground', '--background'], - ['--card-foreground', '--card'], - ['--secondary-foreground', '--secondary'], - ['--muted-foreground', '--muted'] - ] - for (const [foreground, surface] of pairs) { - expect(remapped.has(foreground), foreground).toBe(true) - expect(remapped.has(surface), `${surface} pairs with ${foreground}`).toBe(true) - } - }) }) diff --git a/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts b/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts index cf98f6efee3..6c6b954d0f1 100644 --- a/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts +++ b/src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts @@ -107,7 +107,6 @@ describe('FullDiskAccessSetupPrompt state helpers', () => { expect(container.textContent).not.toContain('Full Disk Access') expect(getStatus).not.toHaveBeenCalled() - expect(window.api.developerPermissions.request).not.toHaveBeenCalled() root.unmount() }) }) diff --git a/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx b/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx deleted file mode 100644 index c410c3ca40a..00000000000 --- a/src/renderer/src/components/new-workspace/composer/NewWorkspaceComposerNameSection.test.tsx +++ /dev/null @@ -1,104 +0,0 @@ -// @vitest-environment happy-dom - -import React from 'react' -import { cleanup, render, screen, waitFor } from '@testing-library/react' -import userEvent from '@testing-library/user-event' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { NewWorkspaceComposerNameSection } from './NewWorkspaceComposerNameSection' - -vi.mock('@/components/new-workspace/SmartWorkspaceNameField', () => ({ - default: ({ value, selectedSource }: { value: string; selectedSource?: { label: string } }) => ( -
- {selectedSource?.label} - -
- ) -})) - -function branchProps(): React.ComponentProps { - return { - eligibleRepos: [], - repoId: 'repo', - onRepoChange: vi.fn(), - name: 'Payments review', - onNameValueChange: vi.fn(), - onSmartGitHubItemSelect: vi.fn(), - onSmartGitLabItemSelect: vi.fn(), - onSmartBranchSelect: vi.fn(), - onSmartLinearIssueSelect: vi.fn(), - smartNameSelection: { kind: 'branch', label: 'origin/feature/payments' }, - onClearSmartNameSelection: vi.fn(), - selectedRepoRequiresConnection: false, - selectedRepoIsGit: true, - branchesEnabled: true, - forkPushWarning: null, - canReuseSelectedBranch: true, - reuseEligibleBranch: 'feature/payments', - branchNameOverride: undefined, - onBranchNameOverrideChange: vi.fn(), - reuseSelectedBranch: true, - onReuseSelectedBranchChange: vi.fn(), - baseBranch: 'refs/remotes/origin/feature/payments', - onNamePlainEnter: vi.fn() - } -} - -afterEach(cleanup) - -describe('NewWorkspaceComposerNameSection branch reuse', () => { - it('chooses a new branch by pointer and the existing branch by keyboard without changing the source', async () => { - const user = userEvent.setup() - const props = branchProps() - const { rerender } = render() - - expect(screen.getByText('origin/feature/payments')).toBeTruthy() - expect(screen.getByText('feature/payments')).toBeTruthy() - expect(screen.queryByRole('textbox', { name: 'New branch name' })).toBeNull() - expect( - screen.getByRole('radio', { name: 'Existing branch' }).getAttribute('aria-checked') - ).toBe('true') - await user.click(screen.getByRole('radio', { name: 'New branch' })) - expect(props.onReuseSelectedBranchChange).toHaveBeenLastCalledWith(false) - - rerender() - expect( - screen.getByRole('radio', { name: 'Existing branch' }).getAttribute('aria-checked') - ).toBe('false') - expect(document.activeElement).toBe(screen.getByRole('textbox', { name: 'New branch name' })) - await user.paste('review/payments') - expect(props.onBranchNameOverrideChange).toHaveBeenLastCalledWith('review/payments') - await user.click(screen.getByRole('radio', { name: 'New branch' })) - expect(document.activeElement).toBe(screen.getByRole('textbox', { name: 'New branch name' })) - screen.getByRole('radio', { name: 'New branch' }).focus() - await user.keyboard('{ArrowUp>}') - await waitFor(() => expect(props.onReuseSelectedBranchChange).toHaveBeenLastCalledWith(true)) - await user.keyboard('{/ArrowUp}') - expect(props.onNameValueChange).not.toHaveBeenCalled() - expect(props.onClearSmartNameSelection).not.toHaveBeenCalled() - }) - - it('opens the existing workspace with the keyboard without submitting or changing branch intent', async () => { - const user = userEvent.setup() - const props = branchProps() - const openWorkspace = vi.fn() - const submit = vi.fn((event: React.FormEvent) => event.preventDefault()) - render( -
- - - ) - - const openButton = screen.getByRole('button', { name: 'Open existing workspace' }) - await user.tab() - await user.tab() - await user.tab() - expect(document.activeElement).toBe(openButton) - await user.keyboard('{Enter}') - - expect(openWorkspace).toHaveBeenCalledOnce() - expect(submit).not.toHaveBeenCalled() - expect(props.onReuseSelectedBranchChange).not.toHaveBeenCalled() - expect(props.onNameValueChange).not.toHaveBeenCalled() - expect(props.onClearSmartNameSelection).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx b/src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx deleted file mode 100644 index c5238a5c8c7..00000000000 --- a/src/renderer/src/components/new-workspace/project-selection/ProjectCombobox.dialog-handoff.test.tsx +++ /dev/null @@ -1,120 +0,0 @@ -// @vitest-environment happy-dom - -import React, { act, Suspense, useEffect, useRef, useState } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog' -import ProjectCombobox from './ProjectCombobox' -import { TooltipProvider } from '@/components/ui/tooltip' - -vi.mock('./use-recent-project-ids', () => ({ useRecentProjectIds: () => [] })) - -// Mirrors a lazily loaded creation dialog that focuses its primary action from a mount effect. -let resolveLateDialog: () => void = () => {} -const LateAddDialogBody = React.lazy( - () => - new Promise<{ default: () => React.JSX.Element }>((resolve) => { - resolveLateDialog = () => - resolve({ - default: function Body(): React.JSX.Element { - const browseRef = useRef(null) - useEffect(() => browseRef.current?.focus(), []) - return - } - }) - }) -) - -function LateDialogComposer(): React.JSX.Element { - const [adding, setAdding] = useState(false) - return ( - - - New workspace - setAdding(true)} - /> - {adding ? ( - - - - Add a project - - - - - ) : null} - - - ) -} - -let container: HTMLDivElement -let root: Root - -beforeEach(() => { - // Radix Presence retains closing content when the production exit animation runs. - const getStyle = window.getComputedStyle.bind(window) - vi.spyOn(window, 'getComputedStyle').mockImplementation((element, ...args) => { - const style = getStyle(element, ...args) - if (element.getAttribute('data-slot') === 'popover-content') { - return new Proxy(style, { - get: (target, property) => - property === 'animationName' - ? element.getAttribute('data-state') === 'closed' - ? 'exit' - : 'enter' - : // oxlint-disable-next-line anti-slop/no-reflect-get -- Proxy `get` trap: raw string|symbol pass-through; the receiver stays the target on purpose. - Reflect.get(target, property) - }) - } - return style - }) - container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) -}) - -afterEach(async () => { - await act(async () => root.unmount()) - container.remove() - vi.restoreAllMocks() -}) - -function projectField(): HTMLInputElement { - return document.querySelector('[role="combobox"][aria-label="Project"]')! -} - -function addOption(): HTMLElement { - return Array.from(document.querySelectorAll('[role="option"]')).find((option) => - option.textContent?.includes('Add project') - )! -} - -it('keeps the selector closed when a late creation dialog focuses before its trap mounts', async () => { - await act(async () => - root.render( - - - - ) - ) - // Dialog autofocus lands on the field, so the composer trap records it as last focused. - expect(document.activeElement).toBe(projectField()) - expect(document.querySelector('[role="listbox"]')).not.toBeNull() - - await act(async () => { - addOption().dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true })) - addOption().click() - }) - expect(document.querySelector('[role="listbox"]')).toBeNull() - // Let the closed selector release its focus scope before the lazy dialog arrives. - await act(async () => new Promise((resolve) => setTimeout(resolve, 0))) - await act(async () => resolveLateDialog()) - - expect(document.querySelector('[role="listbox"]')).toBeNull() - expect(projectField().getAttribute('aria-expanded')).toBe('false') -}) diff --git a/src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx b/src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx deleted file mode 100644 index 074fd0e043b..00000000000 --- a/src/renderer/src/components/new-workspace/setup/DirectWorkItemSetupDialog.test.tsx +++ /dev/null @@ -1,36 +0,0 @@ -// @vitest-environment happy-dom -import { readDirectSetupRequest } from '@/lib/workspace/creation/work-item/direct-work-item-setup-test-fixture' -import { act, cleanup, fireEvent, render, screen } from '@testing-library/react' -import { afterEach, expect, it } from 'vitest' -import { i18n } from '@/i18n/i18n' -import { useAppStore } from '@/store' -import { - requestDirectWorkItemSetup, - type DirectWorkItemSetupRequest -} from '@/lib/workspace/creation/work-item/direct-work-item-setup' -import { DirectWorkItemSetupDialog } from './DirectWorkItemSetupDialog' -import type { WorkspaceSetupInspection as DirectSetupInspection } from '@/lib/workspace/creation/setup/workspace-setup-preparation' - -const initial = useAppStore.getState() -afterEach(async () => { - cleanup() - await i18n.changeLanguage('en') - useAppStore.getState().closeModal() - useAppStore.setState(initial, true) -}) -function request(): DirectWorkItemSetupRequest { - return readDirectSetupRequest(useAppStore.getState().modalData.directSetup) -} - -it('ignores recovery after cancellation without closing the replacement modal', async () => { - const retry = Promise.withResolvers() - const result = requestDirectWorkItemSetup('Repo', { kind: 'unavailable' }, () => retry.promise) - const old = request() - render() - fireEvent.click(screen.getByRole('button', { name: 'Retry' })) - useAppStore.getState().openModal('quick-open', { section: 'appearance' }) - await expect(result).resolves.toBe('cancelled') - await act(async () => retry.resolve({ kind: 'decided', decision: 'run' })) - expect(old.isPending()).toBe(false) - expect(useAppStore.getState().modalData).toEqual({ section: 'appearance' }) -}) diff --git a/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.test.ts b/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.test.ts deleted file mode 100644 index cf54f8a7fd5..00000000000 --- a/src/renderer/src/components/new-workspace/source-search/use-smart-workspace-field-availability.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { resolveLocalGitlabAvailable } from './use-smart-workspace-field-availability' - -describe('resolveLocalGitlabAvailable', () => { - const lastKnown = new Map([['host', true]]) - - it('uses the current preflight answer when it is for this context', () => { - expect( - resolveLocalGitlabAvailable({ - preflightStatusCurrent: true, - glabInstalled: false, - contextKey: 'host', - lastKnown - }) - ).toBe(false) - expect( - resolveLocalGitlabAvailable({ - preflightStatusCurrent: true, - glabInstalled: true, - contextKey: 'wsl:Ubuntu', - lastKnown - }) - ).toBe(true) - }) - - it('keeps the last known answer while this context is being re-checked', () => { - expect( - resolveLocalGitlabAvailable({ - preflightStatusCurrent: false, - glabInstalled: undefined, - contextKey: 'host', - lastKnown - }) - ).toBe(true) - }) - - it('stays hidden for a context that was never checked', () => { - expect( - resolveLocalGitlabAvailable({ - preflightStatusCurrent: false, - glabInstalled: undefined, - contextKey: 'wsl:Debian', - lastKnown - }) - ).toBe(false) - }) -}) From 616a5815751f33a5ca849d3ea5360fcd07f76288 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 13 Nov 2026 01:32:11 -0800 Subject: [PATCH 058/178] feat(usage): add a Claude usage polling switch (#41921) Add an on-by-default Claude usage polling switch with live-terminal-only behavior when disabled, cancellation and account attribution safeguards, and translations for all supported locales. Fixes #27151. Fixes #39502. Fixes #37037. Co-authored-by: Sibyl --- .../content/docs/agents/usage-tracking.mdx | 10 +- .../accounts/provider-account-services.ts | 13 ++ .../providers/claude/claude-usage-result.ts | 8 + .../rate-limits/service/rate-limit-service.ts | 26 +++ .../service/service-account-refresh.ts | 7 + .../service/service-fetch-policy.ts | 53 ++++- .../service/service-full-cycle-preparation.ts | 8 +- .../service/service-inactive-accounts.ts | 5 +- .../rate-limits/service/service-polling.ts | 1 + .../service/service-provider-cycles.ts | 9 +- .../service-refresh-orchestration.test.ts | 211 ++++++++++++++++++ src/main/rate-limits/service/service-state.ts | 10 +- src/main/rate-limits/service/service-types.ts | 1 + .../claude/accounts-pane-claude-section.tsx | 9 + .../claude-oauth-usage-setting.fixture.tsx | 42 ++++ .../claude/claude-oauth-usage-setting.tsx | 67 ++++++ src/renderer/src/i18n/locales/de.json | 5 + src/renderer/src/i18n/locales/en.json | 5 + src/renderer/src/i18n/locales/es.json | 5 + src/renderer/src/i18n/locales/fr.json | 5 + src/renderer/src/i18n/locales/ja.json | 5 + src/renderer/src/i18n/locales/ko.json | 5 + src/renderer/src/i18n/locales/pt.json | 5 + src/renderer/src/i18n/locales/ru.json | 7 +- src/renderer/src/i18n/locales/tr.json | 5 + src/renderer/src/i18n/locales/zh-Hant.json | 5 + src/renderer/src/i18n/locales/zh.json | 5 + src/shared/default-global-settings.ts | 1 + src/shared/global-settings-types.ts | 2 + 29 files changed, 530 insertions(+), 10 deletions(-) create mode 100644 src/renderer/src/components/settings/accounts/claude/claude-oauth-usage-setting.fixture.tsx create mode 100644 src/renderer/src/components/settings/accounts/claude/claude-oauth-usage-setting.tsx diff --git a/docs/site/content/docs/agents/usage-tracking.mdx b/docs/site/content/docs/agents/usage-tracking.mdx index f0d9b95daf2..6c1893bfd0e 100644 --- a/docs/site/content/docs/agents/usage-tracking.mdx +++ b/docs/site/content/docs/agents/usage-tracking.mdx @@ -14,7 +14,15 @@ Orca fetches usage for Claude Code, Codex, Gemini, OpenCode Go, Kimi Code, MiniM Orca asks each provider for your usage over the network, using the login that agent already has or a key or session cookie you add in Orca Settings. -For Claude, Orca requests `https://api.anthropic.com/api/oauth/usage` with the token from your existing Claude Code login; it does not ask for extra credentials. Triggers include: +### Claude usage polling + +**Settings → Accounts → Claude → Claude usage polling** is on by default. Turn it off to stop Orca's requests to `https://api.anthropic.com/api/oauth/usage`, including manual refreshes, account switching, inactive-account previews, and refreshes from mobile or web clients. Turning it off cancels pending usage requests and removes their cached figures while retaining live terminal readings. A request already sent cannot be recalled. + +With polling off, the meter uses Claude Code's [status-line data](https://code.claude.com/docs/en/statusline#rate-limit-usage): five-hour and weekly percentages and reset times. This requires a supported Claude Code version and Orca's status-line feed. Orca preserves a user-owned status line, so those sessions do not supply this feed. Without a reading, usage is unavailable until a terminal session reports it. Model-specific weekly windows, credits, and inactive-account readings require polling. + +The persisted setting is `claudeOAuthUsageEnabled: false` to disable polling. It applies on the host running the agents, including a headless Orca server; configure each host separately. Hiding the Claude status-bar item controls visibility independently. + +When polling is enabled, Orca uses the token from your existing Claude Code login. Triggers include: - Once the Orca window is active after startup, then about every 15 minutes while it stays visible and focused. - Bringing the window back to the front when the last check is more than 5 minutes old (sooner after a failed check). diff --git a/src/main/providers/accounts/provider-account-services.ts b/src/main/providers/accounts/provider-account-services.ts index a9cdb080fc9..65bcb2c0ca9 100644 --- a/src/main/providers/accounts/provider-account-services.ts +++ b/src/main/providers/accounts/provider-account-services.ts @@ -69,6 +69,18 @@ export function createProviderAccountServices( ) store.onSettingsChanged((updates, settings) => { void agentView.reconcile(settings) + if (updates.claudeOAuthUsageEnabled === false) { + rateLimits.discardClaudeOAuthUsage() + } else if (updates.claudeOAuthUsageEnabled === true) { + void rateLimits + .refreshClaudeForTarget(rateLimits.getState().claudeTarget) + .catch((error: unknown) => { + console.warn( + '[rate-limits] Failed to refresh Claude usage after enabling polling:', + error + ) + }) + } // Auto runtime selection remains live as provider settings change. void syncAccountRuntimeTargets(updates, settings).catch((error) => console.warn('[rate-limits] Failed to apply account runtime target:', error) @@ -137,6 +149,7 @@ export function createProviderAccountServices( apiKey: readZcodePlanApiKey() ?? '' })) rateLimits.setGeminiCliOAuthEnabledResolver(() => store.getSettings().geminiCliOAuthEnabled) + rateLimits.setClaudeOAuthUsageEnabledResolver(() => store.getSettings().claudeOAuthUsageEnabled) // Reuse the meter switch so hidden Antigravity usage does not spawn agy. rateLimits.setAntigravityUsageEnabledResolver(() => store.getUI().statusBarItems.includes('antigravity') diff --git a/src/main/rate-limits/providers/claude/claude-usage-result.ts b/src/main/rate-limits/providers/claude/claude-usage-result.ts index c729e503f78..06b33d52aa0 100644 --- a/src/main/rate-limits/providers/claude/claude-usage-result.ts +++ b/src/main/rate-limits/providers/claude/claude-usage-result.ts @@ -30,6 +30,14 @@ export function makeClaudeUsageResult( } } +// Not idle: the meter draws idle as pending, and nothing here will settle it. +export function claudeOAuthUsageDisabledResult(): ProviderRateLimits { + return makeClaudeUsageResult('unavailable', 'Usage appears when a Claude session runs.', { + failureKind: 'usage-unavailable', + attemptedSources: [] + }) +} + // Account rows explain host/profile failures; usage keeps its generic message. export function claudeUsageUnavailable(): ProviderRateLimits { return makeClaudeUsageResult('error', 'Claude usage is unavailable right now.', { diff --git a/src/main/rate-limits/service/rate-limit-service.ts b/src/main/rate-limits/service/rate-limit-service.ts index 9dcc556cc53..fd199a2bb73 100644 --- a/src/main/rate-limits/service/rate-limit-service.ts +++ b/src/main/rate-limits/service/rate-limit-service.ts @@ -19,6 +19,7 @@ import type { OpenCodeGoRateLimitConfig, MiniMaxRateLimitConfig, AntigravityUsageEnabledResolver, + ClaudeOAuthUsageEnabledResolver, MuseUsageEnabledResolver, ZcodePlanRateLimitConfig, GeminiCliOAuthEnabledResolver, @@ -73,6 +74,27 @@ export class RateLimitService extends RateLimitServiceAccountRefresh { this.geminiCliOAuthEnabledResolver = resolver } + setClaudeOAuthUsageEnabledResolver(resolver: ClaudeOAuthUsageEnabledResolver): void { + this.claudeOAuthUsageEnabledResolver = resolver + } + + // Retain only readings actually received from the terminal feed. + discardClaudeOAuthUsage(): void { + this.claudeUsageAbortController.abort() + this.claudeUsageAbortController = new AbortController() + this.claudeFetchGeneration += 1 + this.inactiveClaudeAccountsGeneration += 1 + this.inactiveClaudeCache.clear() + this.inactiveClaudeFetching.clear() + this.lastInactiveClaudeFetchAt = 0 + this.lastClaudeCheckCompletedAt = null + this.activeFailureStreakByProvider.claude = 0 + this.updateState({ ...this.state, claude: this.claudeUsageWithoutOAuth() }) + if (!this.lastClaudeAuthSnapshot) { + void this.fetchClaudeOnly({ force: true }) + } + } + setAntigravityUsageEnabledResolver(resolver: AntigravityUsageEnabledResolver): void { this.antigravityUsageEnabledResolver = resolver } @@ -162,6 +184,10 @@ export class RateLimitService extends RateLimitServiceAccountRefresh { void this.fetchAll() } else { this.scheduleDeferredStartupRefresh() + // Passive terminal usage needs account attribution even without a usage viewer. + if (!this.isClaudeOAuthUsageEnabled()) { + void this.fetchClaudeOnly({ force: true }) + } } this.startTimer() } diff --git a/src/main/rate-limits/service/service-account-refresh.ts b/src/main/rate-limits/service/service-account-refresh.ts index fdea05aff72..0cb65b2070f 100644 --- a/src/main/rate-limits/service/service-account-refresh.ts +++ b/src/main/rate-limits/service/service-account-refresh.ts @@ -57,6 +57,9 @@ export abstract class RateLimitServiceAccountRefresh extends RateLimitServiceIna provider: 'claude' | 'codex', options: { notWithinMs?: number } = {} ): boolean { + if (provider === 'claude' && !this.isClaudeOAuthUsageEnabled()) { + return false + } const limits = this.state[provider] const now = Date.now() if (this.isFetching || this.isRetryAfterActive(limits)) { @@ -230,6 +233,8 @@ export abstract class RateLimitServiceAccountRefresh extends RateLimitServiceIna this.activeFailureStreakByProvider.claude = 0 // Outgoing statusline posts must not land on the incoming account during the switch. this.lastClaudeAuthSnapshot = null + this.liveClaudeUsage = null + this.usageReadingAccounts.claude = undefined this.lastInactiveClaudeFetchAt = 0 this.updateState({ ...this.state, @@ -249,6 +254,8 @@ export abstract class RateLimitServiceAccountRefresh extends RateLimitServiceIna if (targetChanged) { // Outgoing target posts must not land on the incoming target during the switch. this.lastClaudeAuthSnapshot = null + this.liveClaudeUsage = null + this.usageReadingAccounts.claude = undefined } this.updateState({ ...this.state, diff --git a/src/main/rate-limits/service/service-fetch-policy.ts b/src/main/rate-limits/service/service-fetch-policy.ts index 8af5c4b22b0..32512ce90c0 100644 --- a/src/main/rate-limits/service/service-fetch-policy.ts +++ b/src/main/rate-limits/service/service-fetch-policy.ts @@ -15,10 +15,24 @@ import { isStaleClaudeUsageWindow, mapClaudeUsageWindow } from '../providers/claude/claude-usage-window' +import { claudeOAuthUsageDisabledResult } from '../providers/claude/claude-usage-result' import { readUserClaudeConfigDir } from '../../claude-accounts/profiles/claude-profile-paths' import { getClaudeProfileRouter } from '../../claude-accounts/profiles/routing/claude-profile-installed-router' export abstract class RateLimitServiceFetchPolicy extends RateLimitServiceFetchTargets { + protected isClaudeOAuthUsageEnabled(): boolean { + return this.claudeOAuthUsageEnabledResolver?.() ?? true + } + + protected claudeUsageFetchSignal(signal: AbortSignal): AbortSignal { + return AbortSignal.any([signal, this.claudeUsageAbortController.signal]) + } + + protected claudeUsageWithoutOAuth(): ProviderRateLimits { + const live = this.liveClaudeUsage ? withoutResetWindows(this.liveClaudeUsage, Date.now()) : null + return live?.session || live?.weekly ? live : claudeOAuthUsageDisabledResult() + } + protected getMiniMaxCredentialError(message: string): ProviderRateLimits { return { provider: 'minimax', @@ -44,6 +58,12 @@ export abstract class RateLimitServiceFetchPolicy extends RateLimitServiceFetchT } } + protected claudeStateAtCycleStart(limits: ProviderRateLimits | null): ProviderRateLimits { + return this.isClaudeOAuthUsageEnabled() + ? this.withFetchingStatus(limits, 'claude') + : this.claudeUsageWithoutOAuth() + } + // Retry-After remains binding even when live posts restore ok display; early polls waste budget and prolong 429s. protected isRetryAfterActive(limits: ProviderRateLimits | null): boolean { return Boolean(limits?.usageMetadata?.retryAtMs && limits.usageMetadata.retryAtMs > Date.now()) @@ -70,6 +90,9 @@ export abstract class RateLimitServiceFetchPolicy extends RateLimitServiceFetchT fresh: ProviderRateLimits, previous: ProviderRateLimits | null ): ProviderRateLimits { + if (!this.isClaudeOAuthUsageEnabled()) { + return this.claudeUsageWithoutOAuth() + } this.lastClaudeCheckCompletedAt = fresh.updatedAt // Failed OAuth must preserve newer live display while forwarding Retry-After to the poll gate. const current = this.state.claude @@ -94,15 +117,23 @@ export abstract class RateLimitServiceFetchPolicy extends RateLimitServiceFetchT ) { return } - this.lastClaudeAuthSnapshot = { + const snapshot = { // System Default uses the user config folder without injecting a managed one. configDir: normalizeClaudeConfigDir( authPreparation?.envPatch.CLAUDE_CONFIG_DIR ?? getClaudeProfileRouter()?.userConfigDir() ?? readUserClaudeConfigDir(process.env) ), + home: authPreparation?.configDir, provenance: authPreparation?.provenance ?? 'system' } + if ( + snapshot.configDir !== this.lastClaudeAuthSnapshot?.configDir || + snapshot.provenance !== this.lastClaudeAuthSnapshot?.provenance + ) { + this.liveClaudeUsage = null + } + this.lastClaudeAuthSnapshot = snapshot } ingestLiveClaudeRateLimits(event: ClaudeStatusLineRateLimits): void { @@ -155,6 +186,26 @@ export abstract class RateLimitServiceFetchPolicy extends RateLimitServiceFetchT ) { return } + const priorLive = this.liveClaudeUsage ? withoutResetWindows(this.liveClaudeUsage, now) : null + this.liveClaudeUsage = { + provider: 'claude', + session: freshSession ?? priorLive?.session ?? null, + weekly: freshWeekly ?? priorLive?.weekly ?? null, + updatedAt: now, + error: null, + status: 'ok', + usageMetadata: { + source: 'live-session', + lastSuccessfulSource: 'live-session', + authProvenance: snapshot.provenance + } + } + this.rememberUsageReadingAccount( + 'claude', + this.claudeFetchTarget, + snapshot.home, + snapshot.provenance + ) if ( previous?.status === 'ok' && previous.usageMetadata?.source === 'live-session' && diff --git a/src/main/rate-limits/service/service-full-cycle-preparation.ts b/src/main/rate-limits/service/service-full-cycle-preparation.ts index fc9c8186b05..b9ed5efd4e8 100644 --- a/src/main/rate-limits/service/service-full-cycle-preparation.ts +++ b/src/main/rate-limits/service/service-full-cycle-preparation.ts @@ -153,7 +153,7 @@ export abstract class RateLimitServiceFullCyclePreparation extends RateLimitServ this.updateState({ ...previousState, - claude: this.withFetchingStatus(previousState.claude, 'claude'), + claude: this.claudeStateAtCycleStart(previousState.claude), // A gated slot makes no attempt and cannot settle a fetching chip. codex: codexFetchGated ? codexStateBeforeFetch @@ -220,7 +220,9 @@ export abstract class RateLimitServiceFullCyclePreparation extends RateLimitServ // Live display does not waive automated Retry-After gates. const claudeFetchGated = - !options?.force && this.shouldSkipAutomatedFetch('claude', previousState.claude) + !this.isClaudeOAuthUsageEnabled() || + claudeGeneration !== this.claudeFetchGeneration || + (!options?.force && this.shouldSkipAutomatedFetch('claude', previousState.claude)) const [claudeResult, codexResult, geminiResult, opencodeGoResult, kimiResult, miniMaxResult] = await Promise.allSettled([ @@ -231,7 +233,7 @@ export abstract class RateLimitServiceFullCyclePreparation extends RateLimitServ allowPtyFallback: this.shouldAllowClaudePtyFallback(claudeAuthPreparation), allowUsagePanelSupplement: this.shouldAllowClaudeUsagePanelSupplement(), networkProxySettings: this.networkProxySettingsResolver?.(), - signal + signal: this.claudeUsageFetchSignal(signal) }), codexFetchGated ? Promise.resolve(previousState.codex as ProviderRateLimits) diff --git a/src/main/rate-limits/service/service-inactive-accounts.ts b/src/main/rate-limits/service/service-inactive-accounts.ts index ffcaba04f59..45c171dc0e6 100644 --- a/src/main/rate-limits/service/service-inactive-accounts.ts +++ b/src/main/rate-limits/service/service-inactive-accounts.ts @@ -6,6 +6,9 @@ import { delayUnlessAborted } from '../../../shared/abort-aware-delay' export abstract class RateLimitServiceInactiveAccounts extends RateLimitServicePolling { async fetchInactiveClaudeAccountsOnOpen(): Promise { + if (!this.isClaudeOAuthUsageEnabled()) { + return + } if (Date.now() - this.lastInactiveClaudeFetchAt < INACTIVE_FETCH_DEBOUNCE_MS) { return } @@ -21,7 +24,7 @@ export abstract class RateLimitServiceInactiveAccounts extends RateLimitServiceP } const fetchGeneration = this.inactiveClaudeAccountsGeneration const controller = this.beginFetchCycle() - const signal = controller.signal + const signal = this.claudeUsageFetchSignal(controller.signal) for (const account of accounts) { this.inactiveClaudeFetching.add(account.id) diff --git a/src/main/rate-limits/service/service-polling.ts b/src/main/rate-limits/service/service-polling.ts index 0a6e7421d3c..33da0aa57a8 100644 --- a/src/main/rate-limits/service/service-polling.ts +++ b/src/main/rate-limits/service/service-polling.ts @@ -87,6 +87,7 @@ export abstract class RateLimitServicePolling extends RateLimitServiceFetchQueue const museEnabled = this.museUsageEnabledResolver?.() ?? true return Object.entries(byProvider) .filter(([provider]) => provider !== 'muse' || museEnabled) + .filter(([provider]) => provider !== 'claude' || this.isClaudeOAuthUsageEnabled()) .map(([provider, limits]) => ({ provider: provider as ActiveRateLimitProvider, limits diff --git a/src/main/rate-limits/service/service-provider-cycles.ts b/src/main/rate-limits/service/service-provider-cycles.ts index 3218091ead4..42a8be46035 100644 --- a/src/main/rate-limits/service/service-provider-cycles.ts +++ b/src/main/rate-limits/service/service-provider-cycles.ts @@ -94,10 +94,15 @@ export abstract class RateLimitServiceProviderCycles extends RateLimitServiceFul // Why: capture before the resolver await so an account switch during it invalidates both the snapshot and the state apply. const claudeGeneration = this.claudeFetchGeneration const claudeAuthPreparation = await this.claudeAuthPreparationResolver?.(claudeTarget) - if (signal.aborted) { + if (signal.aborted || claudeGeneration !== this.claudeFetchGeneration) { return } this.rememberClaudeAuthSnapshot(claudeAuthPreparation, claudeGeneration, claudeTarget) + // After the snapshot: statusLine posts are dropped until one names the selected account. + if (!this.isClaudeOAuthUsageEnabled()) { + this.updateState({ ...this.state, claude: this.claudeUsageWithoutOAuth() }) + return + } const claudeProvenance = claudeAuthPreparation?.provenance ?? 'system' const previousState = this.state @@ -111,7 +116,7 @@ export abstract class RateLimitServiceProviderCycles extends RateLimitServiceFul allowPtyFallback: this.shouldAllowClaudePtyFallback(claudeAuthPreparation), allowUsagePanelSupplement: this.shouldAllowClaudeUsagePanelSupplement(), networkProxySettings: this.networkProxySettingsResolver?.(), - signal + signal: this.claudeUsageFetchSignal(signal) }).catch((err): ProviderRateLimits => ({ provider: 'claude', session: null, diff --git a/src/main/rate-limits/service/service-refresh-orchestration.test.ts b/src/main/rate-limits/service/service-refresh-orchestration.test.ts index 6f3ef84e67d..28afdea75ff 100644 --- a/src/main/rate-limits/service/service-refresh-orchestration.test.ts +++ b/src/main/rate-limits/service/service-refresh-orchestration.test.ts @@ -2,10 +2,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import type { ProviderRateLimits } from '../../../shared/rate-limit-types' import { RateLimitService } from './rate-limit-service' import { fetchActiveClaudeRateLimits } from '../providers/claude/claude-active-usage-fetch' +import { fetchInactiveClaudeAccountUsage } from '../providers/claude/claude-managed-account-usage' import { fetchCodexRateLimits } from '../providers/codex/codex-fetcher' import { deferred, flushMicrotasks, + mockFreshBackgroundProviderFetches, okProvider, resetRateLimitProviderMocks } from './rate-limit-service-test-harness' @@ -95,4 +97,213 @@ describe('RateLimitService', () => { await flushMicrotasks(60) expect(service.getState().claude?.session?.usedPercent).toBe(55) }) + + it('keeps live Claude usage through refreshes without reading OAuth usage when disabled', async () => { + mockFreshBackgroundProviderFetches() + const service = new RateLimitService() + service.setClaudeOAuthUsageEnabledResolver(() => false) + service.setInactiveClaudeAccountsResolver(() => [{ id: 'other' }]) + let home = '/claude/account-a' + service.setClaudeAuthPreparationResolver(async () => ({ + configDir: home, + envPatch: { CLAUDE_CONFIG_DIR: home }, + provenance: home + })) + service.start({ fetchImmediately: false }) + await flushMicrotasks(20) + service.stop() + expect(service.getState().claude).toMatchObject({ + session: null, + weekly: null, + status: 'unavailable' + }) + service.ingestLiveClaudeRateLimits({ + configDir: '/claude/account-a', + fiveHour: { used_percentage: 33 }, + sevenDay: { used_percentage: 62 } + }) + await service.refreshClaudeForTarget() + await service.refresh() + await service.fetchInactiveClaudeAccountsOnOpen() + + expect(service.getState().claude).toMatchObject({ + session: { usedPercent: 33 }, + weekly: { usedPercent: 62 }, + status: 'ok', + usageMetadata: { source: 'live-session' } + }) + expect(service.getState().inactiveClaudeAccounts).toEqual([]) + expect(service.usageReadingAccount('claude')).toEqual({ + home: '/claude/account-a', + provenance: '/claude/account-a' + }) + expect(service.requestUsageReading('claude')).toBe(false) + expect(fetchActiveClaudeRateLimits).not.toHaveBeenCalled() + expect(fetchInactiveClaudeAccountUsage).not.toHaveBeenCalled() + + home = '/claude/account-b' + await service.refreshForClaudeAccountChange('outgoing') + expect(service.getState().claude?.session).toBeNull() + expect(service.getState().claude?.status).toBe('unavailable') + service.ingestLiveClaudeRateLimits({ + configDir: '/claude/account-b', + fiveHour: { used_percentage: 12 }, + sevenDay: null + }) + expect(service.getState().claude?.session?.usedPercent).toBe(12) + expect(service.usageReadingAccount('claude')).toEqual({ + home: '/claude/account-b', + provenance: '/claude/account-b' + }) + }) + + it('cancels pending Claude usage and rejects its reply even after polling is enabled again', async () => { + mockFreshBackgroundProviderFetches() + const service = new RateLimitService() + let enabled = true + service.setClaudeOAuthUsageEnabledResolver(() => enabled) + const started = deferred() + const reply = deferred() + vi.mocked(fetchActiveClaudeRateLimits).mockImplementationOnce((options) => { + started.resolve(options?.signal) + return reply.promise + }) + const refresh = service.refresh() + const signal = await started.promise + + enabled = false + service.discardClaudeOAuthUsage() + expect(signal?.aborted).toBe(true) + service.ingestLiveClaudeRateLimits({ + configDir: null, + fiveHour: { used_percentage: 41 }, + sevenDay: null + }) + enabled = true + reply.resolve(okProvider('claude', 90)) + await refresh + + expect(service.getState().claude).toMatchObject({ + session: { usedPercent: 41 }, + status: 'ok', + usageMetadata: { source: 'live-session' } + }) + }) + + it('retains only terminal-observed windows when disabling OAuth-enriched usage', async () => { + mockFreshBackgroundProviderFetches() + const service = new RateLimitService() + let enabled = true + service.setClaudeOAuthUsageEnabledResolver(() => enabled) + vi.mocked(fetchActiveClaudeRateLimits).mockResolvedValueOnce({ + ...okProvider('claude', 20), + weekly: { usedPercent: 50, windowMinutes: 10080, resetsAt: null, resetDescription: null }, + fableWeekly: { + usedPercent: 70, + windowMinutes: 10080, + resetsAt: null, + resetDescription: null + }, + extraUsage: { + unit: 'credits', + enabled: true, + balance: 100, + unlimited: false, + disabledReason: null, + resetsAt: null + } + }) + await service.refresh() + service.ingestLiveClaudeRateLimits({ + configDir: null, + fiveHour: { used_percentage: 35 }, + sevenDay: null + }) + expect(service.getState().claude?.weekly?.usedPercent).toBe(50) + + enabled = false + service.discardClaudeOAuthUsage() + await service.refresh() + + expect(service.getState().claude).toMatchObject({ + session: { usedPercent: 35 }, + weekly: null, + usageMetadata: { source: 'live-session' } + }) + expect(service.getState().claude?.fableWeekly).toBeFalsy() + expect(service.getState().claude?.extraUsage).toBeFalsy() + await service.refreshForClaudeAccountChange('outgoing') + expect(service.getState().claude?.session).toBeNull() + }) + + it('discards pending inactive-account usage when polling is disabled', async () => { + const service = new RateLimitService() + let enabled = true + service.setClaudeOAuthUsageEnabledResolver(() => enabled) + service.setInactiveClaudeAccountsResolver(() => [{ id: 'other' }, { id: 'another' }]) + const started = deferred() + const reply = deferred() + vi.mocked(fetchInactiveClaudeAccountUsage).mockImplementationOnce((_account, options) => { + started.resolve(options?.signal) + return reply.promise + }) + const refresh = service.fetchInactiveClaudeAccountsOnOpen() + const signal = await started.promise + + enabled = false + service.discardClaudeOAuthUsage() + expect(signal?.aborted).toBe(true) + enabled = true + reply.resolve(okProvider('claude', 90)) + await refresh + + expect(service.getState().inactiveClaudeAccounts).toEqual([]) + expect(service.getState().claude).toMatchObject({ + session: null, + weekly: null, + status: 'unavailable' + }) + }) + + it.each(['all', 'claude'] as const)( + 'does not revive a canceled %s request still preparing account attribution', + async (cycle) => { + mockFreshBackgroundProviderFetches() + const service = new RateLimitService() + let enabled = true + service.setClaudeOAuthUsageEnabledResolver(() => enabled) + const oldAuth = { configDir: '/claude/old', envPatch: {}, provenance: 'old' } + const preparation = deferred() + service.setClaudeAuthPreparationResolver( + vi + .fn() + .mockReturnValueOnce(preparation.promise) + .mockResolvedValue({ + configDir: '/claude/current', + envPatch: { CLAUDE_CONFIG_DIR: '/claude/current' }, + provenance: 'current' + }) + ) + const refresh = cycle === 'all' ? service.refresh() : service.refreshClaudeForTarget() + enabled = false + service.discardClaudeOAuthUsage() + enabled = true + preparation.resolve(oldAuth) + await refresh + + expect(fetchActiveClaudeRateLimits).not.toHaveBeenCalledWith( + expect.objectContaining({ authPreparation: oldAuth }) + ) + service.ingestLiveClaudeRateLimits({ + configDir: '/claude/current', + fiveHour: { used_percentage: 15 }, + sevenDay: null + }) + expect(service.getState().claude?.session?.usedPercent).toBe(15) + expect(service.usageReadingAccount('claude')).toEqual({ + home: '/claude/current', + provenance: 'current' + }) + } + ) }) diff --git a/src/main/rate-limits/service/service-state.ts b/src/main/rate-limits/service/service-state.ts index 3b0b3c0d35a..f847392189c 100644 --- a/src/main/rate-limits/service/service-state.ts +++ b/src/main/rate-limits/service/service-state.ts @@ -17,6 +17,7 @@ import { type OpenCodeGoRateLimitConfig, type MiniMaxRateLimitConfig, type AntigravityUsageEnabledResolver, + type ClaudeOAuthUsageEnabledResolver, type MuseUsageEnabledResolver, type ZcodePlanRateLimitConfig, type GeminiCliOAuthEnabledResolver, @@ -90,10 +91,16 @@ export abstract class RateLimitServiceState { protected fetchIdleResolvers: (() => void)[] = [] protected codexFetchGeneration = 0 protected claudeFetchGeneration = 0 + protected claudeUsageAbortController = new AbortController() + protected liveClaudeUsage: ProviderRateLimits | null = null // Why: cached display receipts cannot establish when the selected account was last checked. protected lastClaudeCheckCompletedAt: number | null = null // Capture account attribution once per cycle instead of rerunning auth preparation on every post. - protected lastClaudeAuthSnapshot: { configDir: string | null; provenance: string } | null = null + protected lastClaudeAuthSnapshot: { + configDir: string | null + home: string | undefined + provenance: string + } | null = null /** Where the applied host-runtime Claude/Codex readings were taken. */ protected usageReadingAccounts: Partial< Record<'claude' | 'codex', { home: string; provenance: string }> @@ -122,6 +129,7 @@ export abstract class RateLimitServiceState { protected miniMaxConfigResolver: (() => MiniMaxRateLimitConfig) | null = null protected zcodePlanConfigResolver: (() => ZcodePlanRateLimitConfig) | null = null protected geminiCliOAuthEnabledResolver: GeminiCliOAuthEnabledResolver | null = null + protected claudeOAuthUsageEnabledResolver: ClaudeOAuthUsageEnabledResolver | null = null protected antigravityUsageEnabledResolver: AntigravityUsageEnabledResolver | null = null protected antigravityFetchTarget: LocalAccountRuntimeTarget = { runtime: 'host', wslDistro: null } protected antigravityAccountUsageRecorder: AntigravityAccountUsageRecorder | null = null diff --git a/src/main/rate-limits/service/service-types.ts b/src/main/rate-limits/service/service-types.ts index c2c9b9e8c2b..bb67e283a65 100644 --- a/src/main/rate-limits/service/service-types.ts +++ b/src/main/rate-limits/service/service-types.ts @@ -62,6 +62,7 @@ export type ZcodePlanResolvedConfig = { export type GeminiCliOAuthEnabledResolver = () => boolean export type AntigravityUsageEnabledResolver = () => boolean +export type ClaudeOAuthUsageEnabledResolver = () => boolean export type MuseUsageEnabledResolver = () => boolean export type ActiveRateLimitProvider = ProviderRateLimits['provider'] export type ActiveProviderState = { diff --git a/src/renderer/src/components/settings/accounts/claude/accounts-pane-claude-section.tsx b/src/renderer/src/components/settings/accounts/claude/accounts-pane-claude-section.tsx index bc05589e570..9ad23092a2b 100644 --- a/src/renderer/src/components/settings/accounts/claude/accounts-pane-claude-section.tsx +++ b/src/renderer/src/components/settings/accounts/claude/accounts-pane-claude-section.tsx @@ -8,6 +8,7 @@ import { ButtonGroup } from '../../../ui/button-group' import { Label } from '../../../ui/label' import { ClaudeIcon } from '../../../status-bar/icons' import { SearchableSetting } from '../../section-layout/SearchableSetting' +import { ClaudeOAuthUsageSetting } from './claude-oauth-usage-setting' import { ClaudeRunningSessionSwitchSetting } from './claude-running-session-switch-setting' import { getProviderAccountRuntime, @@ -45,6 +46,7 @@ export function renderClaudeAccountsSection( claudeAccounts, claudeAction, isRemoteAccountScope, + recordFeatureInteraction, remoteAccountScopeNotice, runClaudeAccountAction, setRemoveClaudeTarget, @@ -383,6 +385,13 @@ export function renderClaudeAccountsSection( )}
+ {isRemoteAccountScope ? null : ( + + )} {isRemoteAccountScope ? null : ( )} diff --git a/src/renderer/src/components/settings/accounts/claude/claude-oauth-usage-setting.fixture.tsx b/src/renderer/src/components/settings/accounts/claude/claude-oauth-usage-setting.fixture.tsx new file mode 100644 index 00000000000..f01471848ca --- /dev/null +++ b/src/renderer/src/components/settings/accounts/claude/claude-oauth-usage-setting.fixture.tsx @@ -0,0 +1,42 @@ +import { defineFixture } from '../../../../component-fixtures/component-fixture' +import { getDefaultSettings } from '../../../../../../shared/constants' +import { useAppStore } from '../../../../store' +import { SettingsGroup } from '../../section-layout/SettingsGroup' +import { ClaudeOAuthUsageSetting } from './claude-oauth-usage-setting' + +function UsageSettingFixture(): React.JSX.Element | null { + const settings = useAppStore((state) => state.settings) + const updateSettings = useAppStore((state) => state.updateSettings) + if (!settings) { + return null + } + return ( +
+ + {}} + /> + +
+ ) +} + +export const on = defineFixture({ width: 720, render: () => }) + +export const off = defineFixture({ + ...on, + store: { settings: { ...getDefaultSettings('/fixture'), claudeOAuthUsageEnabled: false } } +}) + +export const narrow = defineFixture({ ...off, width: 360 }) + +export const infoTooltip = defineFixture({ + ...on, + interact: (frame) => { + frame + .querySelector('button[aria-label="About Claude usage polling"]') + ?.focus() + } +}) diff --git a/src/renderer/src/components/settings/accounts/claude/claude-oauth-usage-setting.tsx b/src/renderer/src/components/settings/accounts/claude/claude-oauth-usage-setting.tsx new file mode 100644 index 00000000000..5a8dc027100 --- /dev/null +++ b/src/renderer/src/components/settings/accounts/claude/claude-oauth-usage-setting.tsx @@ -0,0 +1,67 @@ +import { useTranslate } from '@/i18n/use-translate' +import { Info } from 'lucide-react' +import { Button } from '../../../ui/button' +import { Label } from '../../../ui/label' +import { Switch } from '../../../ui/switch' +import { Tooltip, TooltipContent, TooltipTrigger } from '../../../ui/tooltip' +import { SearchableSetting } from '../../section-layout/SearchableSetting' +import type { AccountsPaneSectionModel } from '../accounts-pane-types' + +type ClaudeOAuthUsageSettingProps = Pick< + AccountsPaneSectionModel, + 'settings' | 'updateSettings' | 'recordFeatureInteraction' +> + +export function ClaudeOAuthUsageSetting({ + settings, + updateSettings, + recordFeatureInteraction +}: ClaudeOAuthUsageSettingProps): React.JSX.Element { + const t = useTranslate() + const title = t('accounts.claude.oauthUsage.title', 'Claude usage polling') + const description = t( + 'accounts.claude.oauthUsage.description', + 'Uses your Claude Code sign-in to check usage, credits, and inactive accounts. Turn off to use live terminal readings only.' + ) + return ( + +
+ + + + + + + {description} + + +
+ + {description} + + { + recordFeatureInteraction('usage-tracking') + updateSettings({ claudeOAuthUsageEnabled: checked }) + }} + /> +
+ ) +} diff --git a/src/renderer/src/i18n/locales/de.json b/src/renderer/src/i18n/locales/de.json index 6901410c75b..347472857df 100644 --- a/src/renderer/src/i18n/locales/de.json +++ b/src/renderer/src/i18n/locales/de.json @@ -21958,6 +21958,11 @@ "reasonSharedSession": "Auch in einem anderen Bereich geöffnet", "sharedOne": "1 Claude-Sitzung bleibt auf {{previous}}, weil dieselbe Unterhaltung in einem anderen Bereich geöffnet ist. Ein Neustart könnte die Unterhaltung des anderen Bereichs erneut öffnen.", "sharedOther": "{{count}} Claude-Sitzungen bleiben auf {{previous}}, weil dieselbe Unterhaltung in einem anderen Bereich geöffnet ist. Ein Neustart könnte die Unterhaltung des anderen Bereichs erneut öffnen." + }, + "oauthUsage": { + "title": "Claude-Nutzungsabfrage", + "description": "Verwendet deine Claude-Code-Anmeldung, um Nutzung, Guthaben und inaktive Konten abzufragen. Deaktiviere diese Option, um nur Live-Daten aus Terminalsitzungen zu verwenden.", + "about": "Über die Claude-Nutzungsabfrage" } }, "managedData": { diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 0b43fa80a27..9795bf9aae0 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -23494,6 +23494,11 @@ "body_one": "{{count}} Claude session is still running on {{previous}}. Restarting continues the conversation on {{next}}. Busy sessions wait for idle before restarting.", "body_other": "{{count}} Claude sessions are still running on {{previous}}. Restarting continues each conversation on {{next}}. Busy sessions wait for idle before restarting.", "restartCount_one": "Restart {{count}} session" + }, + "oauthUsage": { + "title": "Claude usage polling", + "description": "Uses your Claude Code sign-in to check usage, credits, and inactive accounts. Turn off to use live terminal readings only.", + "about": "About Claude usage polling" } }, "managedData": { diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json index b5e69c8f6db..f92aa988b2a 100644 --- a/src/renderer/src/i18n/locales/es.json +++ b/src/renderer/src/i18n/locales/es.json @@ -21566,6 +21566,11 @@ "reasonSharedSession": "También abierta en otro panel", "sharedOne": "1 sesión de Claude permanece en {{previous}} porque otro panel tiene abierta la misma conversación. Reiniciarla podría volver a abrir la conversación del otro panel.", "sharedOther": "{{count}} sesiones de Claude permanecen en {{previous}} porque otro panel tiene abierta la misma conversación. Reiniciarlas podría volver a abrir la conversación del otro panel." + }, + "oauthUsage": { + "title": "Consulta de uso de Claude", + "description": "Usa tu inicio de sesión de Claude Code para consultar el uso, los créditos y las cuentas inactivas. Desactívala para usar solo las lecturas en directo de las sesiones de terminal.", + "about": "Acerca de la consulta de uso de Claude" } }, "hostSignIn": { diff --git a/src/renderer/src/i18n/locales/fr.json b/src/renderer/src/i18n/locales/fr.json index adbc27e17f3..57e30149881 100644 --- a/src/renderer/src/i18n/locales/fr.json +++ b/src/renderer/src/i18n/locales/fr.json @@ -22234,6 +22234,11 @@ "reasonSharedSession": "Également ouverte dans un autre volet", "sharedOne": "1 session Claude reste sur {{previous}} car la même conversation est ouverte dans un autre volet. Un redémarrage pourrait rouvrir la conversation de l’autre volet.", "sharedOther": "{{count}} sessions Claude restent sur {{previous}} car la même conversation est ouverte dans un autre volet. Un redémarrage pourrait rouvrir la conversation de l’autre volet." + }, + "oauthUsage": { + "title": "Suivi de l’utilisation de Claude", + "description": "Utilise votre connexion Claude Code pour consulter l’utilisation, les crédits et les comptes inactifs. Désactivez cette option pour utiliser uniquement les données en direct des sessions de terminal.", + "about": "À propos du suivi de l’utilisation de Claude" } }, "hostSignIn": { diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json index c3f93d47ebf..5913dbb8282 100644 --- a/src/renderer/src/i18n/locales/ja.json +++ b/src/renderer/src/i18n/locales/ja.json @@ -21561,6 +21561,11 @@ "reasonSharedSession": "別のペインでも開いています", "sharedOne": "別のペインで同じ会話が開いているため、1 件の Claude セッションは {{previous}} のままです。再起動すると別のペインの会話が再び開く可能性があります。", "sharedOther": "別のペインで同じ会話が開いているため、{{count}} 件の Claude セッションは {{previous}} のままです。再起動すると別のペインの会話が再び開く可能性があります。" + }, + "oauthUsage": { + "title": "Claude の使用量の定期取得", + "description": "Claude Code のログイン情報を使って、使用量、クレジット、非アクティブなアカウントを確認します。オフにすると、実行中のターミナルセッションからの使用量データのみを表示します。", + "about": "Claude の使用量の定期取得について" } }, "hostSignIn": { diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json index 2e312739034..268bfacdbe9 100644 --- a/src/renderer/src/i18n/locales/ko.json +++ b/src/renderer/src/i18n/locales/ko.json @@ -21553,6 +21553,11 @@ "reasonSharedSession": "다른 패널에서도 열려 있음", "sharedOne": "다른 패널에서 같은 대화를 열고 있어 Claude 세션 1개가 {{previous}}에 유지됩니다. 다시 시작하면 다른 패널의 대화가 다시 열릴 수 있습니다.", "sharedOther": "다른 패널에서 같은 대화를 열고 있어 Claude 세션 {{count}}개가 {{previous}}에 유지됩니다. 다시 시작하면 다른 패널의 대화가 다시 열릴 수 있습니다." + }, + "oauthUsage": { + "title": "Claude 사용량 주기적 조회", + "description": "Claude Code 로그인 정보로 사용량, 크레딧 및 비활성 계정을 확인합니다. 끄면 실행 중인 터미널 세션에서 제공하는 실시간 사용량만 표시합니다.", + "about": "Claude 사용량 주기적 조회 정보" } }, "hostSignIn": { diff --git a/src/renderer/src/i18n/locales/pt.json b/src/renderer/src/i18n/locales/pt.json index 3e62a8c2f91..fbb12f430ee 100644 --- a/src/renderer/src/i18n/locales/pt.json +++ b/src/renderer/src/i18n/locales/pt.json @@ -22931,6 +22931,11 @@ "reasonSharedSession": "Também aberta em outro painel", "sharedOne": "1 sessão do Claude permanece em {{previous}} porque outro painel tem a mesma conversa aberta. Reiniciar pode reabrir a conversa do outro painel.", "sharedOther": "{{count}} sessões do Claude permanecem em {{previous}} porque outro painel tem a mesma conversa aberta. Reiniciar pode reabrir a conversa do outro painel." + }, + "oauthUsage": { + "title": "Consulta de uso do Claude", + "description": "Usa seu login do Claude Code para consultar o uso, os créditos e as contas inativas. Desative para usar apenas as leituras em tempo real das sessões do terminal.", + "about": "Sobre a consulta de uso do Claude" } }, "managedData": { diff --git a/src/renderer/src/i18n/locales/ru.json b/src/renderer/src/i18n/locales/ru.json index 1af528a9f4c..d7f6518197e 100644 --- a/src/renderer/src/i18n/locales/ru.json +++ b/src/renderer/src/i18n/locales/ru.json @@ -22661,7 +22661,12 @@ "sharedOne": "1 сеанс Claude остаётся на {{previous}}, поскольку тот же разговор открыт в другой панели. При перезапуске может снова открыться разговор из другой панели.", "sharedOther": "Сеансы Claude ({{count}}) остаются на {{previous}}, поскольку тот же разговор открыт в другой панели. При перезапуске может снова открыться разговор из другой панели." }, - "unnamedAccount": "Учётная запись Claude" + "unnamedAccount": "Учётная запись Claude", + "oauthUsage": { + "title": "Опрос использования Claude", + "description": "Использует ваш вход в Claude Code для проверки использования, кредитов и неактивных аккаунтов. Отключите, чтобы получать данные только из активных сеансов терминала.", + "about": "Об опросе использования Claude" + } }, "managedData": { "description": "Добавляйте учётные записи этой командой в терминале сервера Orca. Выбор применяется к новым явным запускам агента на этом сервере; прямой SSH-ретранслятор и запуски WSL из Windows используют собственные учётные данные.", diff --git a/src/renderer/src/i18n/locales/tr.json b/src/renderer/src/i18n/locales/tr.json index 9321f2c34d4..eccd93ad3a8 100644 --- a/src/renderer/src/i18n/locales/tr.json +++ b/src/renderer/src/i18n/locales/tr.json @@ -21928,6 +21928,11 @@ "reasonSharedSession": "Başka bir bölmede de açık", "sharedOne": "Aynı konuşma başka bir bölmede açık olduğu için 1 Claude oturumu {{previous}} üzerinde kalır. Yeniden başlatmak diğer bölmedeki konuşmayı yeniden açabilir.", "sharedOther": "Aynı konuşma başka bir bölmede açık olduğu için {{count}} Claude oturumu {{previous}} üzerinde kalır. Yeniden başlatmak diğer bölmedeki konuşmayı yeniden açabilir." + }, + "oauthUsage": { + "title": "Claude kullanımını sorgula", + "description": "Kullanımı, kredileri ve etkin olmayan hesapları kontrol etmek için Claude Code oturumunuzu kullanır. Yalnızca çalışan terminal oturumlarından gelen canlı verileri kullanmak için kapatın.", + "about": "Claude kullanım sorgulaması hakkında" } }, "managedData": { diff --git a/src/renderer/src/i18n/locales/zh-Hant.json b/src/renderer/src/i18n/locales/zh-Hant.json index 0d592cf40c5..90b60ca6c21 100644 --- a/src/renderer/src/i18n/locales/zh-Hant.json +++ b/src/renderer/src/i18n/locales/zh-Hant.json @@ -21631,6 +21631,11 @@ "reasonSharedSession": "在另一個窗格中也已開啟", "sharedOne": "由於另一個窗格開啟了相同對話,1 個 Claude 工作階段會保留在 {{previous}}。重新啟動可能會重新開啟另一個窗格的對話。", "sharedOther": "由於另一個窗格開啟了相同對話,{{count}} 個 Claude 工作階段會保留在 {{previous}}。重新啟動可能會重新開啟另一個窗格的對話。" + }, + "oauthUsage": { + "title": "Claude 用量輪詢", + "description": "使用 Claude Code 登入憑證查詢用量、額度和非使用中帳戶。關閉後,僅顯示執行中終端機工作階段提供的即時用量。", + "about": "關於 Claude 用量輪詢" } }, "managedData": { diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json index b68f1c671ae..c62d0c81d31 100644 --- a/src/renderer/src/i18n/locales/zh.json +++ b/src/renderer/src/i18n/locales/zh.json @@ -21901,6 +21901,11 @@ "reasonSharedSession": "在另一个窗格中也已打开", "sharedOne": "由于另一个窗格打开了同一对话,1 个 Claude 会话将保持在 {{previous}}。重新启动可能会重新打开另一个窗格的对话。", "sharedOther": "由于另一个窗格打开了同一对话,{{count}} 个 Claude 会话将保持在 {{previous}}。重新启动可能会重新打开另一个窗格的对话。" + }, + "oauthUsage": { + "title": "Claude 用量轮询", + "description": "使用 Claude Code 登录凭据检查用量、额度和非活跃账号。关闭后,仅显示运行中终端会话提供的实时用量。", + "about": "关于 Claude 用量轮询" } }, "hostSignIn": { diff --git a/src/shared/default-global-settings.ts b/src/shared/default-global-settings.ts index fe3326a7219..7403d95fa1c 100644 --- a/src/shared/default-global-settings.ts +++ b/src/shared/default-global-settings.ts @@ -249,6 +249,7 @@ export function buildDefaultSettings(args: { minimaxEndpoint: 'overseas', zcodePlanSite: 'zai', geminiCliOAuthEnabled: false, + claudeOAuthUsageEnabled: true, agentCmdOverrides: {}, // Arguments hold only the user's own text; the permission flag comes from the choice below. agentDefaultArgs: {}, diff --git a/src/shared/global-settings-types.ts b/src/shared/global-settings-types.ts index 0586388fcf9..85f4a02b995 100644 --- a/src/shared/global-settings-types.ts +++ b/src/shared/global-settings-types.ts @@ -375,6 +375,8 @@ export type GlobalSettings = GlobalSettingsBase & { zcodePlanSite?: ZcodePlanSite /** Extract OAuth credentials from the local Gemini CLI for rate-limit fetching. Off by default (explicit opt-in). */ geminiCliOAuthEnabled: boolean + /** Poll Claude usage with existing CLI credentials. Off uses live terminal readings only. */ + claudeOAuthUsageEnabled: boolean /** Per-agent CLI command overrides. A missing key means use the catalog default binary name. */ agentCmdOverrides: Partial> /** Off keeps each Codex home's history to itself: Orca links no sessions between accounts or From 3342284233b0dffb041c3b1b97799fa13352c789 Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 09:33:08 +0000 Subject: [PATCH 059/178] fix(native-chat): an older browser run's Show button never opens another run's page (#41939) A run without --page fell back to the chat's latest agent page whenever it had no remembered page, which is every older run after a restart or once 500 runs push it out. The chat's browser activity now belongs only to its newest browser run, which remembers that page for when a newer run takes over. An older run that this renderer never saw as the newest shows no button. --- .../browser-agent-activity-state.ts | 31 ++--- .../runs/NativeChatBrowserPageButton.test.tsx | 128 +++++++++++++----- .../runs/NativeChatBrowserPageButton.tsx | 126 +++++++++++------ 3 files changed, 186 insertions(+), 99 deletions(-) diff --git a/src/renderer/src/components/browser-pane/agent-cursor/browser-agent-activity-state.ts b/src/renderer/src/components/browser-pane/agent-cursor/browser-agent-activity-state.ts index 98ce6117002..47046208acd 100644 --- a/src/renderer/src/components/browser-pane/agent-cursor/browser-agent-activity-state.ts +++ b/src/renderer/src/components/browser-pane/agent-cursor/browser-agent-activity-state.ts @@ -8,19 +8,15 @@ type BrowserAgentCursorSink = { setActive: (active: boolean) => void } -/** The page an agent in a worktree last drove; `revision` orders it against other activity. */ -type BrowserAgentActivityMark = { pageId: string; revision: number } - const activePageIds = new Set() /** The SSH host driving a page through the desktop browser bridge, by its label. */ const controllerHostByPage = new Map() /** Activity no chat claimed: older CLIs, remote hosts and terminal agents. */ -const lastUnclaimedActivityByWorktree = new Map() -const lastActivityByChatSession = new Map() +const lastUnclaimedPageIdByWorktree = new Map() +const lastPageIdByChatSession = new Map() const lastCursorByPage = new Map() const sinksByPage = new Map() const listeners = new Set<() => void>() -let revision = 0 function notify(): void { for (const listener of listeners) { @@ -43,18 +39,16 @@ export function setBrowserAgentPageActive( agentSessionId?: string ): void { if (active) { - revision += 1 activePageIds.add(browserPageId) if (controllerHostLabel) { controllerHostByPage.set(browserPageId, controllerHostLabel) } else { controllerHostByPage.delete(browserPageId) } - const mark = { pageId: browserPageId, revision } if (agentSessionId) { - lastActivityByChatSession.set(agentSessionId, mark) + lastPageIdByChatSession.set(agentSessionId, browserPageId) } else if (worktreeId) { - lastUnclaimedActivityByWorktree.set(worktreeId, mark) + lastUnclaimedPageIdByWorktree.set(worktreeId, browserPageId) } } else { activePageIds.delete(browserPageId) @@ -90,11 +84,6 @@ export function attachBrowserAgentCursorSink( } } -/** Activity that arrives later than this revision happened after the caller looked. */ -export function getBrowserAgentActivityRevision(): number { - return revision -} - export function useBrowserAgentDrivesAnyPage( browserPageIds: readonly (string | null | undefined)[] ): boolean { @@ -118,15 +107,15 @@ export function useBrowserAgentControllerHost( return useSyncExternalStore(subscribe, read, read) } -/** What this chat's agent drove most recently; until it has claimed any, what an unclaimed agent in its worktree did. */ -export function useLastBrowserAgentActivity( +/** The page this chat's agent drove most recently; until it has claimed any, what an unclaimed agent in its worktree drove. */ +export function useLastBrowserAgentPageId( worktreeId: string | null, chatSessionId: string | null -): BrowserAgentActivityMark | null { +): string | null { // A chat that claimed activity names itself on this host, so unclaimed activity is another agent's. - const read = (): BrowserAgentActivityMark | null => - (chatSessionId ? lastActivityByChatSession.get(chatSessionId) : undefined) ?? - (worktreeId ? lastUnclaimedActivityByWorktree.get(worktreeId) : undefined) ?? + const read = (): string | null => + (chatSessionId ? lastPageIdByChatSession.get(chatSessionId) : undefined) ?? + (worktreeId ? lastUnclaimedPageIdByWorktree.get(worktreeId) : undefined) ?? null return useSyncExternalStore(subscribe, read, read) } diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.test.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.test.tsx index 1ef00b0357f..2e91f23dc77 100644 --- a/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.test.tsx +++ b/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.test.tsx @@ -1,9 +1,10 @@ // @vitest-environment happy-dom -import { act, render, screen, within } from '@testing-library/react' +import { act, render, within } from '@testing-library/react' import type { ReactNode } from 'react' import { describe, expect, it, vi } from 'vitest' import type { BrowserPage } from '../../../../../../shared/browser-workspace-types' +import type { NativeChatToolCallBlock } from '../../../../../../shared/native-chat/native-chat-types' import { setBrowserAgentPageActive } from '../../../browser-pane/agent-cursor/browser-agent-activity-state' import { NativeChatVisualOwnerContext, @@ -34,6 +35,37 @@ vi.mock('@/store', () => ({ useAppStore: (select: (current: typeof state) => unknown) => select(state) })) +const transcript = vi.hoisted(() => { + const listeners = new Set<() => void>() + let snapshot: { current: boolean; state: { items: unknown[] } } = { + current: true, + state: { items: [] } + } + return { + owner: { + getSnapshot: () => snapshot, + subscribe: (listener: () => void) => { + listeners.add(listener) + return () => listeners.delete(listener) + } + }, + setCalls(calls: readonly NativeChatToolCallBlock[]) { + snapshot = { + current: true, + state: { items: calls.map((call) => ({ body: { ...call, kind: 'tool-call' } })) } + } + listeners.forEach((listener) => listener()) + }, + setCurrent(current: boolean) { + snapshot = { ...snapshot, current } + listeners.forEach((listener) => listener()) + } + } +}) +vi.mock('../../session/structured/read/structured-agent-session-read-owner-registry', () => ({ + findStructuredAgentSessionReadOwner: () => transcript.owner +})) + const OWNER: NativeChatVisualOwner = { target: { kind: 'local' }, sessionId: 'session-1', @@ -48,52 +80,74 @@ function wrapper({ children }: { children: ReactNode }): React.JSX.Element { ) } +function browserCall(callId: string, command = 'orca browser click @e1'): NativeChatToolCallBlock { + return { type: 'tool-call', name: 'Bash', input: { command }, callId } +} + +function shownTitle(container: HTMLElement): string | null { + return within(container).queryByRole('button')?.getAttribute('title') ?? null +} + describe('NativeChatBrowserRunPageButton', () => { - it('keeps the page a run drove after the agent moves to a newer page', () => { + it("keeps an older run's page after a newer run drives another page", () => { state.browserPagesByWorkspace = { 'ws-1': [page('p1', 'First'), page('p2', 'Second')] } - render( - , - { wrapper } - ) + const older = browserCall('run-a') + act(() => transcript.setCalls([older])) + const olderRun = render(, { wrapper }) + act(() => setBrowserAgentPageActive('p1', true, 'wt-1', undefined, 'session-1')) + expect(shownTitle(olderRun.container)).toBe('First') - act(() => setBrowserAgentPageActive('p1', true, 'wt-1')) - expect(screen.getByRole('button').getAttribute('title')).toBe('First') - - act(() => setBrowserAgentPageActive('p2', true, 'wt-1')) - expect(screen.getByRole('button').getAttribute('title')).toBe('First') + const newer = browserCall('run-b') + act(() => transcript.setCalls([older, newer])) + const newerRun = render(, { wrapper }) + act(() => setBrowserAgentPageActive('p2', true, 'wt-1', undefined, 'session-1')) + expect(shownTitle(olderRun.container)).toBe('First') + expect(shownTitle(newerRun.container)).toBe('Second') }) - it("ignores other agents driving a different page before this run's command lands", () => { + it('ignores other agents driving a different page', () => { state.browserPagesByWorkspace = { 'ws-1': [page('p1', 'Mine'), page('p2', 'Theirs')] } - // An earlier run in this chat, so it is known to claim its activity. + const call = browserCall('run-c') + act(() => transcript.setCalls([call])) setBrowserAgentPageActive('p1', true, 'wt-1', undefined, 'session-1') - const { container } = render( - , - { wrapper } - ) + const { container } = render(, { wrapper }) act(() => setBrowserAgentPageActive('p2', true, 'wt-1')) act(() => setBrowserAgentPageActive('p2', true, 'wt-1', undefined, 'session-2')) + expect(shownTitle(container)).toBe('Mine') + }) + + it('shows the page for the newest run even when its activity arrived before the run drew', () => { + state.browserPagesByWorkspace = { 'ws-1': [page('p1', 'Driven')] } + const call = browserCall('run-d') + setBrowserAgentPageActive('p1', true, 'wt-1', undefined, 'session-1') + act(() => transcript.setCalls([call])) + const { container } = render(, { wrapper }) + expect(shownTitle(container)).toBe('Driven') + }) + + it("never shows another run's page for an older run whose page this renderer never saw", () => { + state.browserPagesByWorkspace = { 'ws-1': [page('p1', 'Older'), page('p2', 'Newer')] } + const older = browserCall('run-restored') + const newer = browserCall('run-newest') + act(() => transcript.setCalls([older, newer])) + setBrowserAgentPageActive('p1', true, 'wt-1', undefined, 'session-1') + const { container } = render(, { wrapper }) + expect(shownTitle(container)).toBeNull() + + act(() => setBrowserAgentPageActive('p2', true, 'wt-1', undefined, 'session-1')) + expect(shownTitle(container)).toBeNull() + }) + + it('keeps a run off a newer page while the transcript is behind the agent', () => { + state.browserPagesByWorkspace = { 'ws-1': [page('p1', 'Mine'), page('p2', 'Unseen run')] } + const call = browserCall('run-e') + act(() => transcript.setCalls([call])) + const { container } = render(, { wrapper }) act(() => setBrowserAgentPageActive('p1', true, 'wt-1', undefined, 'session-1')) - act(() => setBrowserAgentPageActive('p2', true, 'wt-1')) - expect(within(container).getByRole('button').getAttribute('title')).toBe('Mine') + + act(() => transcript.setCurrent(false)) + act(() => setBrowserAgentPageActive('p2', true, 'wt-1', undefined, 'session-1')) + expect(shownTitle(container)).toBe('Mine') }) }) diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.tsx index d11c156f520..1c53ea2a44b 100644 --- a/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.tsx +++ b/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.tsx @@ -1,36 +1,82 @@ -import { useEffect, useState } from 'react' +import { useEffect, useState, useSyncExternalStore } from 'react' import { BrowserFavicon } from '@/components/browser-favicon' import { useTranslate } from '@/i18n/use-translate' import { useAppStore } from '@/store' import { findPage } from '@/store/slices/browser/pages/browser-page-records' import { findBrowserWorkspaceForRemotePage } from '@/runtime/web-session-tabs-sync/mirrored-browser-tabs' import { activateBrowserPagePaletteResult } from '@/lib/browser/palette/browser-page-palette-activation' +import { useLastBrowserAgentPageId } from '../../../browser-pane/agent-cursor/browser-agent-activity-state' import { - getBrowserAgentActivityRevision, - useLastBrowserAgentActivity -} from '../../../browser-pane/agent-cursor/browser-agent-activity-state' -import { useNativeChatVisualOwner } from '../../media/visuals/native-chat-visual-owner' -import { nativeChatToolRunCategory } from '../../../../../../shared/native-chat/tools/native-chat-tool-icon' + useNativeChatVisualOwner, + type NativeChatVisualOwner +} from '../../media/visuals/native-chat-visual-owner' +import { findStructuredAgentSessionReadOwner } from '../../session/structured/read/structured-agent-session-read-owner-registry' +import { + nativeChatToolCategory, + nativeChatToolRunCategory +} from '../../../../../../shared/native-chat/tools/native-chat-tool-icon' import { nativeChatFullCommand } from '../../../../../../shared/native-chat/tools/native-chat-tool-input-text' import { parseOrcaBrowserCommand } from '../../../../../../shared/orca-browser-cli-commands' import type { NativeChatToolCallBlock } from '../../../../../../shared/native-chat/native-chat-types' +import type { AgentJournalRenderItem } from '../../../../../../shared/agent-session/journal/agent-session-journal-types' -// Without `--page` a run only knows the worktree's latest agent page, so each run keeps the -// page it first resolved to instead of following later runs to newer pages. +// A run's page outlives the run only here: no transcript records the page a command without +// `--page` drove, so a run seen while it was the chat's latest keeps that page in this renderer. const pageIdByRunCallId = new Map() const REMEMBERED_RUNS_MAX = 500 -// Why: state, not a render-time Map read, so React Compiler's caches see the remembered page. -function startRun(runCallId: string | null): { - runCallId: string | null - revision: number - rememberedPageId: string | undefined -} { - return { - runCallId, - revision: getBrowserAgentActivityRevision(), - rememberedPageId: runCallId ? pageIdByRunCallId.get(runCallId) : undefined +function commandPageId(call: NativeChatToolCallBlock | undefined): string | null { + return parseOrcaBrowserCommand(nativeChatFullCommand(call?.input) ?? '')?.pageId ?? null +} + +/** The page the run's newest call named or was remembered with. */ +function rememberedRunPageId(calls: readonly NativeChatToolCallBlock[]): string | null { + for (let index = calls.length - 1; index >= 0; index -= 1) { + const call = calls[index] + const pageId = commandPageId(call) ?? (call.callId ? pageIdByRunCallId.get(call.callId) : null) + if (pageId) { + return pageId + } } + return null +} + +const latestBrowserCallIdByItems = new WeakMap() + +function latestBrowserCallId(items: readonly AgentJournalRenderItem[]): string | null { + const cached = latestBrowserCallIdByItems.get(items) + if (cached !== undefined) { + return cached + } + let callId: string | null = null + for (let index = items.length - 1; index >= 0; index -= 1) { + const { body, itemId } = items[index] + if ( + body.kind === 'tool-call' && + nativeChatToolCategory(body.name, body.mcpIdentity, body.input) === 'browser' + ) { + // The same identity structuredAgentSessionToolCallBlock gives the rendered call. + callId = body.callId ?? itemId + break + } + } + latestBrowserCallIdByItems.set(items, callId) + return callId +} + +const NO_READ_OWNER = (): (() => void) => () => {} + +/** The chat's newest browser call: its agent's browser activity is that call's run's. */ +function useLatestBrowserCallId(owner: NativeChatVisualOwner | null): string | null { + const readOwner = owner + ? findStructuredAgentSessionReadOwner(owner.sessionId, owner.target) + : undefined + const read = (): string | null => { + const snapshot = readOwner?.getSnapshot() + // A transcript behind the host may lack the newer call that drove the latest activity. + return snapshot?.current ? latestBrowserCallId(snapshot.state.items) : null + } + return useSyncExternalStore(readOwner?.subscribe ?? NO_READ_OWNER, read, read) } /** Reveals the page a run of agent browser commands used; nothing for any other run. */ @@ -42,38 +88,36 @@ export function NativeChatBrowserRunPageButton({ if (nativeChatToolRunCategory(calls) !== 'browser') { return null } - const lastCall = calls.at(-1) - const lastCommand = nativeChatFullCommand(lastCall?.input) ?? '' - return ( - - ) + return } function BrowserPageButton({ - pageId, - runCallId + calls }: { - /** The page the command named with `--page`; otherwise the worktree's agent page. */ - pageId: string | null - runCallId: string | null + calls: readonly NativeChatToolCallBlock[] }): React.JSX.Element | null { const t = useTranslate() const owner = useNativeChatVisualOwner() - const latest = useLastBrowserAgentActivity(owner?.worktreeId ?? null, owner?.sessionId ?? null) - // A call appears before its command reaches the browser, so only later activity is its own. - const [baseline, setBaseline] = useState(() => startRun(runCallId)) - if (baseline.runCallId !== runCallId) { - setBaseline(startRun(runCallId)) + const lastCall = calls.at(-1) + const runCallId = lastCall?.callId ?? null + const latestPageId = useLastBrowserAgentPageId( + owner?.worktreeId ?? null, + owner?.sessionId ?? null + ) + const isLatestRun = useLatestBrowserCallId(owner) === runCallId && runCallId !== null + // Why: state, not a render-time Map read, so React Compiler's caches see the remembered page. + const [run, setRun] = useState(() => ({ runCallId, pageId: rememberedRunPageId(calls) })) + if (run.runCallId !== runCallId) { + setRun({ runCallId, pageId: rememberedRunPageId(calls) }) } - const ownActivityPageId = latest && latest.revision > baseline.revision ? latest.pageId : null - if (runCallId && !pageId && !baseline.rememberedPageId && ownActivityPageId) { - setBaseline({ ...baseline, rememberedPageId: ownActivityPageId }) + // Only the chat's newest browser run owns its agent's latest activity; older runs keep theirs. + const activityPageId = isLatestRun ? latestPageId : null + if (run.runCallId === runCallId && activityPageId && run.pageId !== activityPageId) { + setRun({ runCallId, pageId: activityPageId }) } - const rememberedPageId = baseline.rememberedPageId - const targetPageId = pageId ?? rememberedPageId ?? latest?.pageId ?? null + const rememberedPageId = run.runCallId === runCallId ? run.pageId : null + // Why: a run that can't name its page shows nothing rather than another run's page. + const targetPageId = commandPageId(lastCall) ?? rememberedPageId useEffect(() => { if (!runCallId || !rememberedPageId || pageIdByRunCallId.get(runCallId) === rememberedPageId) { return From d1bc6605c1862c08ddd7c9a5f99934171df5d6f4 Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 09:39:16 +0000 Subject: [PATCH 060/178] fix(speech): correct near-miss spellings of dictionary terms, like Superbase to Supabase (#41703) * fix(speech): spell near misses of dictionary terms the term's way * fix(speech): ship the common-word list as a 2% Bloom filter to fit the phone page budget --- .../build-dictation-english-words.mjs | 115 ++++++++ resources/licenses/scowl/Copyright | 248 +++++++++++++++++ src/shared/dictation-dictionary.test.ts | 28 -- src/shared/dictation-dictionary.ts | 109 -------- src/shared/dictation-english-words.ts | 263 ++++++++++++++++++ src/shared/dictation-near-miss.ts | 103 +++++++ src/shared/dictation-term-spelling.test.ts | 82 ++++++ src/shared/dictation-term-spelling.ts | 134 +++++++++ src/shared/dictation-text-pipeline.ts | 5 +- src/shared/dictation-word-filter.ts | 31 +++ 10 files changed, 979 insertions(+), 139 deletions(-) create mode 100644 config/scripts/dictation/build-dictation-english-words.mjs create mode 100644 resources/licenses/scowl/Copyright delete mode 100644 src/shared/dictation-dictionary.test.ts create mode 100644 src/shared/dictation-english-words.ts create mode 100644 src/shared/dictation-near-miss.ts create mode 100644 src/shared/dictation-term-spelling.test.ts create mode 100644 src/shared/dictation-term-spelling.ts create mode 100644 src/shared/dictation-word-filter.ts diff --git a/config/scripts/dictation/build-dictation-english-words.mjs b/config/scripts/dictation/build-dictation-english-words.mjs new file mode 100644 index 00000000000..cf374a36cd9 --- /dev/null +++ b/config/scripts/dictation/build-dictation-english-words.mjs @@ -0,0 +1,115 @@ +// Regenerates src/shared/dictation-english-words.ts, the common-word filter dictation's near-miss matching +// checks before spelling a word as a dictionary term. Run: node config/scripts/dictation/build-dictation-english-words.mjs +import { execFileSync } from 'node:child_process' +import { createHash } from 'node:crypto' +import { mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { wordFilterSlots } from '../../../src/shared/dictation-word-filter.ts' + +const SOURCES = { + scowl: [ + 'https://downloads.sourceforge.net/project/wordlist/SCOWL/2020.12.07/scowl-2020.12.07.tar.gz', + '5587667caa20c4891390c2d42dbb4d5c4c3f41bee77af1457ece3ba23fb859cc' + ], + femaleFirst: [ + 'https://www2.census.gov/topics/genealogy/1990surnames/dist.female.first', + 'bd2f310fc4e5d5e5ea122c9d4342c9821145823118eb20db1647f305ec77b358' + ], + maleFirst: [ + 'https://www2.census.gov/topics/genealogy/1990surnames/dist.male.first', + '0a5078ef6effe3b483d15b0f7f95047662126c9bfb624ecd5e5b978fc0f2470b' + ], + last: [ + 'https://www2.census.gov/topics/genealogy/1990surnames/dist.all.last', + 'b0e2b3743ccbad641ca48b344c24cdebcd1d9a1f76dc6dbf05986f2919f0b4e1' + ] +} +// SCOWL sizes up to 60: everyday words plus the rarer ones ("bezel", "aslant") a term could sound like. +const SCOWL_MAX_SIZE = 60 +const LAST_NAMES = 1000 +const FALSE_POSITIVE_RATE = 0.02 +// Matching never looks up words shorter than this. +const MIN_WORD_LENGTH = 3 +const OUT = 'src/shared/dictation-english-words.ts' + +async function download([url, sha256]) { + const response = await fetch(url) + if (!response.ok) { + throw new Error(`${url}: HTTP ${response.status}`) + } + const bytes = Buffer.from(await response.arrayBuffer()) + const actual = createHash('sha256').update(bytes).digest('hex') + if (actual !== sha256) { + throw new Error(`${url}: sha256 ${actual}, expected ${sha256}`) + } + return bytes +} + +const work = mkdtempSync(join(tmpdir(), 'dictation-english-words-')) +try { + const words = new Set() + // Contractions add their stems ("doesn" of "doesn't"), since dictated text splits words at apostrophes. + const add = (entry) => { + for (const part of entry.split("'")) { + if (/^[A-Za-z]+$/.test(part) && part.length >= MIN_WORD_LENGTH) { + words.add(part.toLowerCase()) + } + } + } + writeFileSync(join(work, 'scowl.tar.gz'), await download(SOURCES.scowl)) + execFileSync('tar', ['-xzf', 'scowl.tar.gz'], { cwd: work }) + const final = join(work, 'scowl-2020.12.07', 'final') + for (const file of readdirSync(final)) { + const list = + /^(?:english|american|british|canadian|australian)-(?:words|upper|contractions)\.(\d+)$/.exec( + file + ) + if (list && Number(list[1]) <= SCOWL_MAX_SIZE) { + readFileSync(join(final, file), 'latin1').split('\n').forEach(add) + } + } + const names = async (source, limit) => + (await download(source)) + .toString('latin1') + .trim() + .split('\n') + .slice(0, limit) + .map((line) => line.split(/\s+/)[0]) + for (const name of [ + ...(await names(SOURCES.femaleFirst)), + ...(await names(SOURCES.maleFirst)), + ...(await names(SOURCES.last, LAST_NAMES)) + ]) { + add(name) + } + + const bits = Math.ceil((-words.size * Math.log(FALSE_POSITIVE_RATE)) / Math.LN2 ** 2 / 8) * 8 + const hashes = Math.round((bits / words.size) * Math.LN2) + const bytes = new Uint8Array(bits / 8) + for (const word of words) { + for (const slot of wordFilterSlots(word, bits, hashes)) { + bytes[slot >>> 3] |= 1 << (slot & 7) + } + } + // The whole SCOWL notice (Atkinson, WordNet, VarCon, Ispell and the rest) rides in every bundle that keeps legal comments. + const license = readFileSync('resources/licenses/scowl/Copyright', 'utf8').trimEnd().split('\n') + const notice = [ + `A filter of ${words.size} common English words and names: SCOWL 2020.12.07 words, capitalized words and`, + `contractions up to size ${SCOWL_MAX_SIZE} (english, american, british, canadian, australian), a-z only, lowercased,`, + `plus the 1990 US census first names and ${LAST_NAMES} most common last names (public domain). Generated by`, + 'config/scripts/dictation/build-dictation-english-words.mjs from these SCOWL notices:', + '', + ...license.map((line) => line.replaceAll('*/', '* /')) + ] + writeFileSync( + OUT, + `/*! @license\n${notice.map((line) => ` * ${line}`.trimEnd()).join('\n')}\n */\n` + + `import type { WordFilter } from './dictation-word-filter'\n\n` + + `export const COMMON_ENGLISH_WORDS: WordFilter = {\n bits: ${bits},\n hashes: ${hashes},\n` + + ` base64:\n '${Buffer.from(bytes).toString('base64')}'\n}\n` + ) + console.log(`${OUT}: ${words.size} words, ${bits} bits, ${hashes} hashes`) +} finally { + rmSync(work, { recursive: true, force: true }) +} diff --git a/resources/licenses/scowl/Copyright b/resources/licenses/scowl/Copyright new file mode 100644 index 00000000000..e49dc166490 --- /dev/null +++ b/resources/licenses/scowl/Copyright @@ -0,0 +1,248 @@ +The collective work is Copyright 2000-2018 by Kevin Atkinson as well +as any of the copyrights mentioned below: + + Copyright 2000-2018 by Kevin Atkinson + + Permission to use, copy, modify, distribute and sell these word + lists, the associated scripts, the output created from the scripts, + and its documentation for any purpose is hereby granted without fee, + provided that the above copyright notice appears in all copies and + that both that copyright notice and this permission notice appear in + supporting documentation. Kevin Atkinson makes no representations + about the suitability of this array for any purpose. It is provided + "as is" without express or implied warranty. + +Alan Beale also deserves special credit as he has, +in addition to providing the 12Dicts package and being a major +contributor to the ENABLE word list, given me an incredible amount of +feedback and created a number of special lists (those found in the +Supplement) in order to help improve the overall quality of SCOWL. + +The 10 level includes the 1000 most common English words (according to +the Moby (TM) Words II [MWords] package), a subset of the 1000 most +common words on the Internet (again, according to Moby Words II), and +frequently class 16 from Brian Kelk's "UK English Wordlist +with Frequency Classification". + +The MWords package was explicitly placed in the public domain: + + The Moby lexicon project is complete and has + been place into the public domain. Use, sell, + rework, excerpt and use in any way on any platform. + + Placing this material on internal or public servers is + also encouraged. The compiler is not aware of any + export restrictions so freely distribute world-wide. + + You can verify the public domain status by contacting + + Grady Ward + 3449 Martha Ct. + Arcata, CA 95521-4884 + + grady@netcom.com + grady@northcoast.com + +The "UK English Wordlist With Frequency Classification" is also in the +Public Domain: + + Date: Sat, 08 Jul 2000 20:27:21 +0100 + From: Brian Kelk + + > I was wondering what the copyright status of your "UK English + > Wordlist With Frequency Classification" word list as it seems to + > be lacking any copyright notice. + + There were many many sources in total, but any text marked + "copyright" was avoided. Locally-written documentation was one + source. An earlier version of the list resided in a filespace called + PUBLIC on the University mainframe, because it was considered public + domain. + + Date: Tue, 11 Jul 2000 19:31:34 +0100 + + > So are you saying your word list is also in the public domain? + + That is the intention. + +The 20 level includes frequency classes 7-15 from Brian's word list. + +The 35 level includes frequency classes 2-6 and words appearing in at +least 11 of 12 dictionaries as indicated in the 12Dicts package. All +words from the 12Dicts package have had likely inflections added via +my inflection database. + +The 12Dicts package and Supplement is in the Public Domain. + +The WordNet database, which was used in the creation of the +Inflections database, is under the following copyright: + + This software and database is being provided to you, the LICENSEE, + by Princeton University under the following license. By obtaining, + using and/or copying this software and database, you agree that you + have read, understood, and will comply with these terms and + conditions.: + + Permission to use, copy, modify and distribute this software and + database and its documentation for any purpose and without fee or + royalty is hereby granted, provided that you agree to comply with + the following copyright notice and statements, including the + disclaimer, and that the same appear on ALL copies of the software, + database and documentation, including modifications that you make + for internal use or for distribution. + + WordNet 1.6 Copyright 1997 by Princeton University. All rights + reserved. + + THIS SOFTWARE AND DATABASE IS PROVIDED "AS IS" AND PRINCETON + UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PRINCETON + UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- + ABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE + LICENSED SOFTWARE, DATABASE OR DOCUMENTATION WILL NOT INFRINGE ANY + THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + + The name of Princeton University or Princeton may not be used in + advertising or publicity pertaining to distribution of the software + and/or database. Title to copyright in this software, database and + any associated documentation shall at all times remain with + Princeton University and LICENSEE agrees to preserve same. + +The 40 level includes words from Alan's 3esl list found in version 4.0 +of his 12dicts package. Like his other stuff the 3esl list is also in the +public domain. + +The 50 level includes Brian's frequency class 1, words appearing +in at least 5 of 12 of the dictionaries as indicated in the 12Dicts +package, and uppercase words in at least 4 of the previous 12 +dictionaries. A decent number of proper names is also included: The +top 1000 male, female, and Last names from the 1990 Census report; a +list of names sent to me by Alan Beale; and a few names that I added +myself. Finally a small list of abbreviations not commonly found in +other word lists is included. + +The name files form the Census report is a government document which I +don't think can be copyrighted. + +The file special-jargon.50 uses common.lst and word.lst from the +"Unofficial Jargon File Word Lists" which is derived from "The Jargon +File". All of which is in the Public Domain. This file also contain +a few extra UNIX terms which are found in the file "unix-terms" in the +special/ directory. + +The 55 level includes words from Alan's 2of4brif list found in version +4.0 of his 12dicts package. Like his other stuff the 2of4brif is also +in the public domain. + +The 60 level includes all words appearing in at least 2 of the 12 +dictionaries as indicated by the 12Dicts package. + +The 70 level includes Brian's frequency class 0 and the 74,550 common +dictionary words from the MWords package. The common dictionary words, +like those from the 12Dicts package, have had all likely inflections +added. The 70 level also included the 5desk list from version 4.0 of +the 12Dics package which is in the public domain. + +The 80 level includes the ENABLE word list, all the lists in the +ENABLE supplement package (except for ABLE), the "UK Advanced Cryptics +Dictionary" (UKACD), the list of signature words from the YAWL package, +and the 10,196 places list from the MWords package. + +The ENABLE package, mainted by M\Cooper , +is in the Public Domain: + + The ENABLE master word list, WORD.LST, is herewith formally released + into the Public Domain. Anyone is free to use it or distribute it in + any manner they see fit. No fee or registration is required for its + use nor are "contributions" solicited (if you feel you absolutely + must contribute something for your own peace of mind, the authors of + the ENABLE list ask that you make a donation on their behalf to your + favorite charity). This word list is our gift to the Scrabble + community, as an alternate to "official" word lists. Game designers + may feel free to incorporate the WORD.LST into their games. Please + mention the source and credit us as originators of the list. Note + that if you, as a game designer, use the WORD.LST in your product, + you may still copyright and protect your product, but you may *not* + legally copyright or in any way restrict redistribution of the + WORD.LST portion of your product. This *may* under law restrict your + rights to restrict your users' rights, but that is only fair. + +UKACD, by J Ross Beresford , is under the +following copyright: + + Copyright (c) J Ross Beresford 1993-1999. All Rights Reserved. + + The following restriction is placed on the use of this publication: + if The UK Advanced Cryptics Dictionary is used in a software package + or redistributed in any form, the copyright notice must be + prominently displayed and the text of this document must be included + verbatim. + + There are no other restrictions: I would like to see the list + distributed as widely as possible. + +The 95 level includes the 354,984 single words, 256,772 compound +words, 4,946 female names and the 3,897 male names, and 21,986 names +from the MWords package, ABLE.LST from the ENABLE Supplement, and some +additional words found in my part-of-speech database that were not +found anywhere else. + +Accent information was taken from UKACD. + +The VarCon package was used to create the American, British, Canadian, +and Australian word list. It is under the following copyright: + + Copyright 2000-2016 by Kevin Atkinson + + Permission to use, copy, modify, distribute and sell this array, the + associated software, and its documentation for any purpose is hereby + granted without fee, provided that the above copyright notice appears + in all copies and that both that copyright notice and this permission + notice appear in supporting documentation. Kevin Atkinson makes no + representations about the suitability of this array for any + purpose. It is provided "as is" without express or implied warranty. + + Copyright 2016 by Benjamin Titze + + Permission to use, copy, modify, distribute and sell this array, the + associated software, and its documentation for any purpose is hereby + granted without fee, provided that the above copyright notice appears + in all copies and that both that copyright notice and this permission + notice appear in supporting documentation. Benjamin Titze makes no + representations about the suitability of this array for any + purpose. It is provided "as is" without express or implied warranty. + + Since the original words lists come from the Ispell distribution: + + Copyright 1993, Geoff Kuenning, Granada Hills, CA + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All modifications to the source code must be clearly marked as + such. Binary redistributions based on modified source code + must be clearly marked as modified versions in the documentation + and/or other materials provided with the distribution. + (clause 4 removed with permission from Geoff Kuenning) + 5. The name of Geoff Kuenning may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. diff --git a/src/shared/dictation-dictionary.test.ts b/src/shared/dictation-dictionary.test.ts deleted file mode 100644 index cb72fb380d3..00000000000 --- a/src/shared/dictation-dictionary.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { createDictationDictionary } from './dictation-dictionary' - -const spell = (terms: Parameters[0], text: string): string => - createDictationDictionary(terms)!(text) - -describe('createDictationDictionary', () => { - // Parakeet v3's real output for the dev bench's code-term clips. - it('joins the words a model splits a code term into back into the term', () => { - const terms = [ - { term: 'useAudioCapture' }, - { term: 'usePcmWorklet' }, - { term: 'stt-worker.ts' }, - { term: 'numThreads' }, - { term: 'useMemo' }, - { term: 'onChange' } - ] - expect( - spell(terms, 'Rename use audio capture to use PCM worklet in the dictation controller.') - ).toBe('Rename useAudioCapture to usePcmWorklet in the dictation controller.') - expect(spell(terms, 'Open stt worker TS and set num threads to 4.')).toBe( - 'Open stt-worker.ts and set numThreads to 4.' - ) - expect(spell(terms, 'Wrap it in use memo and pass the on-change call back.')).toBe( - 'Wrap it in useMemo and pass the onChange call back.' - ) - }) -}) diff --git a/src/shared/dictation-dictionary.ts b/src/shared/dictation-dictionary.ts index ad448055809..44c7523f0f2 100644 --- a/src/shared/dictation-dictionary.ts +++ b/src/shared/dictation-dictionary.ts @@ -7,22 +7,6 @@ export type DictationTerm = { term: string; heardAs?: string; hint?: true } export const MAX_DICTATION_TERMS = 500 export const MAX_DICTATION_TERM_LENGTH = 100 -// Symbols that belong to a name ("C#", "C++", "@orca") count as part of a word, so a term never doubles them. -const WORD_RE = /[\p{L}\p{M}\p{N}#+@$&]+/gu -// Spaces, or one joiner as in "on-change" or "stt-worker.ts": what may sit between the words of one term. -const JOINING_GAP_RE = /^(?:\s+|[-_./])$/u -// A term's own leading or trailing joiners, as in ".NET", taken along when the text already has them. -const EDGE_JOINERS_RE = /^([-_./]*)[\s\S]*?([-_./]*)$/u -// Scripts written without spaces, where a phrase can only be found inside a longer run of letters. -const UNSPACED_SCRIPT_RE = - /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Thai}\p{Script=Lao}\p{Script=Khmer}\p{Script=Myanmar}]/u -const MAX_WORDS_PER_TERM = 8 - -// "Use Audio Capture", "use-audio-capture" and "useAudioCapture" all key as "useaudiocapture". -function wordKey(text: string): string { - return (text.normalize('NFKC').toLowerCase().match(WORD_RE) ?? []).join('') -} - /** Well-formed, distinct entries only, trimmed and capped; undefined when the value isn't a list. */ export function normalizeDictationTerms(value: unknown): DictationTerm[] | undefined { if (!Array.isArray(value)) { @@ -69,99 +53,6 @@ export function dictationTermsFor( ] } -type Spelling = { term: string; lead: string; trail: string } - -function spelling(term: string): Spelling { - const [, lead = '', trail = ''] = EDGE_JOINERS_RE.exec(term) ?? [] - return { term, lead, trail } -} - -/** Rewrites words that key the same as a term (or its "heard as") to the term's spelling; null when there are none. */ -export function createDictationDictionary( - terms: readonly DictationTerm[] | undefined -): ((text: string) => string) | null { - const byKey = new Map() - const unspacedAliases: [heardAs: string, term: string][] = [] - const add = (key: string, term: string): void => { - if (key && !byKey.has(key)) { - byKey.set(key, spelling(term)) - } - } - // A "heard as" is what the user asked for, so it beats another term spelled the same. - for (const { term, heardAs } of terms ?? []) { - if (heardAs && UNSPACED_SCRIPT_RE.test(heardAs)) { - unspacedAliases.push([heardAs, term]) - } else if (heardAs) { - add(wordKey(heardAs), term) - } - } - for (const { term, hint } of terms ?? []) { - // A one-word repo name like "orca" would recase every "Orca"; only a name of several words joins. - if (!hint || (term.match(WORD_RE)?.length ?? 0) > 1) { - add(wordKey(term), term) - } - } - if (byKey.size === 0 && unspacedAliases.length === 0) { - return null - } - const longestKey = Math.max(0, ...[...byKey.keys()].map((key) => key.length)) - return (text) => { - for (const [alias, term] of unspacedAliases) { - text = text.split(alias).join(term) - } - return byKey.size > 0 ? replaceWordRuns(text, byKey, longestKey) : text - } -} - -// At each word, the longest run of joined words whose key is a term's becomes that term. -function replaceWordRuns(text: string, byKey: Map, longestKey: number): string { - const words = [...text.matchAll(WORD_RE)].map((word) => ({ - from: word.index, - to: word.index + word[0].length, - key: wordKey(word[0]) - })) - // joinsNext[i]: words i and i + 1 may belong to one term. - const joinsNext = words.map( - (word, i) => i + 1 < words.length && JOINING_GAP_RE.test(text.slice(word.to, words[i + 1].from)) - ) - let out = '' - let cursor = 0 - for (let start = 0; start < words.length;) { - let key = '' - let match: { end: number; spelling: Spelling } | null = null - for (let end = start; end < words.length && end - start < MAX_WORDS_PER_TERM; end++) { - if (end > start && !joinsNext[end - 1]) { - break - } - key += words[end].key - if (key.length > longestKey) { - break - } - const found = byKey.get(key) - if (found) { - match = { end, spelling: found } - } - } - if (!match) { - start++ - continue - } - const { term, lead, trail } = match.spelling - let from = words[start].from - let to = words[match.end].to - if (lead && from - lead.length >= cursor && text.slice(from - lead.length, from) === lead) { - from -= lead.length - } - if (trail && text.startsWith(trail, to)) { - to += trail.length - } - out += text.slice(cursor, from) + term - cursor = to - start = match.end + 1 - } - return out + text.slice(cursor) -} - /** The distinct spellings an engine that takes a context prompt is told to expect. */ export function dictationVocabulary(terms: readonly DictationTerm[] | undefined): string[] { return [...new Set((terms ?? []).map((entry) => entry.term))] diff --git a/src/shared/dictation-english-words.ts b/src/shared/dictation-english-words.ts new file mode 100644 index 00000000000..1cb6de43f84 --- /dev/null +++ b/src/shared/dictation-english-words.ts @@ -0,0 +1,263 @@ +/*! @license + * A filter of 89958 common English words and names: SCOWL 2020.12.07 words, capitalized words and + * contractions up to size 60 (english, american, british, canadian, australian), a-z only, lowercased, + * plus the 1990 US census first names and 1000 most common last names (public domain). Generated by + * config/scripts/dictation/build-dictation-english-words.mjs from these SCOWL notices: + * + * The collective work is Copyright 2000-2018 by Kevin Atkinson as well + * as any of the copyrights mentioned below: + * + * Copyright 2000-2018 by Kevin Atkinson + * + * Permission to use, copy, modify, distribute and sell these word + * lists, the associated scripts, the output created from the scripts, + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation. Kevin Atkinson makes no representations + * about the suitability of this array for any purpose. It is provided + * "as is" without express or implied warranty. + * + * Alan Beale also deserves special credit as he has, + * in addition to providing the 12Dicts package and being a major + * contributor to the ENABLE word list, given me an incredible amount of + * feedback and created a number of special lists (those found in the + * Supplement) in order to help improve the overall quality of SCOWL. + * + * The 10 level includes the 1000 most common English words (according to + * the Moby (TM) Words II [MWords] package), a subset of the 1000 most + * common words on the Internet (again, according to Moby Words II), and + * frequently class 16 from Brian Kelk's "UK English Wordlist + * with Frequency Classification". + * + * The MWords package was explicitly placed in the public domain: + * + * The Moby lexicon project is complete and has + * been place into the public domain. Use, sell, + * rework, excerpt and use in any way on any platform. + * + * Placing this material on internal or public servers is + * also encouraged. The compiler is not aware of any + * export restrictions so freely distribute world-wide. + * + * You can verify the public domain status by contacting + * + * Grady Ward + * 3449 Martha Ct. + * Arcata, CA 95521-4884 + * + * grady@netcom.com + * grady@northcoast.com + * + * The "UK English Wordlist With Frequency Classification" is also in the + * Public Domain: + * + * Date: Sat, 08 Jul 2000 20:27:21 +0100 + * From: Brian Kelk + * + * > I was wondering what the copyright status of your "UK English + * > Wordlist With Frequency Classification" word list as it seems to + * > be lacking any copyright notice. + * + * There were many many sources in total, but any text marked + * "copyright" was avoided. Locally-written documentation was one + * source. An earlier version of the list resided in a filespace called + * PUBLIC on the University mainframe, because it was considered public + * domain. + * + * Date: Tue, 11 Jul 2000 19:31:34 +0100 + * + * > So are you saying your word list is also in the public domain? + * + * That is the intention. + * + * The 20 level includes frequency classes 7-15 from Brian's word list. + * + * The 35 level includes frequency classes 2-6 and words appearing in at + * least 11 of 12 dictionaries as indicated in the 12Dicts package. All + * words from the 12Dicts package have had likely inflections added via + * my inflection database. + * + * The 12Dicts package and Supplement is in the Public Domain. + * + * The WordNet database, which was used in the creation of the + * Inflections database, is under the following copyright: + * + * This software and database is being provided to you, the LICENSEE, + * by Princeton University under the following license. By obtaining, + * using and/or copying this software and database, you agree that you + * have read, understood, and will comply with these terms and + * conditions.: + * + * Permission to use, copy, modify and distribute this software and + * database and its documentation for any purpose and without fee or + * royalty is hereby granted, provided that you agree to comply with + * the following copyright notice and statements, including the + * disclaimer, and that the same appear on ALL copies of the software, + * database and documentation, including modifications that you make + * for internal use or for distribution. + * + * WordNet 1.6 Copyright 1997 by Princeton University. All rights + * reserved. + * + * THIS SOFTWARE AND DATABASE IS PROVIDED "AS IS" AND PRINCETON + * UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + * IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PRINCETON + * UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- + * ABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE + * LICENSED SOFTWARE, DATABASE OR DOCUMENTATION WILL NOT INFRINGE ANY + * THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + * + * The name of Princeton University or Princeton may not be used in + * advertising or publicity pertaining to distribution of the software + * and/or database. Title to copyright in this software, database and + * any associated documentation shall at all times remain with + * Princeton University and LICENSEE agrees to preserve same. + * + * The 40 level includes words from Alan's 3esl list found in version 4.0 + * of his 12dicts package. Like his other stuff the 3esl list is also in the + * public domain. + * + * The 50 level includes Brian's frequency class 1, words appearing + * in at least 5 of 12 of the dictionaries as indicated in the 12Dicts + * package, and uppercase words in at least 4 of the previous 12 + * dictionaries. A decent number of proper names is also included: The + * top 1000 male, female, and Last names from the 1990 Census report; a + * list of names sent to me by Alan Beale; and a few names that I added + * myself. Finally a small list of abbreviations not commonly found in + * other word lists is included. + * + * The name files form the Census report is a government document which I + * don't think can be copyrighted. + * + * The file special-jargon.50 uses common.lst and word.lst from the + * "Unofficial Jargon File Word Lists" which is derived from "The Jargon + * File". All of which is in the Public Domain. This file also contain + * a few extra UNIX terms which are found in the file "unix-terms" in the + * special/ directory. + * + * The 55 level includes words from Alan's 2of4brif list found in version + * 4.0 of his 12dicts package. Like his other stuff the 2of4brif is also + * in the public domain. + * + * The 60 level includes all words appearing in at least 2 of the 12 + * dictionaries as indicated by the 12Dicts package. + * + * The 70 level includes Brian's frequency class 0 and the 74,550 common + * dictionary words from the MWords package. The common dictionary words, + * like those from the 12Dicts package, have had all likely inflections + * added. The 70 level also included the 5desk list from version 4.0 of + * the 12Dics package which is in the public domain. + * + * The 80 level includes the ENABLE word list, all the lists in the + * ENABLE supplement package (except for ABLE), the "UK Advanced Cryptics + * Dictionary" (UKACD), the list of signature words from the YAWL package, + * and the 10,196 places list from the MWords package. + * + * The ENABLE package, mainted by M\Cooper , + * is in the Public Domain: + * + * The ENABLE master word list, WORD.LST, is herewith formally released + * into the Public Domain. Anyone is free to use it or distribute it in + * any manner they see fit. No fee or registration is required for its + * use nor are "contributions" solicited (if you feel you absolutely + * must contribute something for your own peace of mind, the authors of + * the ENABLE list ask that you make a donation on their behalf to your + * favorite charity). This word list is our gift to the Scrabble + * community, as an alternate to "official" word lists. Game designers + * may feel free to incorporate the WORD.LST into their games. Please + * mention the source and credit us as originators of the list. Note + * that if you, as a game designer, use the WORD.LST in your product, + * you may still copyright and protect your product, but you may *not* + * legally copyright or in any way restrict redistribution of the + * WORD.LST portion of your product. This *may* under law restrict your + * rights to restrict your users' rights, but that is only fair. + * + * UKACD, by J Ross Beresford , is under the + * following copyright: + * + * Copyright (c) J Ross Beresford 1993-1999. All Rights Reserved. + * + * The following restriction is placed on the use of this publication: + * if The UK Advanced Cryptics Dictionary is used in a software package + * or redistributed in any form, the copyright notice must be + * prominently displayed and the text of this document must be included + * verbatim. + * + * There are no other restrictions: I would like to see the list + * distributed as widely as possible. + * + * The 95 level includes the 354,984 single words, 256,772 compound + * words, 4,946 female names and the 3,897 male names, and 21,986 names + * from the MWords package, ABLE.LST from the ENABLE Supplement, and some + * additional words found in my part-of-speech database that were not + * found anywhere else. + * + * Accent information was taken from UKACD. + * + * The VarCon package was used to create the American, British, Canadian, + * and Australian word list. It is under the following copyright: + * + * Copyright 2000-2016 by Kevin Atkinson + * + * Permission to use, copy, modify, distribute and sell this array, the + * associated software, and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice appears + * in all copies and that both that copyright notice and this permission + * notice appear in supporting documentation. Kevin Atkinson makes no + * representations about the suitability of this array for any + * purpose. It is provided "as is" without express or implied warranty. + * + * Copyright 2016 by Benjamin Titze + * + * Permission to use, copy, modify, distribute and sell this array, the + * associated software, and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice appears + * in all copies and that both that copyright notice and this permission + * notice appear in supporting documentation. Benjamin Titze makes no + * representations about the suitability of this array for any + * purpose. It is provided "as is" without express or implied warranty. + * + * Since the original words lists come from the Ispell distribution: + * + * Copyright 1993, Geoff Kuenning, Granada Hills, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * (clause 4 removed with permission from Geoff Kuenning) + * 5. The name of Geoff Kuenning may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +import type { WordFilter } from './dictation-word-filter' + +export const COMMON_ENGLISH_WORDS: WordFilter = { + bits: 732472, + hashes: 6, + base64: + 'W1HV6wIOBvq0Ai7KuYtZwzCXynGQfFTMg2TNdRTGmq55mvOrlclJEMhBCV5onb4ySD7ZSZuwjFWx/98g/R1Opq087/GwtqjmMmmWOIRLrmcpoXIrhwcPsqx5TvucLOhzHVIcDVw4tjRxsoFHNvt3EcxzSWb81otRd4E9T1e7AqnG4542eCUJRvwT245zSvOz7jXuvHwZUVb5jBpbtK+u3vttCYXNs4gfQIR+OQtB7oHewU5tByRqNrKwUfEkdZDE+s0GtFJhJUtpyK4NUgcs+M+jpe8TVeOj9sjP8apcBqu2/axQh7MGhVlPa1yWFB6VwnaZUxXkZP8pT+texx1ZLMCIaDqYkr3uEnD/P+W6DRyPpINOsry7z7/Fvfx0aY24tNxiKd/j/jmvyTdahyvs1ALGwTWLqg/4bclq5cEAfWg1aAW632U1JoOmQYw/u6vR3uf22UJzZOX+pZtEc53//0iqxUNQHH55FkawRR+QC1ZayfftAHom7J/o+uOOyRDvqzINM6r+rUgqIDlhL38geQuOzq8Muv4TldrP77Tjx2wOtvUPFdwty9CkWjvQ4KaNPUU3vpP2TvOgK29nCRp26ckt6rRFxawHTbofz+NeOkkVna3Hv/Lkrsbzzo3F0epvCP4v91liiVltSutYIgV3NAOp4CC9bt2KLPcaSpe2Y7+H8TFz1dqhkwoGBca2CZm+l407iw3zLbrJll5Dybo6bc3Amo6Qg0CCbtNa2/mCEkeiD7AGi8bpZXQE7wtupxfZPX0DFDRCUzU99JXHRok+vWWZ++VQgZcuqdmYKnOYu2Whb0oBz0raVI+xP713ag8MSjwmjYSgk56+a32E/JqAfON8G5yoUzPhmzzNYfJc7VXmgIfYW56ZYod7Jen4GQTwyC6JZJ03Muoy1+KB1WNJvXcszri+yeBWu+YoX45bqeK/Wtcb/rTrPNVix0PvMEOP3ThtGGBvAPrFw+Yim5DD0sEH96McYN7Xx9h7XJaxWhrwOU8ZOUJThSDGkgydbt5/C0QQeUHQln5js6chvy+k3k523yWwcwYmqg0rT2QtBm3HTUt+GPmb+0P9zb9Ef27Ri/yFdrQAabfRjbV2Jx9zbf3Haj2cktzMFk0VeGyXkx0rCGfjnmo3/RCMQ4d9ttpicbXkRgZo+B3fpEfpdyXqLkDDDM1/E7nOpfadGHpNHr7FGn0oferGv8t4TXl/9uPFr+KcoU0Vv9l2sLiskeLD6kVDTQIwlYR3hIfh6X1ShtUAzew5eiL6g2j1109w35eGYF+fSbqWqwXXPLyf7yih5oT9y6DcYRNA60pmodxJ7k6O9ETsV+bFvm4KKGj5zpkm9VVReyYlMSyoQFAodR0JyDVXEQDNyuKHD/i1uLUa6TaIi13qM+ZtLj4wvqi3Krlh9P25yTMAZeiiJ6onxKEK0d+X7Q/wmwQE3ysfPFzQx+Fr6Otjxplu6vZp0YeCPb7TxZPlhsoMRYGouA4uNVanDlZpieIf6zCO+qe1kx3v2txcR1nkxUxQu8IclSUMDtXT45FVdRMlY3EzfRZy7FmTRtEwsdO9qJR1TvS3qU6EseE4XKO+GKelt0g63relnfdcNWulWUwWB7ebWZBp1fREN+U3xu/tfN/BtbEid7jPgquC0d9G7dByjvwbTm842m6sexmFdTeDbgqsTSva5T0mCPAgPVBzPPDSm32q2YpaOqXSVShCwoEFg4E9LQ8UEWSZ+bsw4E7ALyg07n2tkXAGFEz73a6YROrKf/frehFUXWmJp1VfWFNkPMtzO9A76Sw/77De4rJ/LWFSZcVCxoUnBJwEvBOR8Avzc/GWW6jzNT2n+mylaq6+zGFWOUhiFoiqk3UYCgQD4r1r2VaGakVJuzkWvANpM5wKa7OhPzHEbbc1sVuQLPfoLptabvBCMW5EUEj/16+onV6Pp+Bb6kHorTLSU/Ihf1hS/CkUhUf9Xk0l+6B9OFn+/1VgaXHXnEXn2avCOS969HbbeoFf1relc3erZ2IEuZuH3YqW53IYsXyVf/vmCyYt/ZvUqYI7mgDp+v8l1pmfnde8F98aaWR0xlfi/y+NTnZ7KQkPQ4g2qOn6tfF7HG5vo1IpxDrPpYhghXcFX6tsAaBTe+r+YcyHirt41Sa2yoaV1wLU+xDc/0jGTQWEFMbhBk/iJEqAddEaPnAheon6+Wpz+R8qrFwIiGEvQ5IkKyo/EbPjgeWCnjz7pTP3SN4R+o2X7hdyDwjvpLvaryQXrWmm8qYfvGc7K4tQzNym39RtUhcZVuqnzZ/Hpt6GPHqQfBpdIQQ5G53Lqp0M2QVQBZU9zA/UpdukAQE7TE7F6h7xTbBznCWxH0ym7rP7LI/0yGwm8lBnxh4LFzMUJ9r1kcticg+ZPcMFR/qVQ3N8KJrGZytLfokQHBMLcd/TiWmuqVoyg+2VTy98pi2hS4MA6YLtgJZTrvA5pJ0ocJH5XZfQJdXMcFn6MDHqdDdEIRO+niXlh1ftNoX7bvtMPZTWI0P0IpOCMbldZZduUsFrAQdW6b/fXVvKMAyyDnMhm+XL6zw2z12C/pBsh190+yxHyIDu66fZ3XJ1231dAm1HKh9/oc5hyFdHan3X0sI7SYtopHh+1YTyOvlA9B4TugK0OyxpdEZL9Ifa7uIlu7jxpZ1WF0sBmotKlATLsfNIHnnXyw/bg+rilRPwuZ6a+8bNIbPBfDj9ssvYjs4XHe7/ULKVzIteXUSuzdZrP+Nu/OHJ7lid/geSb8fTDqjBnX0AkOp/+khsXu/waSvM/Dy9/0Z/liMLpWJ7C+/5ig6vhp29H0oG7o6uH4W175f7euBqcffoYJmk35vat3BhJ+LlXvnFIoMgb8YqhQT5n3tp8U2F5Pspbrpkt/9xl1URz83LETGPQtfZs9mgGwIfNWn36gl/qvAHR0rK78edDqDCYD2EaEMuefxD9W/afoDruZttUV/TVC8nkTnOwCHX+nZnrU5NLFocYVFem0eHnF1kICPIP3p63dXaFtTkT83ROuocjVEP/wg7e3kmRWn73AnocdcfrUo8Qk16YPy9JKpqEULuyM1Fz0PLY0VlfNSf8JHfsD1+jgf5VG8R1PsEWd3YKn/c0BZUCTMys/syepWzYS8236qe3ix3/Zxq698ch6k4hcCI2YKLTOSvus/lXvYovhc7xlCkjXMOsLJBuWOIG+F+cf8JM0MseOzhQFhW4Q6XU4r7ZpzQzaBpatNGdJ2/xAJdLFs83XEf7POkOp1H4BlW0ooT2lIZR2GvZyGSofuqSh7O7joC7oV4RTTd7Zp+UYfc4SbNcCjSKvkd/8uZ5m0Ce3x+nvDrKvnZuQ9Yqg8dDrLWOgCT0CY2g26YzOfbfWR1IdWOAkBKYf/sgr8tuwbDlpBPEO2C6hlZEKfC5wNtJsw6WS2we/u+1Yu3FFNuxB7jGCToevxZD+PNAAnfr75Cbd0LdHY2clwnydC7qcjAme/T82eb64X0JzZhOdBm52XK9rqG0XbhvZXWY1ktjeO5EKBL732UqWzELijTD64y/f6zgGGbFVZzG4VZUc7NunlAbf2DuPhudIuyqKB4Bb6q0/bw8h2L19FaeOJFlV8Wf5XvuJ76v/DXq90uVt7TCiC8pBYfFiZNj54iL+Z+P6e49Qka6otz54YWZ0uPOeXN/gzoGUBSbWc1UK2nlbsC0D/QJsaVpNquXcwrnciR7ymCVqP7/GcPVa77xHmb1YmZabX6j7z6qSI3E3hcmKts/t9sB3ciPJfWj7OjYlSlC+7X9BFYWDf5GGPEu71NzxxtR6hbrGOeklJ0p3P8rL/q9OGJGPko92lpB2o6J2FSMEl6O2cgOuqCd8mjBYjO4ErzNvn7rsdpxy/2kcazP/8Mbt26d+tlzfWAZn8cDN0X/hbe+kduAw2N1hlzbc7a/pw4Vj6Li0sSrMMMyNEj9eLnQOLIBcpqGfzMvx/KinvGv1yPWhjKoGJkUmm96DjhhazSt2L7KiH1fyyK96C6NPDpg7Ol+sdySWxD3TVPRMV5PNK+M5yxTdyLebnEoKtTMevSWXrR1NDtYnlUh9tK4nBpSdJvN78XRzg86GEM0doMrVjdw7KuP3cW8PHj+HOaWoDViCu1HeEKMR0ZnVcxOHsWr/IQ/MpS6T9zOETGTaiW2uV/YOtV5KfuSYdV1zpy2xsOD9leR0l8tbBVBeh67xgmVQ7avrP8JpqF7kHHmlpV/SN/yArj53uzlOcR+O/oYyRgySNpmUJyLGuFBTdRpWuRs2InAsPrronw4JQM7/owErMGFhJNPPhtq8X3fRW863PaViWJ5blodKFuEIWxcdDdwTQtQ07r04TXZPufMVCXiH9GngTlO7ezVvMsAGWXmgmF2CGshV8wkc9IDaNNrlTpdylS//47kWitAbz4WWsmb8WUi9fa0AZLUwMd1jztVzROb8tSnbOkzhNCgdYU0vqz1HuehjDEL8jMTkH+SPf84P668WAcfGLKW8BHAqyhH5WVX2/f2GA4LMg8i3+2KTqrVln5/NWOvRjt+g3vZFfrD6m/sqdAh7ofPxmBzcA9ooCvtS/4ggpx3Q/tazfmp56ar+L7BHaHF0BZtfsxVmFygBTv7UJmo0Vnrw58TeXvSlYeRBetXyxOP2RJtGFwjwXo3NbNFicbTTdXgR6gF7Km+uV0o15n5rzwD2NNVlZzEVHApCPbUX/LQdMlWZXHVPZjkyQmjWZ+h56uf/x0raazs+Psnvoze7LOX1xkahGmc8+lv3/exdRzS3jt8hNefV3960XLZYnWuN25ze4lk43kO1dDR53N85KOmm/WIvD9xuZmUrlrDXpf/gcf+rF3HTymC2L5G+VjaJPj6oHpd4t9gzLBlyuJcGsn7fdadQtdoEC2Ry9FaLz3AxR22lb0zPIWDTrI9PgbvjeWh2vT8f/Ezq1+lhTHC52+b8HLSu71QpoanfX8LDWtdWXiQaTo8YzNLvMoZk4i/tzLaABZcrn/jm2R39BjZFfG8gf6pQbN4HcUaUOJZz345yA+ZbZDAqs7SqTc/V3V41xHrljDPiBhAOih7WujYFTz+fOmXJfhHc7yyO/+tMTKZhhy0WTxEmuY1tTuRpKfibm0oRFZ9kH2nuPqY3byBDpPwomeFN1XNGB0Rhj2+6rRTUzMlh9JosHtdvPWL/xqBdfc3GgP5A2IoizZyp22qQEGmMQI04tROtef6shyxE+XsHKU78FVv2RNZBbdXPl/SMzR8ijtAAL4jfNLYHrg7bnC5fiOvOYCvAAF2JfbShy3bqfeZxA3nELKrvn97M5VMTz2xn57XLSnaS05XWw512D8xG6OFaSUQUY9PTqH5cIGT0qWC3ZCVKwlwsXah0nKAgc4j1gwHCnoC5+Yp2zpjOdGK+5OjZMGQaorr31Nl8suqs7W7i+nMecYzFlqo6fdr0ZZ4iIDKd2CDzgPntr30Uo03Zn25tll311LqV40+yyvxsddFevr9Hkj8H4Tz5gmZ/h2cFH9LxhHGsEha2S1p+4/dXKd8ssav6VZxZBe045Qj4zd6K4dEE+hFqTnt0mZF4iT0zAhwMx/kwk95ZfZZChQ9X4m9qmPiTcQhX7Y3gljXKRt+DNhbQ/54H5I6iCFdwE2Xb/3fIm307+mFS33j/cNf6CPcCDxC6cd7ABMITgHUqti082tZ3tIyrCa3z0N5Ir8c3rX0vz+uKvXRxnkD3iFT79PXgCPwtrX9yWpqKLoycc/biyoZDve7USFtmkyx6mBWTwHIkZ4HG6zLUbCmeQ7hDwDYRvHQCSDsY8HiLNjbMibRWE5by0LWQkAojkM5x3i+izF6KQKnUvmgqNwiV+7e/+NCtozByRwNmTuc8L6AzFjrTPq5z2QTcUsh8fcfKevMWosuPP/+kryAJ4nj0j7jaj/tQeiDts+6bM3KswXZizagoJkt462SLjzoxpsiYGkctm/Uw+2LxVBTLXGlnjD9n2U4DWvPXhdVvXenrmRgSqHlRW+Od2n17ZdhQYgrgXrYsjDx5JaksM+ZkFoMF2BOQ7Xq3+8WppIoCK3suHLfa+3JoCetW7OmL+6HbKXjX3FXeET8+7FjbYZZbxGTDdoMEFp0Ya61zuWa1T6T1b4skfG87jyaUZU1y5UDGnqWn+qQXoZ2PJ5lxUFcq921SYZVsW5b2DiD/9SAeyC7URCIRH6nxahgH/vL/2sHpIhS2npyEew5jUg2A2mpe0U3lmNO4zX34+jnFyOsBDc5cn1Onbl13OLKz8QSM98oRen9WHheoV9mKct90xdQ5ROOf/n8KpxbxaqrBLa7f8n2L4R9/3/EkV/pYC7d6FgohbyfCzSNldAWCfC39msFNQydsqX9gMGcml+deeMnhfQZRRPLNUqAMtX5d4ZqVldAU9X6XZURUXU73xRGzR+qTzekXWS1HTSfH/zNVB5l8+IWKc8mejMXkAhibxgl5EXzdbDLsKM9DeKOlXzJVzKhHfCeqpbLg7W+L7dU3hpPnylitNtG1bGw/EokJaaxNjQoI/maiTzAmDtAL7Qw2uzM7/EwDpMrnPQXz7+uuv7u3K8jxSZjBhw19xcKHl7SwmZNPO1onkfLhXp6Xbpg56xxnmqCLp3TL5FBUrQ8LPsji2IjOWKdc4vCbuRIF99tzjtSnjv/T/Y4qDoy1KH/LvXXa/DkJKblzHDhxVDH0OZgL2YtWH6cnmdVpTsS8/3XT7Nj8VA6QXwpdnCMa5T/qmTg4yMyqqiIYwaP6REUSLWrWe81cOldHXbIhj9AYobwwrks7n1YlfJQNwV4P3CBj4/dMSAJVbw74Is4+wXZpAwXSbUwRTWSW9Gd6zbZKW++k9J8wuuM1MfXGOV/MVrU2yeZ3trbQKAxjEehsjyLhsH8fh64peVP3H/Z90ezLy1zBo2a/PFWYFDIlt7PA40+8AmnGea1iA1XL2+7kzibtw80c090NtJ/PflasGCXDTBZX6tQJzXkMnPrATi/ynGqvsE+5IFazIjqSR+kEx6+FD18WAa3LUn4/X+jBPkUiJwv3hTTDz4dJkqLtuKasyhWyv9i9X0/mQmT5Rn58tebxc1U8ku74MuYczX+yrZ37ced/nwdzts3WYO5tF7lwmvatL1K5Rp9AzRqXeq6Z03maJ6A6Rn4LdrDnxjZJOrKbsRmFiZr5DywpWdzZcmmabsKYmMjhND2yIZvf/jVio1puf6MMp/pNeeQaJ7ic+mJzyw593dWGGxesdRarr7CxrrH46rFWZrSgydhYQa/UJ9vCuag4oXB9Q9GngX4976FVt9qywR78OlGyXd2Ba67+8pw9dXWkZrGgTl2lpMf3bWN7T52Muwws+QAca7bawzoLeQPDjJhlK2xqsxft/ji2L+I2cTkET76SL7Inopjp8n82iiY2lQgcqbAAQoWbHShOJXH//vCIy8qundx75S/Ul571PlFwQdRf7fTA9daVijIq2xDuvd9jRxJv4Fv4bpm2v+uqdaZZ8/dV8aFdWkyB2MO3GhH+mjGnB8GHV3054nHWvJr4D9frj6M70bZfELv7yglL340gv2+J1rBA4iXRvVYIGMfysMDiQLrNxQnIz0x+7n48BjHf8ZHtA7PgWFAfG1NGWOgw7Wc/jD3mjDXOGDCLW3C/Fd3ha0gj8CGCEqM4RYZPUc34EXm3NsSP1ClM1EQzMNTz2XbAIj1Q6PGyj3jIh6pWaBrmSaxvgSM9hihgHze49xXIunsfORG0ljbj4H8yhoZFMYphLGWznnsHTblaI9rHPuWP8bu6dKzNXHt44sthx/+/FANsoUt7smH+SVyPie6+xsSCVRUgGEeVTAQFZrOptoZhjdhbPG7Eql0kJ9nI6l6t+xAgI9mFWHpqKvhtPZ9P0OcVlNifMWM4xreWvh8AccyKjVOTH29mIbhewMBjNBovus8d6waTIuiyWzkPQ5fP+/lG07LrN+yQZ1+rms6snRRBK+k0vpISRv1gjDQUTEph9BdxVhz/E0Nc9s5zHnrzC4QxTj9qYwnm/joinEScvni5K49VWFAWD6yPxp5qsxJr3TPTOglJLPzPnuwKx6MhKvnF4itoqfyzz36kc45cnwt3wkFc6gNkXzLdPYryQkfv83fl5/ERCsUgmHarg/E26pNhrBlGlbKQotFndE8SGz44RZ7ibGj05ZlHXpGmp8n7nfQdekxIIJi152dTJKgHR2F6TWwje/ni3a0VaJlp68Eq4fINr1X3IEmZFuoP9B9XBB61mRszxTlctCvM+8pXVu0udALHgWe8rR6sZKd7aqx2PxwVs/7+VFqTKidF/KB9pWN4pNw1/+c1/6UJ5r+pxMc3CXT8Ouk/KrRB9aderWtl8wNyVmZJgwe+ymJJrLlMtLaeG6jYh1/7tmIqK/O/Chqva5KhnwF3SZBbVx0sFV27Jbu0yvCSqnIqNLXf4U3yWZnmKLxblF2aDC1BCCdVWYx+BQt/xhtOKlGIqIzDWwjJ5EJBql9KLdEd1+k7E7bcTJIifNs41IwuNplbLEC9sT6KPz6xUmvpYKF5tVopdh7cXottolJNYpx7uC5RuBhuUlb9bmSgdTSTLPEqfXx5YGtaZPbB/6CRikRo+hrRKb7c6Ra0UQkv4Hbv/0w6TnJgBiyVa+zKh827vhyn9qlOYnVV+XiWHgBLuNtiQ2vD/ZZd1EYl75spDfLYhLU6Q+bjIlwmP6kEqHq+UpGT1TCqPP2w7/3tRupjmlLcYjZ/RiqOhN9qcJtF6KMEc7jM9lDQw+ZWA1FxKD1M2zCc25xAeBBKQ/w4ksFstIunuwxIJOZDpCJhp+Dj8Yc05L1HTX0plzAGV55Lx7m2ftp89Q01YB5uNhxF8DaDFAfcFv63DYpn3TDg9AOWY+x/2T26W/QKKUJhw6upywgpOPH3VuZlsqrqo63NqkaWR6cST/E3//2RqeD5LjHF7tLT+X8Uc7Tb2e2Ym1qyr69FZ1ANdhfQG+O6eMazr9rve8QL3xY0JzKPeUp887H0Ll43Qew4qFfxhipDnVK5Y0JekyKxfl5bHnVHePatyFjNWvI6DhbEr7oswKIhvX59b9D82s14XZXGC4IC6XCzp5IC/QaoYXe8666uezvt03DTqejLD/+UnNs7AMJvdZx/KNTcpzOblyPFw4CzlrlyUrPKaQtCPdyKdkTddWe3eP30uszF00SQ9j6nPadGWSJmVXkuVugw4G7SNhzq4GHgEYjFLQuVVKem71UpgMXWSstIHP1rzc/8wHkzh1LHYwkBviV+Ppmfad7xp4Y4ilbYz1/O+gAFrrqWaU5leHX0wTwJdywxjKJPBrVIzf0CaajqwqP/0fWCS46e9qdDqtZp+BDGq18zatUuY/+kR/sjD3CkVg9Agj0mDfdVGZrQX5jU0IQaizNaAtaxp991vyXytqvIK6eBM8v4Ebz6zBr78vzXB7KfEYAAjbtnmK19aOj0o/+il+LDbC7Az3ciEv/Zm0f9ztDZKprmELK9a+FX3e92r78b4CNnb4QYXE2F8o/9ZLgfFP35aaTUVBwsHmP5PZ5NwJI/HZmtWWVpav+JvWxhj9kS+VYg9PtxFxMfmPZOXpOe/AWuFDD17dYpXAoTOEi3585V7fXAf3cfnt5OeinNR/tX4FOBrcfHhJlZ99BFYJAjjnfVyBcyfC1mds0ik9b9gfz6swdllogxA4FkOC4dq5RLSOerp+Es9L01DNNm/vrbWKkHd2EXlOG2YN6Le9ktjHbUq/j7k1czB8Dvnafx6fttBKgGSvaSXjbDckFje5PeO7IubkdsyaJj+UKYJk8NY/69L06Mht0zyX9iF7bqonAAY5i9ztp/HOO+BCkD/3oo7wSzu6WQRGnnN2Ocr+8bvKzxICkJVW7kGzc2Eok8gpozA5LskVbsTjR+eoWP5GC5PwZ0Nn51SyK9BPh79lQsInyvTYA/Q1C/E1gsTkyZBs6bskOuVX6d2x+yXIE0tcY2jz7KgH9oTprSxz6o8lr7nWE9qydvwXOdgbirwfzWOE6Bnoi/8+DxYOKr73c9kQm+L7P/kT+DEcCAXYXbWthF+Yy19oo18NLAqfsDoWhWlsPlXkIWqvyldmxe/IW8IsNUQ2mlkK5qLYBbrkJuSR8Dwh2GzPlInq1hjEBd7UM9R+MvdpE4b24fFiLdWIWJ/Mra21vG+1AT8IYomnOK4HOnuE/v3sFc8NKet0pXsxOfcUstucj74322bNMvK9h5l0gNtzzzWcGPO4C+ysM9bAfKd3c52y5xLpA9XJ6JLasn5Rv6AVV4VSrU+L2yI51xjfKzLuJyOmDXCbfGjdR7rXDY0i93vSitg1s1lG5/Npu1Xwo9Cx6H+ep6JD+XHttrqt0vrn0ltToVA82jNnK8lp1Mc4Hshl7SbO78poV9e74c6s0tTQfWJPfj4s/IF0PItZW6pbsN1ScZ23rJgsTpui8hggVCclppTwXa+lMwC9T3HXJJQuBHvGyX70ib/ttUqKPZUt+t5trzevRX6lcOO7k9umEBiiuNVQF4NgmYT2zSgnaF/0flCnyuuSXSYhYVpdA7/MfY69ZyH6J4680enFMsYTb2UAiomvO3GLVxglNpD/Aq9Klmt2xMpZQv625LrpIKN+y/+vn17kwPyZzfR+9hSXjDuzv2ols2VTwJxHnZz7otzJoW34skiDmcgUR75Tm1gYcFX321+hNdbK1tO/AUrwwJk0qWEyjeXfQRrzxPSutZi5Kt5i9wJviIIV0GIJX07kqk2k6FqeIV8fUX6Rl1ybqIh5lwUW2853s63fUw8Vur16HYjhq+2Z/9Z/VYX+3FaIHSR0oZxNK4PysnpIVxhO3pzpsseBCoTHH0Ok/9SRTH+Z/VhLWEQFjFopmCHKXtmdM9musZrYXs/k15S9CxvuS+2xLG9199xp5R9ThQ7zoeZW62k6nE+ULV1qTmCSZd5Zq8xgw4+KfCzRjh85HL0mAYRroTk8dZTC/GGDu2MRdjAvuJV/btNnF/MfS9ehrKsS2rdURUlDYG2Uq/C2DGjs/vX/PvoWgZ4D16Ab4ecOPY4TqY3TAmmZ0uED+Qj4l+ziCTfY5ZtvZtHtrjBIh19bPL/4QKDeGgTeL9JdZCZo9smjAVxm1N49ZeIcpOtcvcst8cgOG8LLnsHsi6J2t+7SN8pYMqJJqASn6ukNi3xwhSY4N7jZ9C8maLsGXwWgk0/wT/33RtGCH0TnDCp7V5/+GVUGUOvtbO8n3Ixpmb/Y4Kp6um7QyR5NHOekP4nQRqduc/7miNfNM+l1IVrQ4TJ1mM9XSvuvyfsOX+JWRC/gfq3YSj8yw1oXYed8s9oyVtQ5qr0U3n2meg8nbvedKjWtnVnduJt1B9WdcD8iac6Y0T4Xz++Ege2JsQt59sLOmNTTte8OyJqs1zH8/1E/+zH2u/aLpmDnfo+jw6uilZKDlfjsTA34uVS5E9z+ZzN8w1xat2JlzpvRz3+RLG5tP6FxbJ9dm/DJw7n3YA2ebGMTqcLS6OpxpEFVu+HYNLonOn06KDGgPWY+36xcnAvLwd79radeG+TepcY9BH4Jy9Fs8N2PgL1jPrmiDTrfVXELTtz3wYadY1XpwefT94gU9CG3xOlR3fSkLT+RgN8aEaDRQV43Ay0hax2LFmP1NCa9G0tBOlwfILVcX8vlP7jEMWxQxszLvKUUh7QxpXSHWvLleRUai+gQDNqrrC3zFJFXThOF2eZO9PNr6hLx/TeFJQv/9e5yLhtWhXhU4puog1HnSGaovZa57nUCntNVTv8QSSOoJ54wGdCcw4qH3/t1mbQsgvzPOB15cu5aRXL/uMTld+L6AxHKv0XDtmHsj45q3Mcotr6w9yGsP5hgXclEpuoAKP/q5jK1Qxxf+xLocV7ORWCxUptH3eOFdPAlCKVrXwxNTiRGqmEdUUKHG8Zbk+Hm1JsBeUyNH9ivEuMsfEwrviL2uAdE0Kecd95StFAtigcv0TID4OeFwDgW9Tb906E1UNIaKeOKEyOwgH+p8VVUytxc3YBM/4imxjBBFgSFGzSi8KzRYOcQDKgWJApDrKUHKtyLafz9vc91N2Tqi3sgijYc6S71Q8H1RnlRQqj8wDdjsGzT9xaZDDoP6Pq4L+LAFcIiSaiBB/ifjHdzWkLWuqdBrhj5YvURTyWqhGoo63TT+WhkS1A4Svbzw3O7ZvC83NKGpbwff1MKgw2apRaEdYgQUa+aeKayv+RXry+L/e0kZ2YDNaVh7rDc+fhMQu6Otzo6sov3Bks8TA7qQNxC+HDbR6UWxPaF0/HusbNDH1v6nok5t9ZJIN0OEoypaWD5lgPo94YL95zzn8mnjHfDaQN+QpUjRG/sl7FI6j5dUJUQ69DLgX7r6Oz2jx1UCS69z/qMZGe2ag0KlmnobgbcvMna6Op/H0Tpl82HoSdY8nVfd5yZoMsRLF+oB4gGlGM9DmmbOFd2a6gKqKE3eRSxPvb1jdV8z1EMlbaBAGcKz9fi76dEKpzBsejh4BKNvxuw9yBW+cgf+Z+DxzjZPvW5sbFsYwhmWFkW3PDZOnl9+KOMbLtpd5Fwzew6fZ/0kk4fO2fWHnkHu98+3YaCmNVc/ZOwKlUQc5c6RqgrVc4o5lKIv913/QAv8CPfDJofYbWfr1NEVCGj+UiAS4TjHO/FAasuFKlbMhaNWlc/wcm6dKVRaCUYLvQ6m5P2rY1M1cOyk873NeYHxUdUKfscvzatPfJdxaX/OQ9uVaJ32lZiJMfjhmWi/zOsm5iTDEiJRCDlJc7RlJtqYbDcphaRKjMTnYjtYNVYAH1d10dNtewQzjXFGLnacOsHvXvKjGKmybe57EfMuqw07so0KcmwXgtBLQcBdhNEGUG31HY8fi+kTb01hD7183Y6ftPdx//JeRUfq/fjklUF7g2t9VJ+R7wZVtXfmyez7HajtmQrFfoiMX78lucVPoNR59DDKfpmSOS+qlycEwMDT8dtumeeb4lD1POISzRergzi9SOpFBav3uiJ3rQB+qGFCaDdAnn7qWJy74+ZMkoo8uo5fUOlT0de4iMiMJGlLHAjhFksPiW3G5m9r9f3d+eNgJxfPhh0mZ18uFrgrdeQ92xtq8xbY/wax2iWLR+1Hq/DSXylxs/7LMriGg1dePutt9VEDVlpuqJXO6mF7B7tEdmoaiFOLZ9p9xct2Ew+tA7AzDURtcmSKXfaPYVUThdV/fPlLGrTNJ3qjSjemwRBSN3T4Lx7PFR2Arn56V/eP2cMQzPuGyGfKhgnDlxlkkdpao6sjpsG3mSdenTPR+4BT1FY3akvZbXnSZ1ZoHbCMNNy+VOdz/rZVBjWVxbibBjdiTW32AUfOrqf7ZXvTMRO49Pi2t1XOOgYn7y9xLtTGAIwv9Jqk44kZUmxg/hU9pIxAMqaG6TLy4vMq41bEzVfvf6y+1mwlecWXub/KTFDvjXYlolymMloSgueP1NVcsLCpMvkL+5b5f2hWCG6ulQE13sofi/k16pojg75jIuc697qpemv1ay3VLa/YxW9v3obugEn09cpfqxaIaZGo9Z1apsf5Slc6pin0VcDm+SKIpwI1p/Jjms2WPytnJkd5xUmdpFeg6kuHXxWmWB181SQk5SNRsytXZ5xUfYjfOy6MXybSoqgxU8JrTrEwLfd50yKd5uMkveXZWVMpm6ewZ5L4iu1ON6+7f6p0iInvxh+1Z8F4vNSkU5mIfLg+5ZbAuVnvju1GNBY2Cfjkjg8sB4xbUpG06k/YhH2rnSKVDwjUeMsA4S9JtLX5P5LV5z1jYDX2H2yoQN5qB70kTs9S91bBvOAgzN+YWH+tzguvqVUuAHXMa0NkTi+mh0JCS1ppuoo8CJa33ELyd8XvDmVwTqc0PtXMojX+sY0gvPYU8qVP43d1wgNZuLvGgnT8UhNuYC+vzpBk0L7b34jP6kmvp1vCkmGrh8jH9n+u94Q7kG4umPxHQ0AeacHaty9XOTj7XQUlIHuO8PxTBHkbVajdAaPyi9ib3zmsEnfGF7o6XXwuJ/aLs7h276zGMLDlkDQq6f+TaHUUcR7GFpwV1J9qo1PYoM6uDpiSgOJz7FVGQX3y88If7RwQr8TRItcRlEAliu3hz9QRV2k7E71bJ+m9009hiTpD36gOS6v/PhVphakRtf22tunO0dIbyndfRbs/zOUM+42tNUMKxTB4dB1IjNGa8rw0L9Wnlg5gkLpIjDb+HlMdS7G4m+VVRWcPtNoz6LzvPWgVe7FQ+L+MRU2XrqMymddY4S78/y8fbxHV+Fqck9Kr3vYlglLcw3aK7OqhjbSKdnMfLhdBbdjDTpaO+an/wLt7v2UGWCSzuGuT/8NSiKVcugFK5ZFLaBKdE402EJJ1fInt7JgRqwYD5BtUlkLhV/Wb1Ptb3kot8mQRV+vfSsX7DwxydqltrTlmKiAyPv1HZeVVwJsHwEZj70OivAWUf/shcfdyVeZOu+GAr/OcxSzqEu59S4daw786V7kQHiVkWpw1o2+/eRv2Du3o02rXr8w/dLcRsM5sVrewtuDAfxN/dP4EbtlCH+13Qtb1/eSEMDiY/KfPSXy6qZ+pNmnSEmpcfgKzI/sbjj8puHtofJX9rr98Ss9Z8PCgOrDTGjTNxW4LyWpb90zqzAuB7bA7GFpoRRuzVyWe5mwj8tVFhmITdP9HgVyK/8hhF8oO++sR2J0HvKiP4Jmzaver1pRJZitl2npPzItCRzZpMNxXpyw9PL7xTtdyophh5fPjSVyyeZG62v8g55vlZCi80xql7jYY7E1esA9q5HdsPieLfad9/1hlLM7T+5jtj9HsYdwVzzqou623HhrUt1Y9OXtcjLP9F78d9HyQeVuSqPD1tPR3crluvmPQ+83C+Qy6AfgqatEd9sJEX8nJjZc0Jd+lfEokW/3QFM+IPh+sLExX+cotmwQL4OZA0N1cO+HHLZ0VeFM2vpheIPOIEHB9dbrIbycna+0TyZ7WB2kFbYZ+nV7FPKqQUcGs0L/pmMakFZX3uPicPczu2lbYPivsijkDza0/TjQ134nNv2T2LAMPLq7Gs+YxNDIywc1NLTT1hQfNMFlJqCjpdJsQRnHFHGGSvj3dME7vMm9W51/nvZ6ge7RyOapw9Ha43rYg6TNBRrgnExF/NY/78mqqYeNksF0G4dGH5u+75AWSRVdLdiSaLwIP1zM5v4nw1i3gIJnoCY/dg1dIePGkWILdllyFm95RjaTMfqy8VF1Mkp/xH2fMJ63sQ8vXKFjkPU/moofSUJ3ttMdYwaWA/1ItZFhOFfQtt2oN+W52vsv4PGCfjj1Zi4soDpuA/bySkHG8tvs8PSNd080gaizPZMYB/ggcnkZxUj8dnJGzi7eI/E4RsfoW662ev/DOYZEWg7+VfXsb+Hr1O+ypayxjlqv9a3kHC+C1xmWQ3rXLc/2Nf135ccdpWbBk8wAhDujIxzVAIOaWX9SkHnU7Q+Mlkj3laofwOv0h9boPX9ME2PHUPG0NGvXro5qhdJTE1ohZ6oPHIkqBb1/p/HTdUdLWf1PsWNugSVWaqz06YoB7vAPS7oJroeejxvPeFwQ3W1Hcn1/4YdVSCgA3i1EvXiv+vqZMEi+x3kFfzDVAx5vifZnnIB3xKW2uRW6WSZW5I0CNOkntYH6ZhkuSjePOme9opalIUeM0uj9bXRgIwgEuvS/oKi9xwcC1D9Gt3J/EmUvZeDbtpOSHPD0BWx/3evt7xWtCUx7uLwjke9DDvg65M3lj65jbf5Ax1b5wOigyDOvVNbaX0ZbHbe+ST9KaeKgT66eanyvQJpDUPSTvpL4Tq6S2fanUGGcAezbWugrubrooHDIxGSO7Bvu+0Ju9yRw7TTbQ+bvOb+ALKvUd2C6+J5ikVsR/Npl/hUdl0cHqTk3QDGYnyfPzxcwF5QgkJ8KpphNv1x0T/3Q9Fkxsz5G1+AUaRN2GhywjgJxu+sBDaS4icVGOBR9KiffTM+P/LYN/DdgGWmNCvdpLwJ/DQAW7A1l4A805D9qOQq9Nm6bDsfq1G7Up+Kk8nHilk47A4jWd7q9nXftwuiveBzFUj+aSXK4UqZLve9NpZRgjHd8vfStWW4VetSBTUCkyjEwH/OXw+g1C3EY2KTmAcvdV0layOw96Z2bT/0DPDw7WxuyDfbV2OPTRwP/TQNbB32VYmoaXlvwjF2KwDb1TPxO9LXHHDTCS+wb6+BTp95obN+sbOuidK62xd2S2f/qz2fMLPSLuyee+X69dvBgoDhHZ4957AnRjKv1gN73pfx3aHOdLChwwSg+f3wuBXMHR1HX5WkUlj4CSVj7qBySJQp2JbWLi0P1m6Mtn7ymq9EWuRu5vMWb9SyO4Tr9B/3ljm3Al9+uAmQFSZMRB5LVTcHKWY3UnAnCCilTmVxu5jeIJrrF5GIp7iuqNAfeZ42NB+oGwtX3DmXixqaSfE6GYQs1+jrll7hbEqNUNXwA5dU+Xiszyf9Br/ftsmI0WQLvOFTXePIbc6rIgQoCD+8ydlO6IZ0NrjGOg2/tn2FOm1nGxH0u06DLkRau/7EJzlXJSVnmWCbzbztapEjpe3/jPSVTYz191C733+UYKuhyHYGRZ1DPfyUC5dZLLq9fzJ/sp653/MOCLTj7/rp0dmsbKs9waU+cu5Xbk6Yp+F6FabWkbESqdaOj8vwjah976ELUCFpVEtnKbfmtjMhnfreRzJzK9NTim3urjfWp1bVGkSVUk7TdHMvFn6VnK76cozcrt5vy/WSHF/hhZvLwN0tpn3fHbsrtWjfVVtidNd8VPeV/ruSdxfqedu6/cfBRc/cs40U5Dz6uTth19tzrRDZK7ECkfl+DKsxN7NxxkzQRxxz0XnAPoW1Ob9urEx9GjBHgveUhfcVA7CnID68Hmn+ZJUZL9kYhqy6xEnEK0As3UNMcMm+NCmVbtfLir3HsHuOj3rLcoeAUzmt5LI7BD8/Qw/0jV04w7ytPvXC1Xp6O0yTGt/386tKVPeDoRnpQDm4/U4pxjcb66GNMex9NOkxoHVEfW+l5uXgy3Ka9U+eBtVZtAcp5w+YaO+BUvp7c+gkN43bM2u+rFW4jWjytoqVHoYrYL3kvK4eTlmPfJ1ohZzJXOJ2+GmqiR7y947kLN6tA/vRZxW9mPXDAkt9PYVFU2Y5h9htIbobHjvGsr765vwWFdc7vU5Ys5/ytVd8229q9mlRvYAf8+VtB8cpXP+urF1Mc3tEDebHbG2H54Ebxkwt9ZBWuMD8o970uwCtdQS8f1ORgWDdaKzgr7tswVXzHlZ8B+A3iScPBUabpszeYzy+Y0twOmsW8ifwLkEfzLb69Uwm9rkII7OnMOhuz+2wO0dxD+stB5GlA4ppkP8RJKWAvwzMWbSw8mPlYvr5865eXGx6X4Gt8AVoqschO8zcPgb7E67vEndo5w+sS1GG+ECsvUTK3YBlqtNNRjs9Q8EiQdLPQlU6jAVoB3rzNxdvit6BG0DnQwj0Lrv2mQ5Ilu27d2hGtYsjdTu/BNS06Bn9V/4QsJtxx+W5iDZ7RnEj8ojruDzIIZA4uKsoOT910N63TlfYCJHzRxMApGB50kfZBjFTIkLeI5fGFhvfdcOW0bzPthOc5TM3uPhhyrJaUSQgp+ObUXurtBio6TK8ycUevNtDP2kmuWRq7KcEdJ4LfsXXkWrzQriTVvU/JkxM4emVPUmkFlMqgxGIxEHbnYv778/17k3JwlRAyPny6Q0jeejcp+OAf/J+w8XghNlgUucXzcoWfJPUEc6XN6g57cFnfnFDdxP0POk0hnXjbtsJ3Cke4O/TgR/+IXeSGSRIzjttOrPlGvmO2hvd/yaF/5QpRC23qTqj9H9iG+nmbqa+FBAxsxvuR/+g+zw51dBjFk3d+xJ2PEy4drbO1xZ4XZ+57PzjapURAcQwkAWNIJlh1hjsicwGQz7mu+dTrk5BV+U4ItYXVf6QK3gq+36bG2uomzdYi+emeJku6qUrg16lqbr4xKIy6JaCpHhu0OLDraJkS/wTacDQxzqU8Fa0XTAa3/Pc0Yl5omnphmUJVwAKi9YCCTT2XigKFN1az7YZ+TjJFy27b1d7P4CxSnxJafSK3bvmaUE1cSj4TLZY0XxvBvJFLuajjgef4v1BqGvrJ+vonH7cNR9eC8HLC/OdzANi0pdl7txI+5lyjDyiLKf+MmK8Cd/od9TuOwVRtmiNlPXvO3RIjQH3FXtQ59rFPydxEyahqQzQi3uyPN1yrPp988ju+pVD5jX4TB3UQ/OwbZFf/w2iq/oDFb/21RhKzWbDv87g9n3oSbtfwNf/jDRMrHgxdLuYLkCRY2t9f2w0Gd2uHZ7gnH/7Blj05vO9JHlDFJ0Xhx3wCbg1qAPbPrghyngUEnVVzMGdtRdIl+igwL1uNoFuPwQMviWmjtefiTKLrha0bI1lLzGXeuE6xRtg3R50WnW38t3QWoVQkVVbXujIDU66/pVkzN4VfZbPOYLsrBNTO7PJ1VGj6o29vrrZbHZvm+DOkt2ZijVIvPXf6DvNYLQ/tcLntryAeM6B3CN2ThJDdbynK/tpQUwxVIOe8coCS2U/mhbqP0cwfFkNB9eZZ+UizN+3y6pdLQdnnapSsO4xsb5Y/rWPUZRhYf9HgHKAWq8OGp0wbeiGxw1zNOCCR8XaXR+zzMqSKlrxrohzfWh7U3dF/0mAElNBDfZaFj5J5Nk46atPIAei7/E+fut95eZIbZLxSsbY8sK9uLza57MhptSum6fTpWYn8GvavgMJJ9U2BQLkL7v+19CqNnKr6hhKjHf76vNlJerkW88/x+doIp3BqtfyLvjH5X2Wrrw6IkIblKfGpf981V/QUs9fNekOMrT6+p8IdcNP/Fez9JCV5hYzTvwhCVAOM9ifoN9n8pT+KURHAPYAv9PpgffN8aft9bXgVbBUnGxHudX7zcEzAcReI7P87F9dlPtVE7Zc64c/fKgRcK2WsX49lHhfgxpyerc3RTj/silthGNi63aAMtOLqZBToN1OQFL3UjGPNlzgMvufC2t83JMNvzSc92h+qg4p9h+0OQQm5VlBbcl+cShksjdLQrfe2M7VL1EYnQO2uc8prpUQRWgSdSzp9XCvTZ2lG03WZmTQ/x8pkqnd3oognzpkpsX8t37zztkFr+2SIA4tB+bbbdcRGxtgrfXVa3bVo5FVAeIpHNKGMBbbc3QmH876/q1K0bOk7HR9rz9lQAaG8fuerbjP5qsXT9BqSjeR31BpqiMup8YMsbSq5AdmpUuvd2hiUT07n4Y80lc/cC1yRMj7kp2E1xUlt6pmlkuT9bopAF2SHIy2s41VvlPwoMPNOzVjYMRK407AV0+rMrGLvGh4y8N19PElX3iriaLMGBxOfkrzxA2UZ1N6VkR3Hyd5jvxUyVp6rfxXdPAF12aiOpB8IZUzf0ou3wmNun4LI+WlLsRZnoi7GeLlFz6B2NOA9cW2s3x97MHib8DGrSQV9ok3KsgFtPMPKtSXq5gQuGgDiASo4bpzfmhoyqB6BEdYKhHUcKrXhts+rwI3n2/iurXQfnG8lozhcmpr+YgF7AyfRz3JNP2jiE86BesnYF0GPKDrW0yJyndjl3MGnJKwZ3Gtz1SLHr3njJ0PK1y6NPLQPrk1/IpvVdhWMca/pt77dno/IHhh0ChFMWeIS6dj4V9Ez4xdsl7UVX5t4U9rvPLlvPB/nS5QluWDcavC/v3mLCUHDMcrnCBapt7xzAOgPMR8j051JbNJqXtG6YihihCa+UW7qr+5JHluyLmGPvseHSUrm35P6nNwTGFAo9BKEcfTq3Lj82QRTHgV1RXBPnqP11sdJrA+wiuJfNXf7gYL56DC9KdFncmJQe6pXOquG70t9Wj47j0KgOedWMQnO0mGG+tjZGrvL8PiytgMedw21BVB2Z7hvAW6M7nofUdLYhPV4vylM3qZu0sRcoI26djcdNJ87OhhSyFC/PZCtDDFIKxkrnxVyryWmdTZcBc604Lzuxd48/ZtwpjOxNXkI93Pdp22hekqKj4H0tdwHsb52tcd547DBJYVjSPhggc+66z8n//WLv+FefvtTs+3rLTlTn8onQ2XbnNpCYfSWGapxC0ZfYdt4OJSY7FOA/NjzI9hrR13MIrUN6EIXWwE34dAVzu535h962ujTdU5rqyLZn0XfcTiMk/BeNxl+YQjJ9TDliDWgpsKZQvLbt2q2B/2reqsuRyCgHJ60Hn4z21xJ/oQM0+qVzMRO/01+ofY2Bmt7S9+s39ll4KMD2I4OQ4T285C9852s2qc29oscxyLxFvuj0HDj5VohIv2B/J/wJ4bTkF/EbeLCq8r2JNJTEVGQ304FOdEVl3kwlVL8lrlfL2QHHVQub0XAQ8yPfgcEf3lniTD+wzPxK+P3XPNHauG6b/6xRP9m90rddiHZFYM2t1Y+mFP0/mZO+PnK5/ukvMP6zrxq+oqR++A/ME+oLI57/FO5+YRAbwS1i0+gD+tK+o6SZlyM2quDZTawXubs30/CvCKh/Nmeox3NKp3QJ0v/ky4iEPlNdSNUpksLTDWHiXWb/S0onxRJxV+K23Jx7NJaqrxzDXMI0W7eaXwkexDYOC7QWOuWrQDl04tSS5ClzGcp099Sl2lzZ1+ScwvgyFgYXZm3ZC6uYwyFLh0C2Vd92rimcv9JpLUzyyFvZ/RNE+BaQr8kJj+cSB0OuUEQYHT4i6+0xaD+qvX8kn9vHDyOsYir0ld8jZT8K0WuafUQzYkmdA0jwvSu5qJWVF5bh/N/p36dcxWCm+/iS4CZEHZl+0tuKo2X+G4r0uXlC6Xu55vvFf737duuTHHOipYBf9aOmafsVL1L53wkO8VriXXOQFeazBI/bffMVvYPXLvUji9yGFpORSjbmQtd9ubKj3y3LXTxZ7rJdQrZHfZ53a+3K2qpcS55EBp8/cOf2uhgUbhhimVGHUtzX5BjLJ+MOQIrlo2UGRWPZi6vw/ynui77igVroBR++K0tfXp2r6okXxmtfPNuA2rYwTdZ9bYYWwB0jkB1db1pv5fna0EytHYVeBT96Kvtndy/9fTWfpg7mn6avMh9u5eUKRsIZpCd7g/exf400O9io9vD2ZVq42C7REKhO3ipp1Zc3+CAgZ8nxQZ2jFuY7+yHzCCdgzS87tzphwde/tOSinezD+JeXuL/M+M+m4jXpkImqLGKVGO/ooOlGxHj7qc+bb+5IkjGzjJmO/wyssBYx2lDcy2jO5hrIX3ZtcD3aTer7m+QxwcrpQGC4tPhfdOee/VY+yugUcouJqVFqEEPpjtgafK67RfO9qLaPnu3UDjphTJPB+cO9EYiosMP+uxniHNVl3/6V41zPOMD1DMhf18nG43NYuU3tmdX3fuwqelGgGSrrG8K5rjHNPNA9JEmVtaO9OMn2mhWn5QK/QO/tRWzurVUrnXXNHzYCP44eGxlesLuWzxdm2F0v2zXWhhndnzJEG3AJ5jyY4QTa22p0zKAN/MM1CcC7VKVdGE+aJBre/Y/RetCbtkwp5pVAUjRXVMrZfMfMeEZ/3LLf3/WYFEBLux2UZt/H/h00/0dnFURKmVwB6oKFnT8/qz6elWG0Tx+EATLXxMB1rf7IzJstWC4IlqOLeOLqEpvdvKX9/Ht+jQ+NfyBfOqnhSabUxkcQK80UCC93QwbxvqBP/Rn/NHbQigtfgwcDWd54r1RILI2uQHedM186fQG0bd7/HTMeh2y7ntRXZ256HO/UVS2hXTRjI/KbhR7nGjJZCA7prPy0z/HBYLeqz/RP/gAnBou4ROV838LvmuASrU5+5WcP/8hS9zpNRuKeA3KEJhvnimvMSBpngHOBtv/WDV/qBxr4dr+/5O5z9tVkxpiRsAOaFVL6no8r5fTY5jnE+/aZzpyr4F/37oY8mshrHfzeXKGvHb5YiFirV5OYUrwdmegZHhN5mKIHdGQRgr/+nF6pGPrZmZeI45GbjXd4JCzXEL1rfbG3FOF2Mu3npQNX/7fYvMNdK/GquByIg2LXy4szBZvysf3Lv88gNvbj9U9ORC61JFfx54bnf/59+JBQnGg7g6UdiW1OX8g6ADMjSfXHwTXy9nSbw0hOff7X9sr7geDvq23zGnhT97fPrlx9Dv2YoUnTKwirH5bd/k71cj6+zlZ1j/UBtCEDrxeL6igo73+BjDCtAdZdhhX2zrjduR3b1lfdZ4l+tbNcXqUzfMSuWIKoIQH8Z7pJa746vKarr2GW78IzziZRprLOUXdjMwkifMGWS1Jo6jJY8/R6Y2HoFBbvc5egmrmDGDWtmAADbrVOFeMyS/x+CRixZmyIciGNI87IqUNOitrpzYtXhRlXT/tOkHeIbnBN0ROdjw6pxRyNPJ6p9p7QtNHg5iHFzP+/nK4L8EBhYXmNXrS1bgc+Y7kaHW+Lw91orcm3mi36PH94Gpb5eurny2dvqGEjemWypvuGHssvRgXM5b1njO+bde9S7o58Wo0CpQUJDkG2Izh0XCbsDJ+wUTPfQgLi94XIRPQf5GdJ5eIGBhIYmiLu/lNnkUmMp6S46UD24DszicanLXq2S3xxar+0DqBeJ+R+6c5kUpMvmxCPbSlVB7rVaoaJrSkmkM+2CJtNeFfPHP32Mweb9f2S6u+rNjkVLv3lSgl/MQgLx00j8CoAfSLJNxFYbR+wNFT0pB8CtuHKcIUKSIP3zP/vfqj+iTNQEmIrlS3IyTWtuB7qAcEkfbaepdpod06DK9ZpaoPbXH/A047thUdsk1XpOY99Z6p2qqYTmvBosC6DjOFNcDGfZ9P2N5NaNBvabMYDzOpoiVZtI1rpUD7A8SQqbdcaVWVZEkHKVlY+VWhTT3uvd8ZsIS4WWjqsAkbytltjMyNtUZ8iZr7U4k3Ij9jjt88q/lN7LyOmCnEg5GaDAmkmvd2hPb2f0s1fNZyEq/2izAwtuAYOBjIjbilB7yRh41vKs8+8VALNMsUE7Jch33YjdOzITvW4V/cG04a3waJ5npP2NCXpcOLWxrvP23SAmGPvVGdiO8FN98laMEqso3vGba586LSdKfCV1nLHDJmVtmuYPOk1bh9p0oV5LCYDZwDMN1pOtcG7S6J6mgyLhE3vpNUu1c+gtf+mO3ShVxd4skeqqpr5/uYZ0O/A6zm0Xi+kzsaf8ds+ecmKf0FWyG4a/tD/cRWD1zZdJ+fnPGgbzoBdeNQx5FR2+aLdBrrq73AnNM8C98fVxswx4SIc1lXSkWaQd/SbZQKsIAF/0NT6iQKtCUToHGVrRir6FlqjuL9p68aXDted3/PbnMcPXn1SSj6CO1LyHqu47DPTjY0b4+4bZ1l2r+MwetuGS/Jz14Hh/S0cXXVF33L1Kx6rKf1RJCcX7DyIho9NQ+Oouuv/KBm/ZiBiPjYqtp41AZ6t051ru1kZ8vBGeyxOdqP5sT751WVOl+lhOn8Bbmod2TOmiP8UMfHNrNEhNY6RG21UCTRVlzJApg+KFLcarCboIwKyfCvMF5TzCLrtGEv4SXIARUaz1AammXKv/27RzlZ95eLnpdfrKhmMG5xNZu9xcMPpB3x81LIevB39aLkKtp1zIvuFGNcen7XIvNJjqK2z5R0MUNX8XtZmK5/2+HSLA2syYqxajeIjxizf3lPl4qtXCeMJn3RO2x9gZ4C/93tE0He8MZNPL3gG+VJNr0zVH7VxlDofrp6DDRCKTnQ+qDJpYd0MiWQiVVz5RUfCI6+83j9nfnZvn+H/nW640k/pzJ72JNKZvMorIfvnnZfWJ4eKR78Hr8pyAJFyZ2Y1a7IBlGvyz7TX1Vvpp7Gc3vNaq8jsyuTpn0FVjrQf3kjcpuOqQOGaSSY6q0mVc2C1fDnO0vNUsfxy3CJa0g58/XHfJ6Lj/wN1vuRnZx1X47ytuzfOyKznhlKwO90hCV2fhf40Jk+4+YvFfPb/hYzZG5kp0lH69QCDkYV+59u2jVPoelSzkn4c236VNpf2s9M3ouk6bjx41T4s2gz3+b9FXgkcK49jnbv3tbzAwaXzOgoUcu4ltPY87AeKj90uCeFT9SSc2rDW2KvGD6+K9YE13ewZDF/2o4/V6b7cjg/x99PXSeHG792+3NRZRFLp8FH75W7I66QHSHDfskLkqPwxqDJE5qxbbZGwVtcw8pmwGjx+MmvEawGXIbdm0wzII4bCXt6BAVQ6egcZWfu/GfYlxXGjuFF/yGiWbxLM1ur+Q4zwFqA7WuIZO+J4ldriGuhhJZYFR4OqueL13UpzKQ2UVD+fD3KHWrWxyHL/M3mh6/+HhysQnU9S06sdjsuWmnUuUZH254yxe5P6RHulEwX7BOqJmU1pVSDQafJrL/VwC0aJ6nsCW9Zc3Z8VsmYNuFab4bvxVfB5k1A00PB9XkZeBZkPilLKb66ukWu+zcr8cwLEtD91W3p5sECFktj9mCTXlnvO2DCn6YDYCvxsM8myPpeodFwjcl9vvm3esHOznrvVUoW3284v1EfDSn1aD+rLaTRA2KtrvbksQTCOrawPNla4ZN80ZcHs3C8Xic4WvMpRWUncixtpgGyD+RD/3IirxYNvP5KWiHh2NS2nCtEha5/7htg5Bnxp9GCGldh++CNUsWkuwCBasDv2qvCfvRYr6niLRNGJTV4Jfbl+pWT6dNyFIZe/20FGYN56DC8iDN8709QTsMaq1uxXPTcqvrOklJCBcbW2MlneWq/+YcRxTg+PO5xDBDQiUd2mblH8e9WM93WPzooz7WdTINn3YDidk57+lotdLZnPR1sUNe/JFDRXYgvGZiCwJyWAHhQy3UL0RZs7DgJloV3xR/I9cEka19epTCWVGRp6eiX5BLXDZ5t9doI1iV2bsz1eZo0e9sIb6iJcfOw0HWY2fp/Ucm26ZZlL/QJ/a4zdMvi/H+Vyr8hvNd99hVzzGTJdYGl61p9x4rbuGF9syNiojChE6t+8Xv+B/hpV538e1pN8AVRTyetfMXonuz+32GK/L1nTRJOxmnM1FWsQuKVRykhDHICWWtKnMwJU/mFZ3OLr2o7Du2P7mkgs9Uo8TyRVHyXrH4+s1u1gMfHi8pI1yRGmeI2/TeKZ3zM0/656wsLlSE5BUN5Sbk803W+nrx75L8n+Gh15nr0YV4JSw5IHTWfK/CR/9rAa6dtfV6GT38oyz7NBsbR4xRzVr/hsl/hmivzlD3GVd2sONSW0XcYqF59ODOqNwkjJTKJah3yJ4dn9ulFwYYCVmjaybNydsDWV2bWm5l1w6JdazqodFJ/pF6LBGa26OQ90hqXnnLz+Pb3LunTxX9U9ZcrkiZeacjMSsnmVMT1Z9dKb5eofpMbKFsq6i04fWnPWirWL7SSgxtuGIkWV3ra/rIr83uZepUQqDMtCjwYHp34T7+rr+HiOS5EPV558LWf6dGouAB63hzxIEsUgmyPSwHmQRzNQcTg3eAP65zG5E4Rebff9r4eyUAfWrgXazSY9+ugrmU9vLm/7CX3/upxTYfu9fVTZx/SrxArtGpbBQN7X/Lc9b1UQCdPxvDVm6dwEU+cixOTK0/5K5A21kedM0fN+2z8OKF30RobupHA5p0Ctnr5//+TZYVodV4dLUIdVCzKs/48+Y7w59YjOOo+nk4hkrQ80BwvYKj268fERhUWQilY+PovOTYdpD1vMdBqu/Tr+6n/OvpSfzYCP1bJxL1KfD2tuAPZGnuIVcoYcJXEpYHKL6ecMwbXuSQAm35bw9qsIvbCsD9DDQx+z3o4D1Z13jqOid/pFEACvBpvcnfDlvjIjj09qfy7AXOgZdm+L2mWT4Atz5A6v5raNPu9LvG/a1+EeUvdKKXo8naK3gMmkq1an8ZYVPVFwDe07EHjv9W11GTzsaucXd0S7LlfAxtYf6j7nrblwfAkd9YI4G5BCvYd1U/WOYEMGnwKD0piLNTtROswjqVskqNUEQS18nqLuBTRtPjrxxOjTt9YqRDsusODfvUDDqt9T4jrk8n+BfqA/8yR8eToHf5k92OCSZ9SxuSSVpieYxbtwvWJEVDX7efV/6vAl3+lEpOMQ6Gv9+XYuQ4oC2ioaiscKQP31P0QKFPZs/S6vK+PXXrodMS4qNsNkPDf9WZZSHbxR89zKv97pUESB5Q7wo7miqIY/8o1Yhtgd/ig3t/pODTbqVJprtd256Q6EeUK6h6WTKays9ebSdsWW70lFbcv3c92rGDukT4ScjELfqupt/aKiiLBNnxKD+r/irppBPBUNMzwqmyCZTX0s6eDiH5hGGUJ7+tav+jppr1XiQAJ4Tcrq89cuKLpp0MI4kB+2T3PpjtKP8ci6PeRVRzQJGrAib36IDRGdaSfNoO8n9qAa4o7z+Je11dVbZTEKm0sORDri/H/n6WNwez2sX2urIywEtW0MWNtsiDbpN/fwMHRUUUK8rtHUAQNk/x7XKJqXfpH9uf/pXJD7xj8NyK8JI2/jYB/9syvr80qUltVKMILf+bcfQDA8fi5wQ3X4W2rNHlCpTkI5XIz4fCJ9ABPZdeu1xbmnZtaZi6OFgo5iVKKijz+FbCVtwwkcygwY0l1VBxP6GrL1j7oPsfcrJVUBJMU0TUhXy1l21PL456sYVk2E2kt4/yxi6kY28y3p8/CzmSf/9vO52e9N+P62M99JZin4EuN2ZZcqOvlaCu5d85czK7f+iIIcvB+zDva4vN9AmQc6P+mF7NNB2n0/4jYR9QS2oP0KOW8iynQ4aSsiulK2KHZ+NBzN6ib1CI0R4aED08xlA89yM30NV0H6l1kFx1dS+tWk59y46GmqLfX2mE9eSMSyq5FsOZQ87S7Ox6E2/R2ogvcdc8CnT6ABz1T+b/IZrtLOLvlcooxukLyMN5mKRTA+TNv0EbAfWdKtrwd9+1PqOe9qfr4rimozlLnENldQ+CmfFCmBcV45WzO3k+ZHK6qZ3YXQalmcNjhO8+33sdqkzkKEBz+hCw+DYEcShyopZp7loIoIRTAMIWdiHeJEFP3XTT5aAPtHAIVEBpRbne2NWRWIpNQ4X+5Cv45+NMP4D3W/X4toGqXHJ5VtgumtWe+zdrnl2R6D2N69ks6Z41XXA+O2zvjnAg7BleyaUdALJ9/G2G8nBY1Ol8mRV8MMuYlz4lY86gbcQD75MlwM0JJQ8hxwRarxLaB+2e6zqQ/v/MW5ZpgPH56Ymr8LGw3d7x2tfxuYt9M55KdIWl+BvKri0QWWE3YvpZmvumtt97TvYNEc1Bz7C/jNLl/Dy/wTfwJVK1c3m7ZRjrWtISPvJKsP9CV10mmwU2zqQcNJBHXr0nkgxKMYtFbcW/7MrLr05NVCOSb+OqmEtC77HUM1juP6ynwwMizu67kvCpW0406JbW95vsUW7L2sdPsj4udfxmOs2eh82okBXphb3WsQc1a8uGFP7b1dh8a964VnrwulN/eX7VUJcBsEJY3mG/4llLtanflzENolNVW5MdV6jeFU8E3f/wMX6VElhMiV7ey9eS41YLNshXbRODfxFIGYs2zjWoe7A/HXMyUr3Di6ecmMbfGrY3aw1SvkUDetJRHnUy0Z94twyNYfLe0xX/et9+hQjg/2VwdtBEti57Cwg4TVbNlbb/fYVIAIir7RsvlPj9U3s1sTho4Xckkr+2oSr1REvNfrCFxHexjLFC3ruWZstzPzeWpCanXzmOkzYyvFc28Nuz13WFq7Ps/x6oLwupeEkatoqQitfKDdpkqyyG1MfCX5l7VPvwF7UtyEiVVsAViaN1+OSzF3EHOdbPUpC23zEu9tFSQKw5sOQT7SDlGdxBKGJ7OTeDrlnl797o4BuOY2RlLE7THgVGjLXhQbgKcS7B9LeHvpAHwHS/qa8S7uiis1yGYw46Y02GBo6w0NOn1V4Xd5w/wBGia7MQB2Kr6WbHyr2RQq6W94KuEwfzhdP57Vb1DEcIYKCNDFLzWci9mgSTrsXybj6n9Kw7dcF9UM+aCfrKqbrpOJIJh8cCQYvg0RDEj6hCdJiDxpcWXeI1pXblFufFFUjQ+H7CrD/W9bu/5CLMW4zShyptLm//WicnSjx6xxFVwtW6LplF14k0ZcpVf24q+m2oMxCtRj5lxhC34DeVBznJRFhz5ZO2mDEb24eVArtsyYIjFUwmDh1h/uf7fHQYG3bJKp2p9Ib77xnhue3wvC0vW2/9/EahZvlXK3Y/2o+y8H7pOARgcLor13W1nlQHxH45OtP9rwtZPT5BuMv3AJzm1uTUnuE/4nhW/r3U1sf3efdIpQ1KbC7N778wRFBcm+H/5ozwHPgz1NqUvZaqW1ydAZqzg8qJeVdQkRWvxBbNu4TQUQL8PnJsu6LF92gczUAKiQEgfX+IVi12kNe/cZKo0bI4hXtS8z1eDwtmPMbslyrrPs94eGgIrp91IExo8t3jWejlIXKVcpdbG6wnZMS1QdnDt0j0ogPspm90501RLKa4dfK9bj5BVEsct7RY/T/CWOAw5tg7MRpcT3uIi7fnox8Vo1OZzQ2IaTODgBbtifG6MKnK9aftFSAsvU7wqg/Y0d7nSedzGr67zuV9VvNj5NYRhSG45M4fPZeZX41vPHSi2vHNX1D7anOFVlMe2xvuvz2/Q1FNwDF4SjCcF4orNj6tvFU924P9lJGP3wmg97d0T3SbhqmOSIm7un9mTm9VhszZtCwLZdcQDgTGufRnlHQaDQ+n5P3PxGpXlTtWpZvYZgPvzGgXZLH/nqlBpMauRF+IpucFuDR7vvxk6VLn242Xy3ONeJesyWdXdEH59RfkGNjsxYWv9iOyLbqTuF+aboWpyv6vJm0DqLndMiOYT7sPbvXh4d+J98AZmBSGGllIaFMoS0VXvW7qfzCpVVUjgEbQrTtkc2FL9YaFVPfx/ltFUy3XcyXj+wOg0PTu1u4xvzxKw1coGOtKvtMbVZku2vejvqXwfBUembQKpWWc7h16dNXb5ztLfOSEbqWgQ9/9ktwzjAX/U0l98HbE6bE+0u2gzmFYWIt147qoMIpxNzA0UI/cvKFJkPBzn9e2uSizVv0H+LopQs5XPwF+AWxdPg+FI0rd0wZ8JHwpFT6XvBcudoiOvxKk39Jx+craSLtz8xtatTrq61b6E0i+MJJwXDrnL6O/Hl7b9CbR+022ZjoYfJsZaiKh4CMyEJn8r7PAZm4BReMX39/n10YhdoVSIyYznraIH+ZJrfFbtb+JDme8g/xundTqE75da+mM+Z4aJq2rfGvD+q/51IDuq4yU4AEHErN7QkZbqG5oupx6AN+GcgIOrOcJfc2uagRlun/myr9s7zdMCCnKzO0Sgu0+Zaq82jk/w+C9/KW+w/MLO5K+9a2iMq2kC2ozZH7r/rfeOTZTxvgzM7c9u1B/zWOMwBYrG+y73op7PAzSWhVtSFXfuglRi+doLqkIOB8/tJTbDTjLKkIOmoL4tbT6DsAoJ5hdfrThYNcTHLta9scNHNCr2v8TgFbLGthrNAnWkmBUszGf9Xk79oCHu63fZDaTubGbv4PvMRdSQ/fggqLoUp6XfI07V9I3avBUqLKNQ3n2k/APnYsCOlTd7VU69HOxip3jTSnBZg8snA0cUb96RGBnVKksL4G57pPuPkhxbcYbGWGLlEtSK8+SGENFOWu7lJ+4mrqijFl6bm+hJwPEEradtYywUdpa/tKdChQrA7F0SJir06urco05cl95jBljJicXYZw+FjzYjQvxRohYa4BzONABLhhqyCDTmtGlTe/ISlf4fFNo398ZojpskllJQ3rV0t97/3xU7SpUKVNquuanyUCbNnXVit90yQFwMwbYS1U8OL1A0PNpWJJsr/6fDHYy7Vk93MSV+t9hh/7R6E+g+yfS8ZCdXz72Pg0i9WPp1JxMFVw65CC9IxY08g8tcQ4Le73V0pOp5mw/PKyn2qGctFzL7xFBs3UJFAs/u7d9zZOooY0e4N6NbvY76T+Oz3XUejZuwrEBXXbli8t9u6vXxsw6R86oq0NL0bTX90qaA1TT3nyh4bHT9s7adAv/gYM+SbZAmafW5n5PATFC1eyveKuNLndFRuyhjrQaAGrcnN/pHI7sJZcSWVcNdBLpqFKXZ2St//h+/Mm+DibJqtMW1GuTsPCS0oM+9ZDqb1rvKbzDxiBkdf6p/rpm800w/3GYldk+x0szaZY9Qbc/kP6N8Lz/5PzeEtMwn4jSnxYUJXkgrrOpRT5ULq7hqD6WlIIIdTkMZ3v8YQvX1BDfYlww6GEDN+dOiyD9IDBAVlAHBwdVQuv8SDv0o6KKnpHZbNmVWno8/G0W+xe/qGtP+y7JRIzGrTvJGt1dlv7TqLmhKtMLqqFWZ+qBlnrYXLBvRnT615Rb+/rx+vXoR/4nEf9BvmFbpnDPJwTGrCfpfGDpL7Ks90HcGWpKtGBzSV24wlKIHEJ/XFtOcvx7V/dpwSseQirvuEwicgNKWNwGqBU7Pj5KWZv1P0ok/ZI3JDIRhWADD2ymYR3uu1a8nu/0Xxfeff2E2a7QfXx7mHDqmnuPLlinAuz0YwK5XX6T5m97WZo+p3wD4ziialq1mwepGtpHgVE8AP8SMDIdk37cjDHf6XYh+5XaHW3pZzcP4px3mtk2VjAFNf2JvuRcblwSDjqTFbt59PrfHeB8gNlLDvcZJ3NkVSpCPwunj/UpehNDZx/524d33h6Y8mOxJD1XiFZ48p1Sm+bDx5h1nHhvz+EXA0g5E4r6hbUiEZm31clm7FnqP0tZM2XA5cnXTM/+7/0Jxz/2W8buxPc5KnI3ZQv7RNMATARmxzYCAl+X0wLnovZGiXojyJvaJ+7ejTwDeXJl1R04ddSg/FLl6u0wnA+ubva8VPjGRjT9ufytWuPlqgnzY5yyEy4HQ9VIM7+bbHJW+2OTLdDhM+RLXxDt5dGaBwhz37Mo9/8ejhOu/O62lfweBh8XQqxbL1EJfnNddSl+dfrvg02J9mODP4K9WCtKsoun7oqDp7zAx+5fVTPawViX/HW7emucUsGPPRUQ1niBFWq30o0oPbDxT5yF8OMPq9KgzTavd5MFm6Oc/P4bdZujcthR4NdaaNXuL6KdJTR5tmacdqrrirWK4CxmGn1MP+9qYaZimLAoC5xt/bzj2En14n10V/lZkkuuS3o/Vy+cE5QJbhI4l+MXXV2yQrkWy8M8p9nlKc9uHoF4E8gopz2OPKRs1bLXlFpljo1fPSh08H1v3ZmI/9PvvtVUprfPPIqv3u23K3/g1n/IvYUjta5gdNu3NvK+CR0mBmGHWd4lnvEMkeyy6sumUqUPC38LA1RIiTrN+hSHYpmu1Xm8/eNAZbxObTHZ2VaDtg+5leLtGs+k2LVQD6//23d32hpXK+G1LmYf2Q800d3/b/SIZt812JLZCDf7Wk785HEZ9Uj+V/lwsImzezESOdREmxlUP6g2b2aK75mlnpIdvm6QGXGtHXpaFl0FGaGk//8B+k0jxAKNnYfAjvqdG8iwkfePDMNGyhyGlbPdBxrFYxk8eEwt0t7Vh2M3LKtSKNyDnlNlqq/4qn+0XNDp7w/gn60BjSqWOByWfg9hWzts4fKZ0p0z8ryIGj7bqe7kLrTn0/gJbQkrVCPGYOPPjxcfl1vxQ0Ktl2rI2CibZ83CvfbNyHyai2Z7ZWNtPZPe1lYya6kkHMpI5Kz7f1+95r1CyNg6tZBFQeU3v9a0kDBemB1Q1F6ngh5+XFdsV0YV9Bbatz+/XxItvCKDZJV6m7EHlbpiP2vZrlv7U4aRmsLy6FStCWXEBYE2JRzJ0v7n6xlr+1cPtmODDURgeULg+EvYf6S02L6wd//Ylum0Zpp+Zry/CvnfrpvEOdlM88hrfsT0hzS2JsxIzwrxJ6/TqyyF2HtXs0t3FR9iD/+bn8Sk1i9FF5T+15kH8E3Iac7UA3vc3uDtlI9IHLgcIpNr51oRW74eUF+kopy3/w2n4zn7Wxu0H2CZmfsWvJn3SvYe9RyjRj/v77FJ9C/NOslT2nu07XQrMx6AunoMGxsXfMx+ZfiODMGkdeSTbFiqsZIf4m2c+G+PFzfQelScjtiNMVVHpFTsBdJui1Ff2SjWSgfbQ37tIOcQghMoYXq4Gz/hNmp7KxwcmS9ecQVj+Rt5Pw4UC7ldzN/rr0rt3vHYpI63GRP3zs/1Eiy+pjhV488vWfFiy3Bj1/WtrAa2h2yNNBU+FbDKFd7BVT4mS9SaDgkaBzJQ4Y+QpggG3WXFxjJG+9TyNm7srd0NbB5KD2it8ylfPmvLZbn+jjiYRfB4KwHOLx/u1/eBlyVNapYVsTAGhKD0mCBHxVTpl3nWq7IyWuS+krYdH/2QUWKfpk18h3XpdntPU47iSRCes7fLpiAstqaA1/7MZx5EEAMorfhfe33ONIAqAqE95GksFG1nD1c7NGvJi9N5B0+UIWhZtJBItVjzuvvl9Fppe7/Hvtk/t3Q/8O/uG59vzOYntqbkj/6FF9T5UFGfwU3Nd8BmY+6REwZUVnxZzrZSw3Kot88oU9zfMP051NZXMNXD0BDW2RCJVhM+Af689ULV9G6PrpzN559GHI63v1QlvEItHXBGgtGtRPTwCd/5wIXgmKvpjfDBpWpvv/hOOVxHP6VZNqxmjvAN/OhDKjLvrGAMFPZ2WDxBcS6Yljw/GomX0qWiMArmlAvv7MZVWrGc+bN1TMl+tpZ4xIGPIFYvJ00cSPx3E4qfF+r9AcEnslaxX1gaq8ttAi2iyYQidA3O+tKAUSHWdsO419ylq8vH/Au6YvM0uSdZMWmQ7jRhe0QeaI1uo9pZPqXTAtQWWdlPCU/TTlDSN5FkvTAyrodW0jdCznLF/Y5qtGR6o/rVeGmWcmzGWqLCLRq/q8bKNKssNSF1X4xfaYfvvSfPrFit6X22XezVov2CcHiig/ov2avcTF4P4N6p4o3OFSVv5v7bE/NWsqFeVNUkd3x4YS80DxP1vfiQtHJGAoK9jhLPzrS1Ofj5NVOVVT9QmdjHL6oQfJqljgKc/yE8Wdxa9VU6adfQ3e0ATY6MD023ZUFRqq808VFaJEpxnTL11mUekdU5NlqUINPaZ+Elq9T1hdQ3vUQxXhnRWJ2q/sq+X8FK8SAyGxrg0W24tnvNjqwanw3fW26tM1lNwXdSHw6cQROFaGvnncSVFwvZDqEZ8c8ssWcyyTJtgk3oA+GVNjwj9HaRq7HcWYJkEHZvBz0xtNtm7HXSHWRqzCTiRTTQ9uZVXVtniwv1jbCK2uaz25xTM7ELN8sFsrQQ17GVaTesBeHJlllBGfSVQSOjY1DL8yMSvThaamm5BEdeJwhl3F9FnEsx0NsLp5VF63sKzFiIhNQFRm/h3TCSJ3/ARgsysgFem3vrpPD5xuJ9xQsuvJEfpeje5+HLN9R++pURNz3RNPfniBSTrtnml/cJ6q/RtqOmO7amiZhaVnqGW1BHYD1wfm+2BGXQ6UaEwSj9LNli/hRcC8QTWuBwYOZ0b19xeP6vk2YN43ViZPYVmzpD8yHGY2ELRUAqnqLeiS6aPTRazR7/6YvyEH2molC6uhqeFMhba0BrpzjyFWSeNcEuhP0CpdeT5ctQ6VU41hEVcjcBPyhf5kEc5zum/gzGX825W5GiHJNhcUokEp4WS0I1itAEPwvZh/NxtgUz+M4ytbHvCd5geBfRSQwyPFNRsfOUbsCFi+1MaL5bGtsgso6e8Iuck3sQY2+B4IJI5o6zOr+YvXfUqTFjveH7bxI+ukYZ6voYb9CxO47oYPAKi/yBiPHCLKbO6qbO41bK5T/YA1WvXZz1lCcI0pnWs8twQGA+9uGJ92d2k8wWOOFERSiH1+U3Z2gTNJDOn9Gusk1pbycno3E100/WXDdTg342x5KmrrXTYjzNcKMX8jvZt+jEjYIOIY+Ey9+a7/kvzaU5dZdoweLJ/nGE5p8197Atazl0Ehu8KLZob1Tkc4J8MNlJ39dMDen9ucy7uVDmmOIOXKy+RgO8i0eNk3a0aV6pcHs139x1M4k+5L+zXDjpo//CiNF4psGsvBq4qqRyqLyH3EhP6Q43jeYi512/GczW8wyk6zmMbYbgCpIAvyYzKynLfE8z/GKPo4dXHr4zv3bbn0O2UWHrWjpcCMW8MxMoawzvlZVOklfrfD7EInciLwvfdYEcUTmzL6umj78DHlgjDQdFXxm9bWzJqyBvAhE3DEl9saZwJ8ApPb/LOvnP8p2oO4D1hHSnhux9R0Z+754nb+6mTYwmFrCEfWS/W1AoTTc19EqDH2vAnIuCtaok2zoklCTJTPZgrD0sNnDFdRXFTobHoCy5UHZk48903ew271a09lj8wpwnuHLuVzkVz00w2br6Opm2/S+BR8PZ9gmKgOQnHbH58aksQqWwQgdlxaeESacQFTPHBOd+TxSMzYaLrbmLkqwEm5b9MHUzMn6L4i6jii2dNUC8TLpcVgOsJvkdd/hp+2FD6nEDqsP+YpG5ifjmqUgoxaDRdVyA8AJB2NkR//dmozcwejULCkilZuD1rpPxbTn9f1yMn139rcsg6nlzZpFHJce0gcvPbQI044gK1sDPdLVsquP41Zl+v69uzsZPdO98fzcqLPci1NNpKpDl/jz4p2FZ7v20oTj0K1HIgurk718lVk0U7Yd5Vv6G0waWgZhhPSJdLTw80rdNR55SEtX1aMMYV2e0VIvvxwkKZqwhq7xl09PjmHYeDOUqo9tsn6gLeCZVDc15IwXJhI4E2+xYLqtbdyxRxo5+wuveZPh031Zxp4qu9KVlcX9cPJy6tMn9obCl6TWebfKi9PlNylzGflAPfi7NALbfY5LMpZQcXKjFLacwzvJvy7C1GYkfb3RB4GgRUtjOMxgTi+G2BrZ45xN6e7/sQNlVdle5frOPrXHDwPXvHWWdG8JOXHDXwSOeHm+JcKo/8d9j4uk8tuwek3kOAPf4Ng7mtsG9vj+RC1t3QmSFRaadCY3P0v7b7VyxcOK3Ggv6iCwbWVlzPNIR55tWZVViQ0Ei6z9SVjHQSn84Nz6PfE2WXwHDMiM8sxfE/OvUBOFQ6fXtRSIwzW4CU0BXXlzkqkzqrSm5JnyywJLQTkvXxGlhrnV5H5ocXL5P4dPy5q/wDQ8ROfBV1hGndEVw3l6yEVhnmMm2mMLAxzDeV2LS9rMDUGtgKL0c7KhrflbInJ+SRLQdI4D3VKFJf3sL+vf4iPIc9G/Myudg3nlXtS71dBzcksdqQ7P1o3njSWl00R+JcG6S7QNSBl38YXx/HxpKbfXm32L9YcZxwBsELK5tQFWnvbaK+r8g4aQcAmA9imaPcnOojIUWNn5ucyS8Em7quXJMTjzCE7NxhxGB7DGNO4OvDplrN6QUkDNf/9EdPVX18yuvvpoFl0Cwd7Cd4qN/thv1luJ+79Jl3BxBNEQKuK0ufLrh4lHCYXZnZvyqWRQy5XtjMljuiBz6ZJPJep3WHdgZIN/M1zK7Vqp9RaGoQgUuOuUJW9DNDH3QTaWN7L4Kcbx47ogbWRAjfKCFwcqa6C/0h8VHTy1ATOv3RR1Cof3S4TIFPnILZx5ku7kjqcpGOwTtDVkOggQdyO+J9PrUjR6bPYJtca1dvIB9kaT3VLEy2XkBeuPtItJ5vaxWA+h46RYrIicSfkaRv1oxknKCn2Tod9bjhEh3HNU2lCtpbmXUfQS8X+BD1yaLfwnzNxUyDV9fOSXFvrfX7RE34K1EEAbUcq1BfldaLw2t+X30jMEMf97dBT79bCxxVQlJ+TwHnWFpb7WfzAcBtGklyb2h/Ej6A7scS0ypdKeNXmtX2J+d5QEDEcz/vliwHLuVGg9/NOqj5uD8twc68/dtz0MA4voRjbyi2yK/q9gIEQk74WxIXvQmB/lVpnj7eEmTVa6Eki+eoWrzqlwwBfxLvVi9e+PJbG4bJ9vvS/A49lkb8DjbmL3wcUq5y8muPpve10fufpfLiJ28MyCf5mddAdO9AvXi3Aatc3OibxNOGBSiHBkYuZ8Z6YJAVmQIvj343rkZB4gaHVQ1WiTLaV0/wyfvFyebHXDm7qV6STLn3T9V/buFloMv5GWtTWPbS7pJFW0sAeaLbErt9cpyQLApBxZtefEa/FbPv54J1vszINEcFQb9Z7mrrIcUQk27by/Olv0bpjranEYEuGKvH1f1WcN1FosP19Xu/cWn7VFdK4eBERu8XN/kbnuNvZvyweu1jJNrB6oSDD7UqdQ0fBn5oRRAYE40zxuZmDqTgwKk4kp1nmFwIuOHy+Gy3XgP62rPlxyWXgbo6lPq+KAz5LbzjZ2rA01xZI/UhylfVMGK0Jtu6qNiZT2JG319d4yoH23QPmbxsptqmKfcCJqHczFgrwJngmfksNDB8HR/ZOzU2lLZfqM2SwQ50H8lzyGS67j4KbAbnmyYJnSy8+xzj3DL/dOPezIUafzp+vqf4IPQ6SY5MNgvpQjUYIsLZ0Y7mUAfZ2cLuK9P/wwHKV76p9Q/7ISPoZSsnCb/ndlf5je+5ocRZY3PzI7waP9kn4uZg0SWjUwM4liqUHf9m2SQLSG+1yZEWeT/i3RNAPsdj17xKepqE3+Qq4sn1eaWRIls1hWkdZCsdWh3pA07MAKJDrK59vfzyuolGNXZVbvzT7L+qYKc9rtE8Hh+ae52FJiwI130FhJWYx46mLhzQ2jweP5LvvYNxgGeQKcUrTWdf2bYZP+/HQkzrqnAo1Fuibkzd9/gkmrucYChfb30Kxgfv8ay/Bxa7uixvtq/M2uGzLbTMFXVvnW1gegGhf01T21/FvojGMOEoQpLboNWvgVN21wk/qQFtoQCRqhQ6+RY+bhijZKfLtoevXOpkT71la9lBlnTUteDOf7FVP0UdAfghNwJRKTOEMpaTVKR+okJpQnpGshr7i1LdCMy9yr7psDf0fLmAXXdsM4Bs9rP5bbN4/MK2Fy8Cv1qykeZbxydzh0A2SNg+qHKDXmn3F6Zs77fYjQYOIe3Q0PsZcVJyTfThWSlPfruOWzPX6bMV6jdFvUs9HUJMvxl4K3l7lfbrCYY4A/ll/29FmAuvsLzYcL/6jzUzx4CJs7QH16MxIX5eRntwpXVlDuy9j6Okaz9ve7zEmaQ8iqRVcRj5to5F/W1HSCRIurp79qkf3PeKZSIM62RapXh8H1TeECx/KA63RIFKqdVDGE1xDq25vge9hzh01LKci6m+6oIS9WlWV9WM/q3n0qC1DQuszh/W+djtXeOciTIGX7OHB5UbUX3UCEJz6vLOaAXGYqVVO32FBr95ruoc37PXPz90jWUTC3z3R4tWoqiZ0IaJjnlv2F1U+DnL6sOR4z+ev/au/nfnZEa40D9u9qbjQ9eGAKcdrraJBamzO/Df6aGDjDxe5lfoe5mLVe5d/Y520vOTeP0edoycCstJei3V6LqFyZy7qfh5b9ZUz+kEOP3EQNYiecqT2IYnTUkNt2EftX1VV98FBYt2UBtaoPgCnZfENfbP/VB+mfHNGT0HCukjzN3xmi27MzhVsv/8/xKrIDkZd1W+w4uXLsQwODPKmpN2K9CUipq/Id9diwj+wXILd7dKgAM7adaSay2GW2iVrPGXyIl3zJiMyxgoKfpG98bPyDxUbnHdAdbn0BLmironxsgIQjnVxzw0XMcJKtRPP0Z9S2mzjj7IY6+uS71RIzBEA98r3B/YrjL/WfuU3on4E5qndMaTuKr+1DC4RL+FtM2qH0WLq72dICSiEDBnLQ9V1gntu52BG+ZF4hk5wRbzmOO/VhCcGFqLA9bZ9tb/M7HIey6n02jNhOHf6jfMYe29LBR8wqtSqP6bL9N11UQDr5fa0Inqo0A8gg66CZ8ZsVs4bldzkv2qgILGKbjYEKMiLtn1spMNxI6tdVBcVpywPO2pfZtiGdMIG3vm2fdtLm3sUeBufVMpTBvf67cADj+g85kbTUevL3Z8s+OzKl7O1PM5pNbkHSon1avRNVY34ZFOVnOJC3z7v3X0+5/f2DNLJlLo9YynpjcUB/bx9sjJ2ipdYOJebkWBHq51Lh7rd6cS0np1mEdn8Hb4E/TPe5kbPZtkEPtm9CavflKr5MkQKySOEzapfopduVts+ffbyNS0bvNQ/WT4Vh+Jf6MyX5QrXYszP7QRHZ19rff9DGhOvXib8yYtv5MDlNrdthF14Byg7nbNhIz3hMlhKMaQIQfhh+w36AMBXKrO7kIqjdxmlfq+jRVN1PVQQpmvVXeb0MKZnItcgWKD1Wp8eU3+od/T3rRU9nl5b2845R8PwVx8eEH2mwp6cI7u2NmGwu3cA1a36uy7H4fDWoQfhRJcbsK7dnSDENFd2jtPqKHfBC6v7wfFZd/kWCdmdUwL+kwyq/G5pV4+58rT/nkcTeFvAnenq0gF2naimgaaGPOsvcV5DjOIhxRC4Fzjz2xMCqcNUZwF4hnFy51Kv4dL+LF6+V0+8dPN6Y68Z7OzQ0U/x7qoHAvm85DHUOaJH+93eztpfN7PZf//u78S8/G35vmzM9ZagAFD34a6+v0AsUDnoeJmH7UBjLCODEcpWb9f+sekgm5fgJzZtob/2xjh0JllnAD7lURUa/TNyql51W8Jv+3yveZYk2A03d+/YubHPSgeoMrdO3p3Tox3FjfyrB/3oieMg1NH+re88tYr/xV5U1Cz7P7hG2dRbIOke+A+2DDMuYokhL8fDhcszbZ1Nq+/xevcAZrwSZ0nnnlZ9jUuAJvVgubsseqSPvzZjti9WGBC3oe5X8YVhzqUCeNIGn5ctFKGs97kJJO+mDu5q4lz6jvMySfqNIzgHLKx/mwf1ydM0L1QogP8UEvuFM0zry6as05DnJHf999eJ9Xp3UE+vktXOWrcHhJ/S8WvEnrKCSNtlOYL6iGad1vJ9aUgHXsXTYZdvAsB0xSPIb+GNfiSQZJiLgsDKiB4joKtXmTgbq1KvQqjXhJuuP2t5YnwH0tAlr3m2zRDYp8laVYHkgwJabpAt8EJNV9MWf65rigIGoiNYBj7I+bfDKdMyEDwtaGtR5hgsPGI94ppebDBL4AkY7vTk4+V73vlkPWZNFxXqtou41+Ht7RawoRteO3TLMw7iHsX7EaoKNWmEf2tkxZAzOkmnZGjkF/Z7dOGoG1TSV67ddp79/uy0GrMg8y+NeNnp375imiqSDuW3G9Wkam/jIIhSLm4fx4MIyyjrvW8woFoMsdxbZAcxQDK2/7R4QQhkpNmk/inuhGtkdlNhwls4s39k6K79qNDB3ccm3Xelrd6PJ+Okau+LnWj9MtJJBQBezXP2bjPL7Fg62FaX1ot5KIi5zTZv7llVUEyj1dNIrff/sEeKt4TjVQBXcjCSI22ndicEbe6Wz/VGfVpls+jf/dtV6zXmkj7ZVxFWsxUMpdAOH7QwA7HR0uPBy+jGstS3yp573EtiSZC+Gw2Xee5w6DpCxwlUYgj4q4Ja844CAAlbkpsGznGoZUT42/86/cwTDxnTSEz6APM34YU8zfrolhWMFZuTc9zVebwTSvpPuO/uZ//VPM4yAvjLwNf9ZsXItWbGFr0igB2iRM99vjcTL20m133gBflaAtVPJ/vv3wTNBfWzm3txrT3i0n3e8uPqqgrxuh/YuK06vwKLJMblXl3Gj+BEG9ChdHdNjUgl0MW2eReFKam/XDVn3v9Pej6Q/PNCxMYsQO5twmfVNc0fHK9DmwbGY4k93I9pv1NsDkjot4CoZ3X3Ar77Xq8sfyhnsj0m/6px0BLseqyQLvufAbt8/nkytR/sSN+D2e359G+ryKyvenxgn8B2Y/4f0uaSjo+Io+y9r1Px0Rs6RlDs+zPxcNaFCxCaBMom7/+lcbCmYM9vtkGnnfb2Ko6lz/P0wlbaZ8hqZCj4x1RsQbqCYnivtpYYqqIk+Yo0sw+l7Sfg/1Y4wes5FXdEqTqHvWGRfJedOg2Ek0mAn3/0r13+W8GaEy2t89GjkpDycvY6R7a8w+gA2LGoHdUsVvjOvo7ghxuJ1oh9sbHLPfHopw2hFia3z/DlZU1/rcBIZYz8dH/OZEHvb8iOhmJHWhsL260xCAIX+aYxxxLtVxZ486FC6kd/fZ1WobAGfWMy9U+wO3CIqpmsKM90rTbHDkiZ+1K24/XzuMEWET95NcjOsqzW82FLDU6HGfwSFyaiZwXgA/5YH9WpH0G7Gr7yHeCvc4jnTOPMTcoa4wW0urnf2WxLenr9croF3S1aNbzv90wDPlqwkyOSXnqe+OonZnahz+1um8Ui5sYsiPwvxkONFGarLbx0HZsxXWTvQrvCM3wsuHf636bZFOOA+wBpL6OeGt9aKE97QWzXvoYMc7K8CG1ztcz6QW5ubzdadK7OGCJNNt2VXwQ7uy0E7Ty/eKgtAzbYmoMdqqjhux+cQ3nhf8d9n5pP4XmRaBn0NLw4tf8jgjX2T42mKlfiXWc5gLol8XFExACX9h6eNf/+FRoUYVMyh2kQzIR2wO4G/fZAUbK4GAWqeTpgM4dSPJf9FWsr53Qacwcl5d7FgVv2Hl6iarwko1E5Yy6RgSVpzjudaTjGyXTK8422q9VbikBF3mAnTP2+UqXklbP2/eluLTiU7QNO7+HSE7H2yDlv6l1RdFh11B/VutqxGXnbf4eis7vX4944WbbibZ3s8HXV649/fVOEFlasFth9v1nTFjUQcCz8L9cJNk8QuJS2IM7627UiaF8VOMQT3/UdOvsaxSHv+/c7QgrnUHSE8GpQy5wQdJXwvtGNQcB53hNtrpj9qoBXxF5Z9tandYFrfqc92j7d/2J88D0TmAl5/8ynrHdKJyrvwJSNxcfXs+ajMlYX5/Y+w8qOD+cG84oQNJn7LTw0+y3fy1UEzPX2NL+QA7tvQSHMijfo2x+M6jz2VhJR7TSwRPD67MxGVrRmTQwZ6ecO6qQV9QW3rtHQWpJWAKyfvwOjr9g8LeFaB48zKJDvo/a5HPRDSAQ5+Dt8zGD6eMs/h0WtqXFEvZgUK1Sn9kriYB2cZDUNprsyhvO6pJauHjMxa3UrFZ/FWjJ0r6stg7GBMNOEDzrU7af+kgFH3YAUHxvH0JRNmJbPqGFxOhM4U+hncLvkL4wt1YAUcznp0/D0dog0Q/5mFHDt7XVhmG3tP5uhZYBmWX3IvV8fEzhsjZoUHfB0vQo33rU2fl4tab5m6X/kcT7I7fmzLeUDP03xkhnzrdtubCvtrDFr8m3JT2UKHvX8Q4lLRbed805ONluAjuz00JJawPPg0wJV5pjkArIFlGBjb5zBGyn4cNqSyb7RQ2ObREwYP6lvXFfSMgLs6y9yaPGY5ES4evbkJPEt831QTEy7efVkAHoPRD2x3niwxAXIZTH0uNHhbytr/IYvrDmlS8mguF/YYwuN6jh8Z7L9J3ybRWhRK59ktnVh/VbM9WPsV3WEE5+wwCUs7V6LJOW14KeVxLc/g8PB/QTPyv6oxDM2qhmvNDeYTIuYWY6p+zf07Zq7chA1gxf8dGyWMuy3rQCXNWzPeXP+qmGRpRMmk+N+2Gf0Ps4OidH6Pt+sFDTG6+9bBd0dRjEFnPnBr+6fOxk7pnk58p4LmD/eQqztR/1DPeFSAcwEf/PbWqaX8FDKjWHnsCjPjgNv7bZztMDitBmnXbxo+dPDpLt3Z5/F4HYVAXMJj8/eVFGtyCUoWzfj0nKcKC2kE/Z/MqkvMbtNDnPsfpzU/jh6tNXSh4HfU3zCQV8FNS5iBorIrfalPtTm/G2Xiae/lMBTCP6LYtlkGQimUCbpp2ynuCzHwc4Psk/MIbpnL9fQGOaPPNWEv4Z9zIu39O4NOLJ+ZGSwhn9bbVd/C3rgvzf9wN8ywre7wBsMG+X53HIubFeonLQRVTtBajot+X/99rIoJc2s+/oaMq67peHXMllGi0bbfmsdTHzePZDVmEjBw804V+7btNJsUEg2vBxb9Zt+CnkdHhG56emGdztuZhDjmm7MwQ6Ft66YFZaLe0gGqtF1OvUWabO+/Pd2DFK1ti9VI0L3818MNEdsEo76LzdkeyqG0CRBm6TLppVIP9wCnXHPyoA09k5JP65Hwal90XrfHhghU6zSqSaXQ0ydXT1gkrCxRHefLVF9S2C2NKs5PdwCS31KJtTG+xCor5L2cqr7L1b+qd2T4xbMRFh2hVd5r6OX3BmUgZaCZ26fSLnOmQ8JSpHqOPTPc0Ra2dZVVQDJZPuuaVmzwwViKd0jBKjdNFSnHpnLsAWYeqFpvCj7vVCeN4vemw/G4LF0QOftZnyOiDwrE8uNrCfdT0ZKN3gzqv2+man/yZzp/y+0ZwnA+IMBM6q8bHzVUREJJfBysvHJ7XvL051lMhLZ4tanREvprl1Z+C6nA+1xBtsHzszS7c6Z1mInlJ7Oj3pVdPXTj0pfPRbuGLgoiXrJ84AyTnO4lJ4fe7JSOTYp/9vS6Tw4SMg32pMVtDSJiA4r9WsgIFasuQ0krLmqQiXgHBu60tF72W4F2w0T4gnpXR4vZHfT+7aSvO7Ycvii/jDXkuwtsdsu/Xo7fHbRnG6tNsGpJtg7da+2te7YsRe0flpFaNB3LWhGVQkAuReMqaYsY/uWbDSoKvmlrfr61f0gpG7mZdIr/NT5WiVUbhHLqdq3MgUBuPEMedB90SICnZ75qtV9ZaMSW+SzqOY/YpXU8L/CSI+LbxAzxIJL8/kQKqQ1xFlY2ib74N5+jAHN79IUcS2ps2qR+9LDxYWqQWwhOe8OfDfmO8GpHvBW0oI63yCjTtk4Njw8n5EBE6NlMvvubMwFoN+bUOztYMr8CrG44DZBZlpV7mCXVqy5R+sbmJ1xurc5So6YzgEQWC1eGsPh78xrXBf/k61jPZhSS2iYyWyjGyvD6hu+RZMI5lG8CmLVQFNhX7+dtgc25PzLuc0cLCqLTPMGXVhNbNXFAo53R/UFrlCIHK1Spkx2ArzhMK9EnFDamDC1qqW+/JlAhY6xeSdQkbx3jPqS1Az3Au//98spW67+qcgy0/xm2GtYsXqbSDHsuh4YcitzrBu3O5vnVqMbhVTotfXL0x3txC6P8WufLMD/gtWLBr1U7bgDiNPbNIe3wr3Q6HxBTbPkApLy9HK3bBme4WMWz8345wrPMIg5bKPtvn00r9JKT8HuvWybbtl6YiUUkwAtYTUUxuTsKrwWfjBqkA2kzbl+v7guXncwaF78Kj+wKKNW0epFqeeVKtm7W+fhpghvtpBg5cIYXEHPYiMqdxJhHzvwbGwK1V3W/ghmf/sLMRscGq8VYqz1xdsa2rhGeK/PazPXak9XcD1YDKdGpf0MMTe6+rPBndtWeVfb4BN79TivwoMoaLkKPqiE8VYbjj9rSvu/KV/OZ59Qb4uufu5fPga//8ALHayNJ9gCtOvPgfZVV3KjEvmk0JPbqNkQCw9agV6QDJCfj5ho8xaq6qlS739f9+0xWeFbLG9ne5uc5IyMVWZoYgTY67Y6zJ560vlimwDTr+MB5rzWK42rSKGt2BwX+fZ4UbKxBBysoaEh/WJsVWYSzTySoOeqXNTBfN+5zUQ5QtsrC6eMa05faY0uEFhsp2SRAgPA5mLVlG9M1RjHvT3bnjVXszVDM9Vl835/lVLCnZ6nkAEM5exv6D9BYTse39xsySqC3SD5naK3KU0Z+sTLZM31buKMlUelQxr5KSM69N7qfg8tMhh5qDPP7IFzxeY+zFm/d8kXoq/ady+MEdQcs/CHG9O9zc/7rnSTZLaDX/k/gtM86QdmnOAgnMgNQvpnY565BQrX+RbBzVCsptJLzNgcsFZ2GZ9Af6SAy5NdlcVd9JbSxIOWInMmQkcgOvExNWiOB/V7/acco2plpI02m8UFwauO2H9s794r8hksV0ZEXVd+29T+T3c+gYHXrJumpkIqyyIfD5nhj+nWVs1TcnGBZopttoezL7bx4zUfgenavywCElca11yFZGiMspkc489oXP1aG5FgFTv4t5m79Ud7HmhvojGTuH12QNNC73Iub7mE5/V9cfWqVzKFq1heplBZA0XbyMR3ZWsN9dR95kfZYSJECvF1kW0J04nsYiyspHHKVV4IbW/KDRzdqLYmovVtUVgrJ6ZFMQBqnjW36UO1M3bL/VXV33bYrI2bpoYNPJmQryqzR+l+CGQW6BjJutYlGErj3D8fMr5p1ggnn2mKy8ThGf2rhMDG2/zC0qn8V4rhJjvSeTLw4aSm4XivzZu4MZH18B8kMcEL8u+8S/xH0xp6EmfGtSLHrvVZMbAzbHlI2PGK5cB1WNS9C+jMfL9ovYxk7VRpnbYy7qr0dttN1rTO4wMmSp533Xrxj/jo3yCzR4c2enawvV/T6WoeXCbpdoLEt7pnEYIe7Q5xh6+tyIEOy2htmKktFDWjv/qsH5L/MruZAe/Fol1UxHNU6v3lfkM9HiVuualhS+NHBgianqgEmdIKc6eURDJQOm7WHJarkcL6GF4BUqj9hf8IXLtj+aJQKJQD7mzpOPaIlde8J7f1gDkaD0jhTMcTCIjA2bEUj62EQQapg1UVMYDWBea8Yz1Df9lnODq2sft4svSeIeBPOt/hrgwVd5zlrK4f18uCZeyoboV2oz+PIgO+S1iTXuU3baAvld+DRGVmfPTZBbcHeEra8aew/OykfusEB0zt6ZbYpqypnqm+jJclAoqJEOSdzomnbySsPA6UHXwIBn/7rtxiuPDnvyqbW2WGdp9rhrvWp2SIJBhiW0JQrNId576D9XlnHTTFcsUnEMBa87go27JWq7JxupaHRyv2w/grJreiy0/sdzXpEKC/q1Lttyh0NDOJT6fdhJLtRbFmeGfZMm0tkYmzCP3lj1qdtavTr5paogI9PNxdbDWhvNM8OPgZ7d5wy3/OBRS1Ly+VacqtqL18aGAk7q74rx1yF2KscZi3DPyTzbjG+WBYJ/abvsYC+n9ufrnFBcfonzhFmsT7QoUZauI6JFMwEdmH5PY0NKcswX9hok4pRS9f3rb6x5ThACCPyzkoEvseALsfnQJd/YEfdezXtJ+HLxrpYcdk/U6yvSL8JnCbFEbee8B6tGNAQetLOe7rF7RxKz30Qb4Hz97ivZW9eANoXMtbq4s3RcENb5Y6jXfKmzKprnp1oLN+r/l9m82G6qED1eJm3+r7+cr8MopxOd1exGhnh3VPJ7IcMaGzTyEDJ1FjCe71Hy7YtaIMY8ZgrXk8kcjaiBNp97JghjRuqHoALy5DBv0fBMAbE10cFUmZENrknRvinbBFzEIQikG+LY3YWLiZPpbU7eZ8Qbymm4U3QZ/mdewJocFLP7tKpzDitD3khT64ge+C4jJWoYWM+H2n6iJhe0GDYyh9ngzbN+dkm2L3IwNmgA/8mLSGsIJaOInxfVSUwu9LLuzcMPp5AUzco+c+fCE8JcGL7yBr+tj+WAvQi7eDraX/JItfh3C5BPG5jWX9dB+TACFt/y2zMbR26jx5NSRre3Sk3OtprthN5u+UBBiyIUXEN7K+s5RJq8ssRmCDwCg4E20V9FQoC8Wz53xwgbGRez7A5qUhU89ds7YUu96U82OM9EO3Rx7EK83sh3TKGGvnRJsOFMsh2aVTL4o8upSdVl0bMCvmAIdgs0aZyDo7T/rnYazG/v7VuVZh8mti4i/LXDecGZYMT/EbO/4jd9ltwiMptSUIEWJbUvNKhlPn2EBTIsDybBwn2TX05Yj86DrdunRUbRMPT8ZZ7hPCYE/5Klbqk4iAOjm+0fgu/gt6NLBskMhKmtkSXkn3gGsXcO3b7fuKHBVznchGQ1qEqBgjFrMR7JZpZG7+NjjR1TE2mSQ6+RdYKqvjrhJ36P/bWDSglsPmqZtVdPZPfkN7y+GDFmBR2o3UjlKB5r94o+zdNFExua0w/OdT2b0nrx/DOl+HIUZCuXyPjza7Sofz/h7vCI6aSTACyzvwM8rbT4afm1+h1fAG08B9LbZF1f4/pcb/JLjuO8NXnPvXcSd9ShB6JUXeAm2zbqvEbjaEJPW6nwTXhJoEe5cgS8FrmuJizKgbUb/zvhkTKSW5P8W9/PRRLjAvr+fbPjcMYjEcy8UjhKrX3tugU13q2uKEdJJTXtiZ60+H+C+FhFQVjWWhy+/pzj8GDaa2f3UXVbKVB51PIus5wzJVPghVk96uoWBKk06aCnG9UNbo74SPGRnN3+X251z805GI9NIQle89vxYMhsJZUn4XTUK8afGQGSUxrJrzsV7nk+1GW3fE2yz7tRN6TLOr03YZ8EWE+BZGY+kvLAlQuU2hOzTYoNzH+5Y0z/zeiu0vwA2WawFl+udrueJVABIfvmYb+Mh/Fk50CCZgwJJ29xiunpLipKmfM4y1g3vxhJ7qTK5r92wA43daH1mhqbZnGg/vkP1wBKx3ErqBRbRtdiwNxyWtC1srcMbLyonKHBwO7y0Eo41vb/VFNqCLaKVVBQKBw0bxV/6fkmB/xIYkfQa9LbCzJzlIvSLRJ565M6YMjV0Dj615XLGvgvyLyxVJbTCz46mRxp+qxMrs6l12GlJR25vrsn/Gzrqmy3XfCcF4s6Vje746/WXxRViG4mca9OfqxyW2UJ3h1M9BnELnIOKtrQJwm29/YkeV7rT46Tn3ats+SpTMIy2548RWkW8bcULc2Et3+c2G3Il49ZZIW6YP7MPFWf7Df+8WY81r/vMry7RLCV7u85ETa95Sq298k6GfzdT7l/+SKjCQp4pbG7IZMnb35sF8FX4f7X6trpVmvE7N4bBWAnVCPBrVE+Yt+46W0Dl81T1Y+TxC1d9scrwUsAH7zDkySlFmHV5xJm9SyQFTv9/tbmBVVZ4R3H+V0dwBXWD0p9TqD7Fkdj5JFfJV3TShdLOpfpJDPwJTp3Qd6HfuW2ICNqmxX9rhFuaVogWwLM7U8Fbyh1x3jMenZje1z0P806N2xWAkkB2nczRx+BSSTXwIABOWx3efIum5JNCl9lI6qA8xKdgOdSX7Xb9BNa+qGe/zrVN43bdnuO18Q9cwiI02tPc3ax9w6qI7zfZSX54r8kWWCJ9LN8wO+mMG7XpO+r7zvT1pbBv+IEh7Gu9mZVa9yO8JZqf8PKUD81E20G1EsA58c/EwjIVrPPIx8dZSLAAJyMU7+Z/MCGpYgLz7uBXviPWORVwFOxoGUJV3ZtO0z5M1Vfe4xf09H28Z55Fe2VCen1KH5Rp+AWnFoLuXrxdOn8fzUqiS2egmb/vyCdhE3XfC6J4T8cCCfyGPevo8rtohHfu7kA49/w/lrrqXhrKNv2AJBb6pdGPq2ntd1iJr9OAX9Z8bU73vo+u0BKzRUh+q7cSULDQn/jLSrVm45njJtu3QSznUrWxJpFCV3a3ZuhKTk344yaSIU1v7E073M6ImPWf3JZ53ScythPH+/tv3xiF7gcMQ7UjaRqWbYcL8XurzpZ15S438TzN2chl4QavZwD9BJo8bdtzSIvWUcx9uAtFoWRf7w2ToWRO0pz5+1h6h2EoHUnIyFfXhv+hwED2Klv/P5kqy9Xdt78Xx+jiOSAk0Ak014+30M9jQrZu9XXIWcrQJsaFLOHpEN99nBhez1d+IaXCSlluqlQLWAllMebhQa5bIiWN4OwZCOBvnDXgGQoZf4PEPu6Kb+NmWvYmCbKuBZce7szIQY3OAJeHqiCQ9eMHS31PIMKtiWlmHXtL/VpW5YPNoBPD219KXwYdDzmLp+R/TiyMI1YjoWnuCbp/nBuaszGIey3frGev7FdG1y8REuErODgG8swA0x/LG8aEB710uVnXVv74Z83igYtI81D60Dmu8omK/6tDqcc8nXalRpT+MQMjaOtkr5ikYBv56wH/xxpcFjL/nkqjxqMdBN5BKjRgnI+PvLCjjGgBl28K7VIgxI2zi/Jb1hinqMdXzo4MZyLZL+fN9Um1sZGHS1Gsx1JGG1hzHvQPFN5oD6JOn9HPyB51HOoC2vvJJ/6+dOy9lBKc5LQ0jLGWhfg/igVB51+cCyC1cjEtCNyZr1om2xMltGbJo4xRoPz0DcR3q9BMDKM0d20fWbITTkk7IsXrov7nSPT9PeL1IlePMMxrdKcohFCLcK4qu8AT+Xh4vsFyuHvSwH6ht9VbJRUm6oAiswl0GcKjuHyLxGf7YP46IJ5g2uuThfifml/QpM6Zk0p1c92QYcCWR+OQwldJ5+aiSP7HCm5m30AMjuH++VL7f/C7W8WxeDCg5JaFccbqn5s9S5xHqEjALMbT8faS3/g25XMUuZus5cHMu0/C6l86DnOZ6+ujUXX5TJ/skuAd8+beeDyqIbc+o7M98YUuc+WqGKKPRyWXsn39t5JVKzb/1Cfgwhv4fgOgXrQ16Jtl+yE9Ay7z+g1tlTbQKVz0iQwYV9eafxviuiiKY2q0sHKu8eDTZW6R8hQdnpP6TKp0rPgkDx5A6IYrR/GZJY5Ob91ZpX5asWXf3I1fDMTFoYHVUbkvz+Bd7q3TQuTtghQueZPz7q/C2XP75hsUn4+jpKaWvwZwlOw2F7ItHmID1amtyP1h+NKw9oAn/3aDm37pMPiplNO7oQvJS+GVVVNipvhXjeh0mdtOfVUR+lpUmzetdmJBk1Rcbh6ndW3oeZihDcn4PZd97hxMAlukm+m2mYbREuDC67459xiEC8MsVlpZFC0l97fF4jb08zpgXfpuBPaFqhJfnMEqTS1CkazTbm1fA86HBY1DkapWQxeInABvfsSpZ12+uqVOTw0r7nf+ZYuFWbh7WypqfXuAWVVAx41iphCO6iV0RCvt/i4nbNwYr8SgrOpko4gxdVJprH3t5y3AzHFzZ981PJRCB7njlYxHAOp5QFw8gn4geWLpV/N55zdXEMzQIfMflIRiTPI8v/X2Tt7SOEpZ42xcc3vsNKxNTH4zHYUZFHgWPFULpnSyvNt3J4b16OM8LE0zvHyXyRBknrtLTk5VTofsODB6EVJc6SG5Ac+E0dMn6V346j2M6Jrev3sRYE0Ms3Xnv7N/rUOLGizro1FfD98UFaANX57eSSv6zKfMx4paX4kz638aaV4Hc3nhC2aNgbdH7XqKugPewEI4VZ24S9/gOIhGzfr5xBdXTIyGt6mf456hCzULS1cj/EzHRO/vM30eas4Dt10q2/qpmieRXsSc1QXlgt1DdtS+qlLhqavROeeg/XKvgnhYL8GSK1VrXaWWHOHkd/Sbbr2wxN31aR77ZdPkmy9rfFKi+qcR9R2EzgouR7Ggwpfe035MpxeMI/HD5UowrgUtW4Tj5/QXxpUFS77/alI+tzCkttTdnCAwX0XU40ovhiHXCLzCZ7nrJ2/PbpO47Pv35cPJHn3fBtY605Ed1rW4dRGNf0biAFI2GjcA6KXVV9epFpaxr6Tk8t2N3P2oAMPbkXjKTYahzu+scjrAL9739dATcaz4bR++yylYjrUoaaeKZtmCw2LB3XBEjsMimX2105ZKdJc/pvTzR4gHFi5xYZtD5AR5TdKrmXUS3gj8tj1Ts0+I53vQwk6lSH/FExm9wDxfqhfU+aSc3GFTc/GO3oyn5OfHplyHG6vjwZ3JJ5KyYsOc7No0FC35OnVOFdKemu93/l9P+rs/8cv1SlkbJStxWRnHSJvmbwAYOLwsKLM6svz1da4xL7jyeV4jGD8323/C6CqhfMQFsTjxN33UDhNXSXVepuhfg1eiOx5Sfpk38nWOpqcaNpvfPw0/+NhS/rxhizQ390phvJefC+m+wb09Ww1cP5dLdG/fyWDNlwJC7kv82GCBZcF2cocxvMI25itUflr/8246VNttrZE4VWufTIerzH6B+TnYdCNj19n/h5aQ95ePlX46JpufKPidMCaNf961Ej64Icz8MZT5kXTexsKk24OwmH+b+/UH0/se094tObcfNTR679psjaC7dbmNu8ho83zp5qsI0bP55rszxm8CWs+f+hhEiVAXOk1wA287WISW+i+K4+RfF5fH+r6Lr8CFHf+JQHKYdtaJXfuj068/mjV6cC72+rqfSiAZUbOK1cGhAMPnfVPKOb7nOGATkwe9G2qThzkOPuTdA6/eK3mF5hz9eG4Jft4c0EYt/8jF0OXd7fXy8pb+1BWb4kZ2XvA11hmuUaPvKqTOrIj2ZsI7yEpcNzLwi8jK2er0MMn5Q5j5sjtkhRt7rII1jr9tky0R1RuTEjrmW5u0PST2L6PvWM9a++QrJlN8pHFiHOOTQI8triFZzeQoCQZj9tVZMz9ugB7vrNpwoq5g5ZTtxoakoi8c1dlXiOn1jiF8XvqerkzFu8mP1v1jlR0R3n7InqWiKCz/Zuvc3genCbcTcgexqVKIQPa8ktRBcQnxik/S8uODuGLBlYS6EMW3v/KsfNhiRWn7VE6sIjQHXTkpR9Zt07X/7S95cGRBEHC9KmvcDneyQC3S5tyxOKMOU0vv1v+uIuuEP/R+e9NNfbZeGFn65jPmE2JV6snHHzn7Lfd418Pb33G7UMPYfu82hRoPYhAU7X4ixjy3EgsARa3T+L72PRJn8BrvmbNAiuuJPdQcz1nCm28EO8xVWTbb0yGF41RFz+eZFtm9fdo9ATijwCrzOKCtnc4SOvYEpkX7F9/Ky7ok5KDuMUnWLh+AoSaoz99FsqF3NJB74uh5blgmaL01fb/x62bpy+9/NVjtxWtoofBCCBmsgdz1tWyzLxOpi7t0vrNn3jp/Cv3gW1/OHaK1qSia2T92dL/e0JX7/vQcsa0v7wz2rc+H5fqADO0TBXN13G4SQDbcpzEuV5SubRjbWmkC+/3E7Mnbe0zKQ4Sa4Ub5jtGxT4zkEVzpRXEyuUFNmxpKzkD+5rO3Pyw3fxx5842usgu9+ksWjhRgbe+tg0sYBGTpL5oxp/lcyKVvi7+aMAjs+/gBt3WZOnDFrUiA0dWqZCgfeX3e+nJr7yKUnMBx6nqQtutYzFj+Zsu3uq/HSJq/KOrWmEuc+n5zsUW7i/GGRAPvR6MWPvpGcIiyLJ0w2B3hGJtz1TqNMI/0bI0oW16Y3rtz+c+9uyNm0YH5WauSXzFhxLdr6q2KQQjF3KzusXkARYQfuF+9yeCVDM5W9fHYEMNpoPQP7qBu58/VyEwDyJXySrc2kDu+yhbHVyjKb4fXcph23tiQQ3hPTef7XX/YfHxZ/cL4Aiqdqh/4kNRJ6HH+2jYI/kuv/y8JtamuMO4pCy9nrnBR5Pu81NN9wmUa3MO8Su1onRp9k9DSrOKjSAPNckUF5Qq2HOu7CKfcQU1N6rphUWchWW092gCpXqL0VQ/ksqquMPSDaVCAgnbaHTScai9ZJ30ScQ6CSxai05Fvf5EhAhO9dpFIYbeZ9onLau6u+MIorgpsYGp1qE0WMfU+Nysz6EVelMPt0DX+VZ8NzVkr/oteF29M6pu0Ov9u8fDTNpoc3kJ4mzrRJNkKXWOI3xrvZjHBqiwfzTInKR9qi5XMBYs6rUFz73vBjKbxHXfLBeD0osm/3WI9M4ZPooOdZHMPdvMndc95hbprnGXZb4V14XTilM63H1sJ9TlRr3ExUstvTEqjCndyl/qBMb94I24KMg348eD8vB67v41HUDDHz0pKdTa/sPw88VXhhofktqizr6M5qb0Fvr1kRQrqFvq7Zfco1bW65yXvVUV/+L6rKwZzHuSqC8oyz7nGKopPnhsVB6P3Etbupfjw1zmrGzvIXh5IX2h9xUYrJHHgJesSpyBM71ZVGi2R6qnVa3G0OoE9RGXaAXcibldEY+M+ZGpt1lt9nSW2t4B+02wYdofSaxnxbxEgB2OXiOm1iPCAkyHzIlLUIV2trgosJdYglPjRoSHsjiL/3rphuU9NAEDN4DHGlQAHb+ZZeDXpa29eVIduVK3gDabZ8dy6RaeKEdPZQrzPT2zQ3k+4x0gpvqHnAE4N9wtHY/MD1zZrt01YGCVvouR2zg0t+3Oc3qVxqxd0saR6neWZgYevtCn807teOufIMSFWwJTI8UvT7nfSrYpsoltmMK/aV1pEAXgQk1lLzs0TisI0N/lzXswjuSMLHXkLh/4bEgHsvpM4WrpmRs0t+xFq+LvENN9re0dDaZktutvTwHo95lU+vfKVuGbjAvYjXUkidvpjhPOUnT0/+GZv3v8MvpBZpnJYH2pPpFks3x8WZwMdh/GE813xcFgLNbEb/AVe98y4F4ssm2wGzaO7UgSOeoWE91TU4RMRpOXL2S44CDMfUW0MT49LcXVJpfYPeh+FBO50y73KsuwQ0Fr1a/q+TP3MEBtJQPOTsFrQoKa0hw0lgtS6Hzy/OrDZTzs2sf194rnEcf7Ch4HUBGfSDKXugE4xuay3Yxq23ruyjlSPeTMPV96/1c9i+sqdP5+aTjIlQKSShQz0SiIJ51VRn5cAcfFX7lJz6Xmq5ZcHt/dtiqcERoTwN8w7SMsFv04kMdvzC275YZd6EkgDeCMHY/pRfEk7sqNRlZT7SgUlPTXy5PhvGE+Dj64vgl2GtxWt3j8/gafBI9yVR1AGjHe4XhZBUe7YMmSTX6KR+t3WRxAP3LyefDNY+QO+cJDkJpmfFuqvkMIqCcze6qZug116+LOyA2YTdg+JWBDpg+ZAn0Lkyh7TtqLE4p485kF3jtYnQ17Vlj+U9prQyAvJUfx6PhkrEwJba/DTDdNWTuYNhvvTJlhHJduEp3cxu2P2uFOkGFVv+qJhEOOz+sGLFJvJkcXIo3R31oB6bqAL+GNe9Pr0V2Pi454Pe84/TaHF/vYOu9Y8n8/dZ4fQQxox+kJWXDtEJ14Cbm75/BbPOcX1LAqIQYa1VPk6Zel2TbdNfqT3DeWzXJA3REppNDCJSIVY+1r/Mr14jEd2eLqyHt75yL95hiiN0848QMKJjoiK0abeYNoTdKsYTuw96db/FX6LQ0z6dq7H7/sc9TfNHnexpkJK69dw1afvP3APXJDuSMFNLZPptDhf37FEfVLTAyygxtqqkiBzlfdxWYgzneheg0wVp5Gk/Zsuum+qkNdnx9O4NvbI9VJ31W4da1fbxoq5A6kdcCTr15T/Pd0hH5A9KkMPWsWqjMwUDFs01YLzit+7jmTd33WkWMlHo8qzVaw+bYO9KrJ7ufjpRaOvU9N1qG01uaZs1ojKDWpevJhpWWRLp7PntPe9/pV45O313ZkUTZCNLw5YeB5jMFfWsHx/w0mXyNg8Sk32MtuU0xfW7239qg29EaAYMhzPc/VokOqNgt45ScrAZ5W/jRdi+gQudqrS//iI01DfnFQXSGuQYs4PauXKVqD0IUdYa8vpc2rL4jbPbUpnatdANf/FzNedVmciKsmf1LT3QxSc/hwO2Pacit3MEQ7Sv4mwadjAZQFt23HLCyEbToWhXWH88RRL6PDi+eU306ZKnTvQH6Ls5N0zVMozDQ+9Mcf1G88jcWjU0j62fLBhS8c9X9allqczQPEvrrF/2svvz9vI8w8YKG+bHlp19bMpMVnH97vdj7OjCWSMH4Ekcti5JogK5fA4S5QX+XaHdd1xqJBDA9MVSikAAOhZNLuS91G0Fs58/UhZOxkXL6uXSrXPrU8lNurY+Qcz0W9qgd+DswFEIh2HobYp+8YE458vofNr3U/aqdMyIXNq7zlcqrQWXsp8BKKfhasJSaPfZEjqmFNZC6dGhj/e5c+9xrZsai262sD4KD34N9q9VncuywHPzy0e+Y4QALkTfaN/3NvVM/huKC4KpAHTcm7xBpjvpYAS8LgRn4epmYWyi0SAvH8vk9N7AeR0CodoJQ1lXdqn6YR1dADz+Xe06lCvZx2vyIQLDLv2/MQWlBA+0o9cIP0+y2yfTiJWJnrP1pLNUIRvDVs2FpHs7VAYPd4E7e6eQ5+h/b7IjXODL6RhfUks1fpqo0tZ6C4gY6ehQmzoj0g+nRm3xQs1bSKPp/afHuvbBgM1fpmThQTXqOOtGkjqCxJexLfVl/aIGd2duVefGyvuMOImylPBu/aOtD6cD9AQ+R6dlMWi++5N1+6Qsx/CPm8VYS/E7Xb5HfFu9RvOw+fouJo7v/lM61LBS2Rchv2EMv21v/bJgmquZVP3i/Jb20fAd5H+ncGvfBQNSr3pnj9ests63kO+gecpPnyUjK5pFSMWZM4T3udMszswd55qyf3H7dIBycdK0RlHKJzEfea+TU/X9sUOUfeTM3vt1hxHLzO2Mf3TdHHxdlZLFpmhvW7gcDIXO8AbG1/8IO5OFmbn3t/HCDeM5SqjV3cu9ZUVk2vzqeYF1upMts5jZDcwJvhDiPl21vRP8vVKgBFsgVZcJNbMpkvORUt3k7/7mC/LW3M/XrhIZb8oS7w7ev87xzZOrfNQELhF9DgP5Yj25jnM7pwNM4vEZhDuYJMp79bLUTOpaBGR1XfRoz4HA5stdmj2pFEgi31Zxxx2VypNCSZ4B5UNX2dT0tpbNet+pJFz52QU3AdKreGURfS1U/SMB0NWrYI92C/qYvGTcueF27v5zS66J5CIGdTkOr5o1glfafaL8Latuk93v32Q6K8efCkLWm/B6XY2Izzsvvm+759fa1kVvZN6o1B7zvJG4O1gcvJIEMC+Xm+ITsp9ZJE/KUmm8beycXOUC36eYN42ZKP2hakr/oso7q/VRGbkrW2Y0JS6uPImxyPNa2gfXKBEePggehdvrJ78BS7kGBVnYxExULTPmze3kwQtXiNacCt0yh2MzgWvbfCxQjGK+JP27GSWbdflPkSUc6X7cvT5Bm/GsPX9O1oik0P0waN/7T7F7BMhgubb7iXVBgua7gDXuFt5PD2P6j1larMCPrMd9HS6y7VebSByh4pFf1Q/1yIxe1aHtBaNYY6/gFnZ9eFCchJ6uFfNcu+OGblM5rGDXa4glQrubJ6X5Ps3WEwIeHKHRDZRZ3xzxv3rEtfGsQRfAunaXurv79eGXJCLmL1FwJcpMAk7Cd9Ar9A9u0wPF+qXg0x1d3BUujPU5doNek5BF/cHzRSzZ/4GSEKKKq4rb4vomhnS38lrBxblYoO7W/+Rn+ExHLbKb6jZBPdKh9yCi3D95K7zLsGk1n0fS2mfxffTVzJXlc6P8NbidqT+1vf9qUycVwi5/X8vmfCobBAzXbICDvSY/+ZdOx3bUl35j9rnbfkpX/TX+ROi8F46vG+XA17F/YiU37XLlySbp/IL2FTOf7zGFHN5HoEzX2SAW72zChpD134D2+SK5KYzyNA7GbMzhRXwXV8A1uSezSyfjymJfIUErdPNOL770Q6OO15jF+CdpRq9UBfr175w5qITrDpbWS1RC2SufiVjFJQ7DAqyfjWW16tZ8Uq9tXchzn6cZ61QE2j/+bMrqrSg7iyf2Jb6cS8WxeKWxT9GL1RpYoR3L2cT1BZPdUGCXZx3sL/u+gcE1tEC/c7X5a3LF47VjlQUQ72vajT8AiehoD1bg3lsFlL/eLYGEPrxq8WdIaGbPY6qxuiWfUdrRNSWEJVZTuqcSemCqj+y1EMmqeXjii1uVbe+C6K1W8LXAswtmAS6G+uWkp006pY6p0donIZXli6ZbJXhOt9edJrr67lW9VOw7LZxCrZoYfs6550zUC9RCj2vANzDWpdT4/aLYTOdrdF1UIv0GRU89ZVWkLUjkImV/11Y5U8TCLjM4A/xkOyAydJySvcC4xY/N+1CHyuhhvK9FvuSb4ntj7kl/70vrE9/UxfBXe6S3YayDvmvBzxBP2MebkqT0RXPrtYhHflgHeRozFEBgUBwp2kVJYwS17BmDqMbP9q6q25AnOzx2t6LiEVD/1T8mLC5Zw1zbNejcZukzUBsZpnsQ0TqqgJI1mTAjNjRMW/kSwVOVsWsM9Hhxf8e5oJK42+SH71EakuKJuzHzzfxKfSnRheDuCRYb2kRLqZsxGgmqJ/qSxfC1688GjTqQX8q4TpPPtn9MTvbkx9m24UoB8L7eFkpqbLxTM5f32j3L6EVpluFFC6e/OY6LABk6xQKNsbeI6je/k7zEvgWyutVBuDxCPph/32Jr2tYrmiNZuK/Mbwl3g95aWH3lt7Oru9U8/wPc6EDCJXOi0cgjw6xOGheBJwvDBNSI04uH50af+15kMjHSz3S/DGaLTf/JbzdLpFNVbD2b3uTIBfmMQGn5l8HAS97VFtww67GYkkdLI+3Za3LrzT6EaXRa1MKLhc/RuSOxSRtxn6VP6ixAotYn0fDklsF5FQkve249JJvHfvUd6f+YnfIssc8nbaW1W1/PJkDpPSVzKV26h7RkCAvXDdPNq/H91bxMEsApRPGzk2h/fvAp8hh24qXTbs0G/v4VW98+6lM8m6Oj9yMYc386k2DkZD3+aBwYhNsTdrv8KBWvvv86eW1UcbP4HIGLcXOT8THTHLngDqFZjnJoRYQsS2eLqr/2TkPabNjt+mfjwG8N6I2R25xr5ocl2k9faZlMrQ8x26xJ6CJbCkaQ1j5fmbFtlXcKqnjpN+7/eeVE2wT7tAvdtkQ4J+ygfnQdS/+jsNuVA8egu0PGLkvc/0Y/yzKvaifzDYtbqfy2ZsoOw86h6jcNZnR79ilX7/OrF1r0LqVfS3BQ3ROWJReqXqUnoyd6AolaYdbRsDsNjQOnwr530MduMwwVw4TOQlFKHOMwMui6cmTfS9DF8gBpo6FK+KzWGjn33VIJINJbUt2PjFaqJm7N6wP8HlGIq7qbe/0m7fes14atmlQrsIbHpklcr9uPwPc1V9MOhjDA2btLePJNq4l2jfXT6Hxcj8PG+8cCCpNg2cAuWNfiL6pogmLhlaeve2ZrjzeK/mgIX5d9Vt0SOKk2FXXzjLCHNb7rAqqEnqWHSfZncA+vhv740vp27Qq93Rfrlf4Kv8Oo7zHyJZzfDRdfzcbYkXK/5JqJwlKD5sTbMdIIs/0EUThAG3Bd7Ja2OWBuI3OL+oNhHcLPyH+b1Nt5eg/IAqOwxZyCf+vlJTM8jhcdtMA74vy1EEOrOvhMF4p0pNcTqOvqqxiFZ/+Ba+nhuJTyFVl6wD8HG7C/PONVb2vlSkZ67+OsM7fZgieV+RekricdhQ3hx5UTa8YCDNooRMqePM+jX2JD9gO9gkSrrnD1Xdbc3arwwNj20liFLoU8y+/s3U//V5jv3tndVrW1/Pl2fczz3Ap4I7nK4kn8+myiDhjWHATnbamMbzl1WawDvf/gnrLSvb0eVmKryA+aHTuGV40q5CdhaiFbLl4/HBH9JTpb5xsvnzOlbIYZ2ALevw+dmc+btm6xJ9eef2r2F8wjZhXt56K0ux9cE3BubIc8dRf+0qXD/6RvSJ1s4PYieWwZpPOacyhPZAstw+UlNHWlRsXVV5P2BFaGTWQuA4L6+bPrYnjkoUqnDf95cpNddrc9H+4VpK2vF8k5/1i4i2hWP9Spq4WE/SqLzuzKwK06H8P+9euUzrvPIMQiR/mnLPFqhljyVjI0uriiN1FoSUqkR7kKY9JoTWblUXLo3i9oUW0cbK5b03FGc5QyY4Fd22gT2eXZjYXw/U1t4u491+4OTNQouDWX6qiN+oXKccvoHMKg26aBfTkOwh3M5dk+oAqoh2y9fdoirSHn21/46nNt4iqtENy+v4JXAGG+hqVnQxZkapeiFhDf0HWmre3pqzqdBDjfbAE5V7ibP1xod4fJ/PSo7UTHcVc/xUitg3VX2ezubwRfHH792BypKfkptbBs94XRE/loetTvlwtjVSk6vtIxTuRpBDGPN9ehIGxKhjht2kW0WWyr9aXZRz93L32M6mnG0/pTS4ca3IWNxGdBDFb3+uaUU8LK3ldKmY6pfJdyZdyexYxc/K5BbkODmzCBo3nD79LIRogifkEw7FRVsKdWG6n299UrYOH/b8r/TJ3/8oZ99mWNvxkK1a4d1mJwn6m1X/2W8YCPqu0+QzF/ibzpGrH3LT+PflMy799vgfMrJzn3L3tpZwIdnf0tuPPnJd1q8MLffYvhlCWq3BGp3ZSyis5e9kiXZTMWNg9KjbRBQHgrvoKJ0crjHqHlqUw3XBTi9bMaHswdx7sL0j3xOuUg7MypFDXJzWDyi3P/CDpaX7/bhlji26rh/kY458CVHJ96x6b95dKQjEhgmpQ9fmOb//ldg4n58Li3ttwew2tE7l7lNv6wqSEbCy7EgbMKHxLl2ZMfT9x8TsnrO9vbCVkr70d8nuNnh75fR/rBupWiixZt10owfR/r8Ye9ukV8zIqaTjMZ8KxxVtH4Kk6meeqizqbQT0ZEeT8OaWZHjqaMvI664Rc/iAX+nSNP4KOSOZMpNZ0SztgyseFuHMHWC297FGsqzFbWqYZKlkvdzFOaGh/ZkbcrwPlghTufQaZPlxGXfPf2oUKfuwcoT4ll1z3LCKYerLalQtlEo9+d8QJJBKw8ebomSSZKujB2AW3JTsoF9qoMe2eU6OMXWPez/fmJwNxXZqnGxiRHrNWUv2v8G7EXqwB8mlWWx/fKxm6gD1n/MGyxPOLGefJym9uAy3E2NJ6CHHr+BvK6gLQ7FnbfPTZ5svlFlfgOtkZAjGpKtaIGPp6Z78hmTObFQ9AMhW+ts0VM6PemGnW6AYtQKlfkjQf5MU3qU48F1Unui/bAy4h9s6CmoE5KyAH8MIDFzhD+Lb0/nki/t37hm+LK3M2jBmrCkHQIS52T1LL16IMr0EFW9wf6nS8IfnJQoCBrp4h2OOxJhX/NO157+soQPn6qCV435b7DuJuDbAQC4mw+TFvi5+b9OZYEyGvRN9FrpPMXV3U+VcT1HjHRII1WW7jQkX8x2JXALQaOp3a0RZvW823S3vv/GU/dcw/2P9sFVaHg5/LZOAh0xfer7cH/ekKSKzpUPwQBADN4clwHxjtsMu1PgWMvaKqH+OnCxG9B34dQsL5YlxrzY7x3QZNzhRwA1lp1TRqhk8kjff0Fn8olEZMVL1pJaSXFcIJ+HFcwg+rKwVAfXbMhVbN+EawPwrPqOIqd0XwTFUPebGVv7BCtDlhkltwzvD5xPeXSV5BDzRfkPoH882028TeojIWl9b3owq9Uj6NRvmkv+08l1JUa4Z1ZU9nkwvEqnNA1KKW0WT3k1h6bMUYHWdkcw+cTjg43ScGstr2yU5y/+bdo7hO2rkoTsKnC/v7tvqiyLfrSp4r81DUy2Wod9e5/RA/jlpx0/w9v1iO6u2RlMsmOyhSVMWpOX4DwPtjvVhlsFZY5z0UTHvnDKjD4/iz38m3Y3odHpbV3HFXt5HtnhuqjNNp79na/cY1O5qLBCOr7WHNiQ+cuWzvhfLWM5FptskF8gx5e0yodNvnl2S49f5UZPneD/VvvsM6Z+BmZrMKHtnuafSH+Xet9QwZJDJQYvsfBv9bcfpNxOCbABNTjqPCQSpPBKW01CnYYeQfQBhveD5r08zL8aV33yJMIryUx3nmxMLArfa1MtMZ7Wj1JmkHWPxOaVWXki+lmrK1H8137o/Jsj19a/He0QkCoZbsA77Cy5U2dGf6SAlTw28F6pm9yqN0M/1vdMqjdwLD0u6cH+ZigWEbJ7E3/vl6htpd4yjcSq7HJn6yUieOlI04pVSL6CU7w8XLQowN/kbtp1twN7AbBzXM9Jq7Z3i6zv15lDM/08gXVfFFAUsttlJKGn40wjfqp4fsD+Gu79jqXNR+/HxMjqXcgobovc16+jqnL5uom/fnHns1ixOqLdE9vWTp+pgD9DZQj3wsw8RkcD+CZ79TEwAxMTKdu4S3uhg284GltdIGG1bf79l883kvH6mCom04qhYJRvq6+KoLL9cMF6YHevlYVGCoaaysZhJhUKvOlJY3/2stTOkv6jIij+rKYtFLxn2ZdQNzlvYq2iZjO0mwIWmAS5J0h4O6n8Nvi2+c8r23ULbX9HxD91Aee6Q1Ns156wTkrp/jwM/kSNK6g7zhPFe/0F504W9mgUCTjTxFl0/0qWz9cRN8tMumhYyxrMSo8t1sXefAvBvk3a8QdW0KctkfpG41OOwoabf+mDNQgaJN96bSSAGnPL4P0TADOUAPComZ95nC1vcODQNVWft8ymPdCCFYPP5c76vu60ipgYpmWqv2/FI+9cBMYoV/6lR67zYXpvjsdmmMtK1cUOTLGzXPtKcsAEp3jWWPrZ9ZDJAAdDK10VluHn84FeJyiUpuTcWHvFotLpHubxKtTJ5FTAanydT9ufXId0cSEt03rlXRuDkEq97Q8EtR2cBkuxKF7WOsxzOvV+yT2eWn5XSZhJ+U1BYxEEPpkIogWiEwjX4MbOZy8Zm1yqVaKJ2suQNJbO3N9aa0ftY55d1ngE1vD3db4lvWoxIyoYluuBBG4NzWxqb101bkBa64CXKjofiyiv+NIGtPm7hZxWXl6pvdJA0bXP2kdtqkEEQ+zDo2BxxTov0xnKuerd8qkz6/9mTtBAcN5fqf17iB6UT/mLTFuApzO0g+XwL19e/zXX3JfrjgQVryOIgfoG7vs7y1LZvt/L3byl6UWxzWxIzk+QOiQiOinNlyIOWCGALNastwCrmbZdb0nOdu/zUBHqhl47F+x2fpVrtpnIS4XUevRhfhVxL7TDNFcOPSSqJb74aycBbq4GxgwmsIlbP0L/VPvNDfY6QQ3ZwkiGs/Azc4b5jFr1YxYvXfEo6tOWq6GNFLhTiXnJBFnGmmVbDSSUXaPhaP50V2V4b8rrCQ1/uV9D9lgu4vl3v9vbx/eKsw5WGetwSvwq8dBj6Ua2qAv09AeN8Lb0meAkVrudlNWf01TP3el34aWxZHbHdTgeTL/SSIZ2oXuJa/+SL3857NTHaNJ2din+FmE0Pf9eatnliYJwxKrlwhfGBTs7t7zIu3EaF1GM4KJHqo4iDcwV7Y1A5XNQhl0CS6h/svJd2XvlQTh/5F/c6IiBhMPaoy52RpNeFup0kv0KZd9QB1NTCjInMnn1nRg/pLvV1yZiEiPaK0/W1Yjy0TFeeAjhszDCU5924UjN5S4v2NRrok1fTx/u489uAcd8E6aipN+ZwV5DatScY2AXMuvmDVOe4/PislggLG8PdfwE9VOuMer4OQ3mTEn9dmbfAjzwFjyLXgfd9j3gTFvZePBjbF9FqEtZYaZLMLm2c2+1B076gt8J8OHZuaQqa0EjSMTf+19sjtLjeliML30vvsuqMql0skbN3wYWCz9kpVH9ZROMpw2evC/QujOS3gizircjojFMs5Zt9dMAzhWDqbVhrIacDLRX0bbFBil38SEHkPupssDY7bB3pX+Wdvfq9IOBh4sMjs7FCWfYQChVLkc++0fH1mK3km38ivHuYTjqmqk6CItGSrNHbmdttYtmRJFBYptvJwaYwj1Cg5kMFpnO2SRFXxr2i8x40oLxRJajDUKFWGco8nPkdJaMVckraFX/8aeky+xr+IjqxvuwUQ14tZkWGh1PDSJ6oi9nDakoqlIRxOuAguDyPJ7xjt3zksbNRVub/4VmLeA/jzqGS64D9sVHXG3sOFRzrXH4dHZQSdQ/dOIT8awCvfFUuzZ+XmkZkZP0MgV+OzPE41m28HzbqcLzOf7Mc3VL8p9/CxG66UdzEEiNSkXKP37QYn4Su/uoS+hBbO3/eHLr+igv2tKxoXl8Lrr+29XEhU3Vvf8njJbVV88dx1CSZ9bJOHcwIdCrltwUzmoQ8GBv18IUeXXk7V187L326TLLS1dpf2I4xxKbMqAxVVtEuD1UaSPdyM6t6s1MSlkmxYdlA0EgudSheWUDEZPyakrXZb7WjsGMY1PcmAifj+8inwVtc0GmT0sfjHAcyWpTNKs0kjPXp2GY4p4Nv+4lWP5KEqD7KGl2Z3X4YKgOtUevoGIuA8zyjE1TWmin5ZLQTFZo4dojwY8lBNE0ajPK63UN7Soa+/k5pX7xP9bg2Ilup+NmhbmfsNpqC5+PZlVhcfN3JLYh7kFzT1/4L+YCTsgFdhVgiFNxwY4Pvu27p1FQZ6cI7p3YXxJoEZ0Psx6acQ9+PzbmJRC+WRVKxPifbh0p+NWDheyDamhmm/6sF/BelwdU1PBacAtTfbbeyOWYnjoC4Y8VjiCUc4IsVPm9Le65Hx75yASsJpUnYk1pGf+V+Vq/DU962Bv2ztoxwuJb3WH3vyEKrLoc47GHlZCIA+nxU5b5Fcv1Vthnx4LMztZR9bgJUYbHvC3kpVW8pMaSTAztb5/AimTqfg00Zg/C5h00+FJkaaR6Ad3XqZlpYcpxglINZkyhWfIxzDCWhLYhfPfLzUSGwv78R6tY9GoZPS7mv+GfpfzxbDYTbEpxX2jsRNH9lm+BuMo5rCfKQ45X6WMWhmirV6dewxkdA58G12VvqbUSONUaKJkpXzENwBFzlwoSrG0NcFG+DajrgC3edocUEFlCdS/5KfI3+MvWVey0olEj77ixfI8dWNzOkT7ngfKwk1ERRJh1K7S5jpHT1EdRxdFxGeFLscpIJ9hu/+5TstzKwz1e3pZGiVuUC2BGHMtLhA/Z3eB+bAY2cOU30hhrudQ9HH30NNzeqGyFV+iiVmYmRJWiisglqSywVIFIup7dN1FfEG8vBDNdR8SpbLcertzXh6se084lLHhrZrvLeqBJsO2lDYfBIKDpLkKUU5LEiSgvRYqYZcxhkooVoeg1Ost3bXS0crSN1yRfiGF4DaEYWKb4ySKkr1FPiELjCpNzrxmHPMH88X98ZRFq7jF7/mYTBF5VTNSpQtc9Z1P3jKz6Z581h7mWN5xjV3/i00clbIhlrogUprdQ+ImxIOW32iu3HCYmuD0WqK5Vgb73vcDImzrehy4ZpcLJA3XC7AXxdjymncxJUZLXxm1JL62doCHR9f3luZ3Gy8Fx7h+SIusR89lkf0tRRgkbgE62ypNFc/ji1w57hF2eRlSL9zXcjV8mxMOLp1YcCX96kd49kXXNn+6z3uBH112SQLbBBTT2wSy3lcGQ14CcK7rxmdH2LRkwyragGXXDf4HLl5uiueiSCXmqPXbLXQDtUzDmc8s321H7f5gC4XTsB0I2sM9PQ4CcUOZ71Xc4erF7Tt+tRY2OP0r+TPXM3OntvGfvWN/HTF7auZih23QUfVNx7HXpQLzf6BBbmnrrsmRpl0Uf3Y788mUV0ClCUpVbjY1HnokZU6eHmKqLgmVHXabzBN8xT1/vpxfPbvH/hFrNqNE1I0Fm2VHh3qT44nTaka2fhqq73eOFWWx7f2E2TCW+KM6Kp5HcIHkuUz2jZUnj31F8eLPl6kfPYulR04K36Lpky3ij/zzXBPEn5C8ZQuMXq4poeI+1lO1D4Gm0pPYJtsJ8TrTKPwlNFWmJP93JbjqgYMhQXFDoJtlI8OTHtxnHI5Vl11toix56w4PXzbKT749yTi6oK+OHcfMux/YIxnGiYpmyHytReETgaZN+Re/vPbw6dZjK83ZdvO2Pvib3maPzvByTLeo2g+UN/U7Ig/lafiHhxyMrns8ma26dwBa8J7uS2OBkrIxhqsBx3tQ6YIi6cxuHiNEID1QiBq32UWcQo42hahbtu4LfUi4PrBdYv8JhZyn/657dSKuS1mZ85rK2yJqL/9B1C055wQGePCrBkFuXDj+Hr/EM5pzRlL9xNV/ug93eHJVdOvNB3+IFYj2CWeQq/i30RDnhFgaQVjF/UJt9yQgjZLMeXW1WQXNXTLdUmj5GUqT2HixlpXP8vOLmtm3i053UcDOcyo/8OXcpmsJ9N0JNnKQABwV+4Zq5YYQHzlIFI2bZQnI+L4IMlLaL38Fr2FAAtTT//i3pPaxp4mCQ1yE0u3/EtalXu0i90q06qDDjwANdMuEPRrsthWMmWGb11MG54pI4YwJuZoD30ogNKAhuvusJR40x+akA2Rpmx/pOEV/P1V1JkZ9SNvkz2OjYno253iHS4EBcGAtcnpGBb/8SsxBCSvdp4ijQ6aQljf48s7s35gf4HW5Uf3dqCmQWZ+OXTpDvITSW+6+VOVybkHbude5N8Mq+f5N9ncX/FIfg5y6s24vvAq/AEdr/eDt74+E0sR8InW5min71CrJXHE1t3QA3CiiqT+lPnLO7JYFvI8xPtlS9ChmE79yznjSdeeP+h982kWF+4+is/K4UotooPlDzGxFM+Ybi7tlTcxQaMupn2ZGCj1ghhkpKZgxp+dnokqxJnJlY/NWdbN/4eiXGLuiw5V1pn7i2sUzgQVmmlB7i+LynvrXfgnqsuTdnCePhGTJ3eqQG/fUxLOo74c5voJ/asGbxHGyLWQvTim9LrQdCwVkzGE4aEPim0mYrK8RrnNodY5mk7aZnErInSypXoyvb3d5YM0DOndtQikgdKc6RIv8uqnAzCbKHSD9mkJeGjD82rVqm6Zsg89WZZp3LwwPQWj5kElzxFxaplnIKDUFQefPT7zNic4MalrDEI61fOM8DXMoa7DqxOMzYH7i4PBZ+dHBC3sh6aIlcFH6NxrkDbQyKlB8b/EKhNLj/J8mMw0MVva/eyYiKNO4ciZEVDM7ZWGa4vI5RtIiLpXVUNODPPMfewoGeFzLEtaajf7gm+cPwaX2B3xZfWFbEPWMWrhUmUM1adzR1tefq0z5iu1Ve/IPLhzOhhm7H8cZx6d7ZBmJPNikyYLtsRy7k1S0eVxZm3zm+6VyiufNvnbVcvUddPFW09yGVZQR/r5PtWEv0d6CZWZD52ZoZE2sMllAp7OlqIoAdUJpKJmoQH2B0WmVl4GZQ6HvoFVUnnGF5+X/JuPidlL9gFGLkPkE0lgnfhT0u2n+sf07lrU6B3nHtx9/+O2SDOFvHFxGDbKO2t7ZnKYVTzv747NX+Yv5WYSGrTQgwWvYF6xDI35u/lPJLUok6N/NtfJ4rj3p+t59KBd03Y5Uzvq4W2CYdwiex1Nmq1zXy2zdiNB/j020pvMkQObXz+f+LGZl1pfmDroOr2evt7864eQBa74eEYE9jVuQYAyKpnL5WmQdXxgt5Z4rsdWuDZCY/mqzFvboGgi/8A9veFFRk6vGtl64VtdWQxLm7SpIHDl7znmy9eP9ij3uxyO4i8qElrJtq9BWvaUVHytIXBBUp6Xt+bljsozl47NCbiHARgdV/KvbZanJP6XqK1THNpA/R3bwhHuwrQAZ93OyKuKFQpxRyxyd6USXCb/WZd3c2JYsecYvudVDPyV+/bBPdc3fVTTkXkjmTYPVOZURuhKeJI/92oKwFt/JIQVCMRcPepJIKhcS22oGrMq+AO4y+lzEv/vvJxD6KUsPv6nFHYkrhMM/Ppi+fy8eZFeA7pItCV6beJJ0Z34K3e2wx6nKk/e7yeeie67ZVGF9tmnYJmJ/LtPrb71Kg2bnB9zbCXeHCpwHZ9X/vjWTxis95XRXKU7Hk4ltgfTRGS4sjZl8XX37pBNfXNztACFxpWe5vJRddHU4kavpicVeTcNqpJCJdig1I5/44AsR6v1Q5bSglTY3Fm7floA/DhXyg2RedMsO/hfytte7q1P3HLtN5yRbifUNOcvKy8z895JkTfwM7s4NiA1dzyVq9lg3a+yxu3VZpM+k+zb/L7mYa/lNUCz/o0G8AnPTXmkcG7aVe8asi4S2995O5JXZz4iXP7mafKpi4hH4IFUUYX5W3znU8QTo61Xm4wy5IMTNxcKAteutaUpnVVMtV+PovfE+cEpyKCI8C4bQkOu/IxaW+JNmCBW+n2AYnJhMbCekuYWHi71Pwpo/jn4+VNTnhPgngg+V2haKpb3/tSI96uz9w4K1SqPbHqTL6RasUNhejzbhphj30iRGGGuPf1Jx8zuTYdgpNkN7uOhI9UkRt9FyDC8Il+jQ4v8ZhuW4j91+K1uRj1Gdk8Qn/+vmPBzaaYpb9ITjxDTHXr0y+wUQVevFiaqsIWHvn5zfk33TOw2TmLLtXhcLH4R7U+vp5Ke7gqDXQ7YbFrypCeu06uNUL+Tn7V59q7BSAYZNa2bp6ikxisPAuKhnDH4nwyKn1ges/Vkm7NIn8EVNufheQK7eTtghZC92yCjKMf0dTGcUww5d+gU7Le63hT+X/EXNnGhCxfrlk5JonM2IX7byH1UJ5/g020fAtC2Q6cbsCj48bC+zTPv1LQ2ACRwPPTNY51U7w6K0g97lA3zs/bDFR1Yt/MVbTImjvL0Bj7yGPzPsvJx6wDXyfrkxSN6jr7zjcn0NaNihTTWsqeI2/8/uJ6a0TK0tLDT6vGsU8Wa+s6/34H3+Nn+vuCtqn7nd+PEnvEhdynfHGTTHWSHBXhME+jZRS70p+5Zw66+VVeJlJuKvToh/PD0/ZxUxCTGEokKvyn9xt/L+Qm6tt0cLPumLOV6/XkI+1loxS6UPZ3P7nyQDsfC+EA8Ixn5mhNZU+Cw0ZlZXoToFlpapiuYInXJQ/tlNDimlmT+uo0G5hefi6qQTCycoRd+cHkvNtgbTN3tNbC7b2XwSLetKcXLZV9s4v9fDCcxAWk3L4yZttu0SqyDUAL8RpZCid/LGVB/YUJo4uzlz2vOP9LrmL/fJJFLmrr8USvla3eCfPKwhUyGralFHBRlMPfvFYR+cRt3HDCzCHRQ6Ot9iiXtbb4cjtYj7f7nnaCMImTW0XRr1eO+CVi67bJr6AXT4f7DOR9C6Ryqvwsxnjw3kdTMMDB3YhR62FYt85/my1efakvXcI9cJQGe5RYsJIf/NWLxvZ0EhXaU+9sujyumy8wx+uVU9gRWGhkgHXWIgi4sgCiedbS3B0YymOx8IS/SfAobb3nwBL0QM67K9buANZ6lQOxW0M1Erq7SxTZx9c96mMthOCDZJTblmJfOfrQUFIZ4pbiMRx/rMDeEGOqIv/nqlAOP1nP8qn2D8MlNHjfwtuDNUoztOFkpx+gels9i7FGpfL1v2oabmekekh34hc7QvbLYIfoKJKqii6gTEvsAaUyLilbcY0zzI2DHuNkA0J6pvWq8JxKmgKGq6Ya2NxN+J+1ljbRwirS4ORFuaY7g83Phs7UA5nvBs1Gh/vfs1+q/N2Nwu6w+zwzWsePfucRAEqBL6e0NX3SWfP0H2tepm/8viCpkSza4MI6lZzlSXZWbOp/28wfWxb83lC5dbp2+dYYckxQ2b7jLf0IKy4esJAVCD/F7zF8mxGOf7cGvbKuKWtzES3v6jF72cLeOR0pAwScMOmsrtKWx2Z7fXL9gVie6aqMFfWg0L6dER44d1wQhPjv/ZS17AIJn94O5lt6xR2UHjXv/H9ObDI/mGE8x9xCdPSaD/GPji1a8963qK3U/HhwDMalUBluBbmP+j+tVbajAu3DXQfT7JpVc18odRbz7z9WMQbHd6cVpm9foW1cOdTpjFLXzef+omOPKD4wyKPtJlyVqAXIV0y+uu9ukI1bVqtVgivQp7OjAXsHpw3wuVI7dxTWiZnQhP/9x9iixNUvHueLgkHz+GkM+oVDuv46RMv+zZDQ3ggLHEI/efa8QPk7v/vzfs1b7ezdzb1MkhX0vX3nK+kh836X5LwIXPgVLX5c2vO/Bx2CJLpmnTCqJ73gBxRmx4iK31BTbVXf9WdzgEyaPx5aK6pbnC49FkZZQPutuYZcbdnU335xe9BhjgyfZoS1R4OQ1v8EKUsqoyLktXO3mJ8yhUlNH88No1gekFwLbHnQyo/nNvTTW5KDobADnwfYr1c5XZLf4Mvka1G57ckXq3y3m/om3cj/wsnM5W6UcY+pYD79Dq2Kqi8f716jhZgjr+UgWl9EP1Ej0k3zZbL9nRaQTB87dOnTb2jgtiTFnZXkqi0ON8RNzp9atN5iF2W1JviP8K2nkys4Cf8r5J9/eWOdj8aG4GVIi0kwxDKeF0Twk74MkM149BuOzQqOWg9XmFY9mH81LSdArAevajuDAM0jntcwfAdSGpnRv3i+EKef/NdIdbdDV81X2Hx6aYhhHx2zmt2rI7oxOnD/t1NXD3zj6J7oNsU/NTt6FvXct9gSXVh7P91X9VLvfstrYie5Ju10YyUUZikoGsbXHvkfX3S8dTiwUHczL4Q/hbyO94UHNWFdN8O3gRdXq9zvCpEdepLKlGsUJlrAOJCoKr3gK6/NTLxXn5Lw72+Tgt+TJ4cmyV31p7u1LmEUF6H2Ruqd3GT3GUIYWqzlEz/RzI4ga+gce+Te3O4r2HYoZ9PFTJQEduvzLuLoLS5ZlLP5NV5pw/x9R9EyUMGwOA+iwWRPTOeReuQF82HSrbnEH/5hQzXiWs+8Ghr6ouffpfmu4EjIbIXpO8UiUmr+QQ6CrSDVk7PCvauyEale31e8eOq+/nv0euYEVFSom8+4LJfstguH7GN6XuWFv8gB0emzu8jyU/6Naz3Ho443PArqmon53bDXS2ImHiZ/65zo612KpfDpamPzFLzEmWVta+GFDHOJff1FGtams3ZX0or5gpoNET7aKDvsF51t8X/AyJIvO32QIfHlY3sf2aPmt1CoZ0pEcdctuUCXhQ8VWyP5zNZRYX2McUtGbwhJnG/EcADjdprIbix9QBvwvIaVZdRcWrvFuqiyY041+PRk+/fqF+jFTzJmWQFUcU9lf5nT8vMVUWRWefbQUFsLsAcFeOzbQGzkovfHGOujzxLhLIUOKOJ+c/47ESoBSjC1gOudey+RuZnJgRfs2+h+iPezrpjaVNGtHxd5TeU9cRkTnDYEq/1nmF/bxJW7kfxnfFkb5Q+3a0qLIf7paETUMykao+sf7+eOPGUqkis7b6LEbAwAzOcx8c/fVONdl386QwNr5i+rqljHsdD/S92Ow2eY/uNyLvQ62o/VAN4PDq7XH1rCX15sn9VYmCzfRamlG7+H9XfYvK5/MWN4FzKxtdsKp+mhh0PRyTgeL9RI0bWsnAgkp/sc6N5HB62iQ+DId02za30jsMgTyLufeT7v54ZnbEKc7+czhGNFrMhUVanSb5SWjC2VZPxBikpOXkQ0jD/0XFnVaQ7+a/zHun8TlxGOfrmx3Ru1s+OuD/+adqKCBlSNj2CArJt9qCE0kEL/frbfct7mG6RiwSIuGuU+XKb+q6i0nnSw8/HSem2X5Q2vOs9BbieV7Rhh1ldJzcAhD83W7ubqfI32AfnbzRt3XMS7yJ/W/UEX9+zwPYoLUDjRItW6tL0kqD2ErtMlUhqs95mvSS+IEw0ERA6f5XQ6hv51sxrZvYD6daRcnZIQ2Ybd1f80odv8dygkPaqsqdyT/qUBJcOfmjzvcLd6LRoGP5tgoLrvlLGCYQvyCy4yh6vbVkfLdEN0+qG0Uk+p9Iut20z7snwxWA4L1vzjw9fzx2020SXtjcT1ltLDLv16ZSHKvgL2RsgLHiX0jMztorUf4aRWSXozVs/WhsDqIxrFB+434i1+8zKdcf3BtPPur1T9r0nd9AeRFOHc5fxRBKH9zNmiUwjqKex0Dh71mWZ9yzkc6HAbxnP68zbJsHAf5m7AdHcx2yYrMOOdGNztTm9PnkpV37rPv/3TfNleBPo6TkvnEyeehu/3SkoHcyJSG6wcwCuOPOUbozmVQ+LkHn+JMctvyIVuuso6Pp/bkaWZwshS7ejfgSj+Jd8o81hVfX5YVp76pKj7eeeGUqCejazXhjV8MoE/3Q0NAPxL2bEtWuIsN3x2H7+4NtA16ncgbCw3aPJL47GcJ+e5ocN330h3ejSWxTt7sFcMz5SVLhAI2WHi45ST6xm5HUEfjB+aLDB3zmj3Q0p18FbZ2OTf8vNO5JK1xDavVjcbJd15xPD6b3zfzlcnJqPhh7xZqZsoeIROj4Iw4X0ZLHD0wlksphjFC2QACayToJRrJfWgIp3FmOz0NUj0g+8UMGeIlA1ddE9WYgkgmf5zmsmR75FhrDsL11L3OgX9viNv8EXir3fojK6dsMKVq6fqfNhPnd06zL7BE6bOzMI9lVapAPFk68pmVA8mq98j4VuLt081flX1v8Viu3RjNZq/mayPlOmu4P/KAa+n7lPjzhi70JYjaCLhRrvlt82/P3Q8LeVQX1L3zyjbZDFIaub6ss9oG9RNBvy/FdW1ySXoq+EXCXyj+nINT0qu0w0pnudxD0AY86W+WjzBarpLwaEjapQf6ohERbPLnwGPJVnr3XVsM1wW5NIU9PbHEcW0X6fPN4y2+HoFD/F/Ed2het3u6zg2oDb8NVQEJZNhtDlGsuPmph4b0sfwZXnKs8WgKqtGHWiEJQ3Crqpojsk0CZV/vXvbvfoa4NCeYtlnaSYEdfrB2KIqm+1fE4Z85txlcAtQ5y71BE2O/a5NBW1fdue6Yhr1EXKEwMuxMu/7LfscO/vXOmSd7MDd0kQXrQs8TAerhi8XEV25BoxcmsD3XXPv+Y+//pZIxXYv+Vguva6kbtkIkixSkhXGghKvXwubScM3Jx/WyDXoTHBNfWHpSMtXP6g5ZIX3iv8aqbdEms50DmvztWlMQVaQbpPgu4U0fZCt3MiPQp5Yx7h+9Hbjv9U8zjccRUjoYpzuukWrov3t99VZO9plQOyPGuthYnvSFS5xN8JSOV/frTV9vDZeJfByoNm9aOXIX4srbjn7Klv3cHGSDMGR5bm8/nRwc7yg7dB92gfIqoxzm/fYxMQ/+wnwmTkWmnOoOgRuT+w9F1c+uH88hC4xexJYDk6wDJHcT3TvuXjnj8Z7cfl27oZ6uyfKgL0yHqk4LiKziZhyL43LArHZXmopv9RG2ALVMhO9pRKQmi8xkY3NzRGJGh5c5RWZ2m23QP6p5mtdEYdG8aDZ9VQk2TTdFhAvWwlo4+k+Wnq8ol1PS3+oaI7mY/X6Bia188/7deq8eUASXqnmqgXTl+n6EeChVLVa8uWpO8gScWwcbM51tZM7LGUjrGISrLf2H3hf0AzLTXHuGG+2Jd9uw8ph649gC07ob3ooN+9m/l6nu8DUlvwdwhSfoX9P87Yv83i727JBWIIh+DMwTcrVC/0fz3s+/fU4NPxev5ulEQUxZhuAKcoZZDX+XzZeuOLINc2x21aJ0vb+IZiQIsGHduLlpQa2nHVuEUUJVd8epNqVIeUnBeraV0Kxi2/TSz+h+FN396SbuUHW1z53s0z/StywQ+cf2KMsjb3dSYvhwjupX3dSuqq26GoxvzW1L6gD8UkWnzVRRlFNreXFoSg37m1OJf5yaxnXYbZEAuTBjHQcWF3TznsL8fb7DTtLkUjj6gon/5lV8TUn93C/t6hPspII0dLvEFwSSlb+F54ui9modaKopgEuAoZ+z321S2BwcQcPS+11qKOLu8C2s/H377gfHw3YDPx0Y6hR3KdI6SC3OP+SguoNNns7RDg6Eiq9xYOJfVNy1E4X/olIkV691Vrx+9gt/Z33tluT1tCIriBDlI17dY2b/WiP4lrDyf6atDfXjTcz3xM+MziW6nYt0xDil+7VWeCm88uDj599l2WtSUd5wTll2fo9fx/lTtQ5QERvfEilRhlS2v1In3gwcHAmYgaAfKj1t77yKmjrypCJKCt3gkeV/3VKfP+ztfqOSj3c0SCKQhazuXrvkLcc0JaoukjB0KZ3QtlGHQwwOHsvzf1M9z7WifsLZ5qvQFAxPTJSzJ8qGjpl97pbK5a+cHWzPVUbqsOaZLpksICjq34QM1d5rS71pLiGz/7LYDDxk56wnqTHXdE2Zf87ZxkyBKuQVy2BtDOnOL0ZJJ8wkesmtD9Z77srrKkDcXjVNbNqsRvTSySQ34nWyG7uAgKzOi8WmNCko4zzdxgMISSeDGkyp80jP8Hi17Ysv6WfsqxGH+7/vP4TU0lTQxTyClB9FF2maoZZpy5vSVzSoP04IRB7ClqziH86Tzc7hy7+XIwAcqv9Qp4fuF6l96tgErQ0HskiPK7H1jnP7eTVZOfcW+Wqzc7bke6ZhEqjPX9rzZuU/NAyJ+8HMeETsw4uoNkUON+MqLpIvk5bPZH7N9dE8QUTDcsxUwz2NhsLPUAYqvW6giLq+tPGqIfrT+nsRGJLiejqHhJ4Orq3oTJFhzvBFyqB/SZo/TBo8v37rzlG8mItRwu9wVzBDFzq3oqsoS8yf/6avfL9aUtpKdF4wqW1Vhe4r9ICOuaddkyCQd/PSHqWPDasBLPMFUgzsHJyJ1CGZihEr6tqwWT+/5lcCBJBVvI0TuRbeULL/gs1LA8xhYQN3ftNAl4q3LCOSGTSUBV7RVkij0G1FUSOJIEOzHxluvNkExTn/PGrbdv8ADK0/G8djWREaCpndzY5o1FBvJiZwZEnY/X4xrl24IsQlPUrs7CfUUdUIZFjaaGzGWpOKd7oxhdyeVZbrOuVC9muJ2Spb6Wu91JC4Gkih2KQQHmTqB2KcfsyF6NiDQGaHfQT51kmNotUWuWb3vnva3pvCgdKOePQcqf6mLTt/Fkhjyobo91eivrbCU6wEbgT1vJLcbTWML5686w9/4ato146+4u/3tziSn47GgUTTVobRnWR+C2fuW2Frm/pJKCA2lZj9MCtPPKo+xK/jVhUX2vk9COMY7F34gO38tNfRRJNBxQDaVZuskP3mnUkkHlvW9UnYsVUlStIPufu62EDTkBr3WOXPnpoEAeLGWL1cdWwCoCnu+s8oNDdruejBfIrbuh39sZP8Amum0arcKzbyVA8BvsOyI50BI1qfvrN6m+My1DSFwZRU8e2WqNBTLd7G/T3ztCwncwF14rc5ub3IjdUEqW/u00jKOw/519p8Vsm7ReDE8Fq3yAvCf0EKrl0jP33Hh77V5eVT0Ugp6EIZSe6o29FxI63+xVjHznarsaXcTkZvj9iq3FPHHg7RJmZ2oEIf4bi1pZw7b1UPjFIZyhDr/vHyqVog4YLtMm2ZWd+xNU7nuk3fI8VrR+hDTHQayNBvKOp/TD9+sXpXvn946+N1b4UHhHcqi8kyS8fEQQolKtfgXl9e2UxrzguR5UtnlS2J0HhEHOD83/xsW1Lz/dtJgsm7PPNstPQSW/PIGVywP3si7+v6ft1vEV3q6bEny+uDIWvzc0uo4zXv9EqIHkYTm/Nc3CPfs96B2uuzoZS5+5DxsPnG3OSlZDAyy2JzD7/nCbF+7H9zcxtiWzEkwEOZVRNEsTS4Dhasix3nunIu/Sja8zu6P6C13eiH5uf1a2wSLFfY/HvIaspxxl8E9544aKDPV73eni3KSN++oWxm/7Jmw+EHlAbiq22K/Yy/VXE4lNgT4T4EE31kZ8nhkaZoqisbDqgjyTpMqXAUnE6vtbtz9AERDmtokxftiWfPLhMpR0xf8TwJkkNcevoS/Z63tYUqcRonf4f2OBWowyoxjq80NmX9ChAsHx/47UJCH9ds9vwn9/w5AdthysSstLm7PrC1zhXYFflIfk7fR3Ph68U4VH62Ba+2SYPud5n2yKJgKPOTCIH8ndUt7dMkaDUU91s24qDTEjcGtX8diEAH2Vdkj6v78QnYKfCH2doB0uwV8nS8ft6SCWuxvXVpmCSvWsvMfTBtur05OgfGdoX9Z6byf5mrV7QM4it5fAqy8hpOp+qfsguk4Rw1uRLu+4cYmzRRShlWZ9a3HjdOditRvkiyf1Id357pI/ywUfgV3k5UzqKZpSftN+erKPa9N1qCeHkx9amYTV3yzMvuxo+1qmfxluKIWjvQ/1fvC/hP095HWTd5ho/8DwJEMt3X+13TKuAXk86g6qStOOI6qsijJXm5S1Dv5l+vzh7EQpvlR7m794AARZX+uRSLKeWVd8xfE0u2yN7UYmVe5GdJFOat398mW6L8dDnRzMCq4EPyG151bsvrYF2fP+fooJPQZWyQwbFbD7QeMUovkmNBW6d6VClORwiCj42PE77BRNqjR4Z9IUKi210E3t/KlR98jXnSpwEgkqsSBOZlsXBh0Z2546/6CljLpgV4auLeP7uylU+1QbNP/cq94d7drcfYBzQt+vebrdYNjwLDK8c7/cnYRemLvy3GAlXUvOia7nqOKpJ+pmiSSw0U8JVjdTfTktEhv+ZbZu8JBcaoct7bQvl/+zYCKSHJQrYdYFNl0un7cBvtl2aRyTd0tL3HO5cz8sfr6hdXzTI2B5qcmmNvre+4r4cLMpSnvTDr/HP0O21EomXmHVojdouuqF2v769a5zk2gz6SAGvzWvDN9PptLq/KA5kk42Cvo/k9Pc7on0Og/xxtRAQI/h3jeg28dJTYtG3v9uBKWHmobTscnA/SK6D/hD++ooMML/LekexwrLAPHnErK/N+b34toDgd8PP21N+eB/pyj9Eh221OALl7nRcGZFDhx5xV2LjotsKIi3fEahayz2sO1xjVJocs0ujl+i/0uyE2do2GrMBKwffNWDXMlMUKTex7ZZ42vJVBQ++buZA3z/XIP78Fert3jekFSTf5xdxGK6NrPSKfFSUQ8fVTE3rzCVNyvPKAQHH6GzkexTH95/RjsvK0aNPJL40uKJa3wrrGSMd4p8LfjLENTfh/J3mgx3hTO/vvus7y/GvMnqzVjHlYAvK8VhZmyX+KDO9PU92H2wK+2BRSFp6xWr3mAqJWg6n1XwdxHnPToOaW/vZ3U23+ef7IvPJNvXnJVi84DjFSmswj18r7Bv927y9JlQu1vfZjPHtMQdsysXB3LExar06tqb+77tfzxS+WakiN3HUTO9Qa+WC9xI3wxXYX2Yu8JQ/qgoidOM5f0TVWlNUiUM6gYCiCVWkZVwnqk8uasRUSweJn7nUs6p0FPvwexSE7p/GMGgNeNPksZxYHQMrjp+1ov8sF5EizqkpgJJSypw506DSd4pmggKeD0JS5UO6IV0qfxGwtmPg7aITs/uOP5rgvPLnRjy0Mi78fuesi+MXzQoyP1eLe/8K2OfZGWCjskLfJmP6o8pFZf7DU9dPWjRdP+VRJ/ZQuOs0q8GtBBJPq1uuczT/tnwt5dxXYb+SD23t7IxWR0p4fV9VjN1+F8hnms6y51Q42ubBxaSyV+yY5nYrB/94TtOYU6ryq76cmGzretO4eIaH2LUH/7HLKsJ7baM/VB2HPs0NhfdYKvfX0ocWm0Dww9vt6etvpi41e95LYjwdnUa9UR7uUP/ftWxqmdK8xWHVtS6wJg8Fl7ZFia94mGSpdrzprbzMJMsrhuO0nMY9+wvbXvTzFSIEhpnmnPRc0IJotFN3AXizlwP8gzIbtDqlOLBf2Eu70XzCY+Pkkl96TxkWjLjy0nNFSY1Vf3lsUkHxIUSamHZaEmb40ir/7T2Ed0VJfevyeKx1UGRavxxD8ycrLT5X698/pJ/LwpFcmwHe6P24gk6rRW4gjhoZfTkDo+KKONFOrRvw3XhLiwe8BVJdWDBvB7G/9g/A14kVMwYXCDg0/xsBlTRKZPzQEm/3kdv9Jn+jY8KZMr1kHdT3v1vf5Znrr11e7sqJJXbeW9ZszHfCVQf5ON5jyNHc0V7ytFkTjcS12IwEleSwYXhmmWdtXAdonnXe6T6Nzq5/gIX/vvsBLdK/sLNL3S5GPut805I3WcY2RSi4U4yKsDjt4hfzA80uzeG/iRnYV4m1we8Twod8Y4mYmRbK4dkG2uWxjXawx6C7+3RBXf6WNbcWjFyJax5qX5fHIE2i/kqlF+vlEpY0145HOGXj/71hX7m52M9Qf8mgLUd5aJnXK35GDZMUv/AVm+aL/27MTtda7TeSPpDkm5SesZBzH/4iMgu/NkNxgzZqv1Em9zh3G6Z4NxwVdMuJhU7UwAfSKEz9MYX3AmEU/03Zr4jYSPTYGgRdegDaIEJrpb9nueX9Nd1hIv/rS6zOFjcz2W8uT1bGZt8k/QTTAfikOJE2SpGAqai5WVKNIAmszq4KMePIwLiff1ZhYCV5a7368YppszoV6p0uW+d5rTncsY1O1M9+XfGsGtL2PUBu3ji8hOhhnvNdDv6nprIpWOYM+qtuwLXnlNq+LB0Nu+3rZ5vpMcXfranbKWrZsImBTzIOKrZY6Ag84zE0vYRGznf56Fvedl6DELgET8EB9JAK+0KnuCDkfmZOV5oIy1XHw/TIWUdBo5xwo+XA/YJbmfRgfv5bkaVEcEV4ngzTBwijKGcHz3dl1htZJm0EyKHz6OudcYRuevnAnpjHFm6bkrEtE68D67FzAB2DKq4d/LZ8t2IMyQVrjAEVS5+Nmdnz6Cc0dwt5Qoj3NZ5pPaLdOhdyIpniLZx7GZ1Jfnwpxyrxa7XJrsv4CpxcDjVWxbQpTbYoR9tf/2nrdwrfGi+7aYn+FojfKM9LNFaSn+YcT+3eWj/9gruU3LMeGh8AEqbdz/mfv2pNhEr5l+1Uec45R5iSGF4iu7Z0ut/MSSPEPMPaihrq1QkTqc0WZtd/WsJx9Hz5r/HZdAoeahX0Fvgo4yteoKxvQSpDkw2dITlqo5hNveotO9Z5zKkZ26hzxTauWCitXWPsWavVOCHYNFTKE3LFH7yNq/areUupsprlxKWeaw8CJXxQOkgZVvzJ7gva79rdD3T6XwSuNSeAM62vJieDH/uWEXr9X592VCBnQfIPcc7mnChONtPrme5MonF2p3DjdKBff6/iK69ZHqe45rJfsFbJruSWkdA6p2vy/y8d4Sz/sCnFCwnUwJNCic2tH33WEMY1oeShAYNcP6LuZktX9xgP8bkB5m+fruOdIKfsIqn5Iqc3Xxr+cKcgIQ7A1K7C5edX+43Ol5rZ24tqCvzZxf9wBKAwSL1V95Z32ZFL20GKibYzK9es/345F7hU3Ljvou0CHq/CekEvVFRO80rZ9yUu0xQX1s6rv4hlwOLhs9rVtFnCaeBIhGNEMCiYLGZ/1TZXdLYvvNVrginIbzfnv2Zy3PwwZWgFzxsjpBXUe7hK/OdR91XnK2N0zMjwnl/2ekst7T/DU6iiV+ELYc/YXIel+zVObR33cp1kXyOYSG1zTZfe05B8XOnDYbQc5Nj76Ijp3s3Q7nQLwx3Gn/G5nU4rdZ9mgNPgztAFCTv/cm7Uf+riAwX28uoD5+CgQ5mxVCXVrHZ2/jD8cG8C5C2IahFQBESR8oP49dXO+b1CGOUt5F/clhimEayMrVJuanfe53BNd+dH/SvsjooNIp03Vsiz+P9qOlR34t/TAftxnpFNa5m2PcZPI2SHGhWH3NYXh8l5/4v5HYYZLROV92R3ipab0YnR++idU5ax0GeY7bnlROiw9vGRsCKu1olifk2xpbybdpXHgzTq2HzN/0/MU1Jg3I4fK3+izYesxTXGhS+gXZgxq8K6h5VA88bnyAQmUmr6ePkSvAlz/NzC7i8OYs1zvEZfcLVW1VRDbkRp0htmNTKuXPx+aFX+5So8RMa7yV0N4bVW5ZO2bJUsVOCBST35jgX0V2UbTfKkLagCV8fijp/7Bz1XCoANjx9ZnctuiVQAZ3fI8KqDxCR1/Omrn0DVnzFs8FW+Br054joP6vYWvOV28lN28O2tFX/YDWl/ynK/I9SA7krRP+vnhz2d6O22TBRqWdrMvUp72VTf2oH+inFh7TS9u9pGu3mJqM5zI7zcodziVljKB3NzoFcWrxPEpggSjezgb4BXuT74IXuNkMLMSHA0HW39sCk/j8CQL8fk0IEZY/Yj56U0+lCJ9RCh4aANqCr1XVvVumTMinKYDWkFycUY+uRO5ety8VcFAkrBHnCfSTDZHq/uYXKloXkTsnsanwXU8NRDveMInZ1jl4kN5U+u36Qe+oJ5m3UAdS/URd9xcZP1uU/agh75RjL+2ta3WEQi/b9k+zjxvrMPnDeXH7tundTzuUpbuSFJsZs0NLvwbX62oIW2Parg94N8Q6B5oJ9UFRSbXEAPsQSeROwZkwmmxtmuXhdkifrjPhRxtCsht6Zm80mVChmYPtv16me5PxDnO3Otub91qz+fuUlzH5/P1X5ppCUPH/HMVPLh5zPW4fchUOLHSP/PwWfYpQbrQTlzRk4+/P1aX0psh+WFMpQ8+t2yJS++xfX0wxkCal//L26va1DFe8lymhtyG2g+mH17ce8Dyvj90cToDwtVOb27UJ4YD4rz2FnFfpJkD77emInfW6dS7FilsmyG38YQnshkNQdB6X+rqP3wj1A1wd/BYbnX047KbLTY2/em2lrn2LhW4w0tDfbvOmuAqT+/Ii5rjhu6ZRak/ql12Se7QJ73je7bn5dYt3degxOW7viPK6QZblpns5T81mf0Raw1v0ACCBLcN1NbAMlHDmLgtoDJ/vkzFqFBWu0QBLzsQ6RX+fueqdufwy2t5wR8VAnzimPEGCM8hOFaan2eV1hI0DLVjbvAk6beSY9TQjoQB4vgR963DI1s7T0rHTqI74qQrQxPt6u0jXfcHlrH8mvpN9Ck/CPdFkyDCXsDReiFHWW6Si2PdTIbYXb8FnG2l4YghbFGBR3BMkgWs4t0w60bY30tlrfqGFRO5IT1RYPtOCVT8W4ouHqNu41PQfPeo1rLjF/748JzIpeqUaLW7Y/SIiH4JBWDb+pexq9R6aHtlfPfkF/Snb6w8NLCvSJ9PQzk335o/UoNp3HuaMHmgVoep6MdmsaKR5GPUIWGzo5v9m7qi9UNBk/NRX5r7CZLNlH9J8j0jJv7mm/WDwbKhlf5oYixryN+AN7TVdRBF/+Zy+oa86mMa1To81nHQWdKG4vn7L+08Mc9Vj4zZQb/nOs+JsrrZ7o1qJh4sU+RVtwvT/5K//kfDsNh1NXsnF6ysBMvmk5+6K6jUfNn0rZCRgPa4yv1xg59ZyFwjMXMsx+ypaT4iUu+F0tU6TXlovi4M6sDyQ75nOqCXCKb5MX2y5SkzL/380YgFKhnOvv+MFn1m5e7yfJZL9zJ3e9pRQgR/tpIph80+EM1n15qJGELgHvepvWQyfw3YtE5FhOVyI3/rFbxp8dIGkHeR6pBabWTHimDH4ThdU4qk3u0LYQj4ZulLp0i6iosyJ4zFbxHChayeJoMyHm3vF5yWt91hn6eJZpiRcHXJoe8xBJpOYmsgNUW6y3079fGjutuiH0nY1echHmYY1BieL/KsDIx75KaPAiOqHa0C5KStt5YWI/V02z/grU0sRlbS0C5vjK97rllh5kMS2OB1pZLOInFpV9CVs2M1/Dbh105KPq+IDTjonnS1266ZN99SzhK+gLKLflqQxkCUD1+apmTk5BH75Oz2d6fRGQNmJLCMzqvaN+TNzpdRdWfq4QeSiT2Ij/GEH45+b/qloAoKKviafnc1oKKrCMH+sBjWtr8+sO7Gm5DRqboCXlj9qhITbq2F4EXNHsfB6OsGj08dmpo7uujxNKN1oTbi+s5e+c6qQsE6DncbFFYEtCjwS/Pv4pDB6Q3xcLzyPagoEFdHoSS3+kEP8T5qpS/z36WFCyFY28RxLc4tlHb/0T9kteuAHwtmoXfI8dodCSvJfeAckEGu8pIbdCCjwOvNRrJr54e/6WU//ntnhhuz+g9XHCO9A6NfgMZxm0cR8UZGetLyUADSLGxVkT/2breCNOUziRNrreOrVjEhqnqm/ExWUsfOlujKlNEcuScItJ/Ach+6o7ORJus0SvdnMkxucYr8BPMvv9ibHya1Wij3XwPSpfAsZs8wZax+IvUE2pPscHYczQdHjlupu32myk2Z7sJaT79leM249ZHe6N/u5tu3xu9AirM17ajBy//PlpkG9jn4a2Ky47GHtd1KcssAmrZ3viQlxtlvm5SNCysKRw2hB6P35TL0WDuE51OTVnG6QVag2lwUBXlLw9kgE/h7puuNYhj5jcPx/k4tEVooZEuxuq73mKPOchcxqnzoUen13Z6Ct0qgk03tLYt6VDmj7J089MwN/yGcCsWrXrRfvuZBgNV/1Vpkg+m7RFoHqkeFdteflfObh8YqECAB9goNDsVL+bEM27fekCoY12OZdchyHKrcbPKHQk8SyNg/ll+ilpb+B23uS+fW1/Z83fAuBkRaszpBijf93VACx7SZnT18ky8sdk12g0/KXf1wFWf60xRqDx5/kfv+HBbYnxpr1PHR5CV49mCRY26sdwRUmaZ43ZLKXkhZyXSOpgMFsu6AWfTmDWCP4a7+8flY0v7U5aeTBRzPui+JWoi/c/s1XF2yS8oPtNsbmSDw8uz8c8/7X/cKQuxX00PNyqa647QpeVvrgw8MKaza2B81FoGOMW2CoaokxfZgnRrm3nbf21n0h3m2pBHVoHYupr8XcfIYdwrVucNrZ8piS0RN2xJoBWCyPDGmfvxW2tLHPGuzrNK7Tgba45ZlLp4VEpPo3jffUsOVJPrwu1HJxwMyXRf8ti2npi/WQTp/z5dtKQfx/apqJZ/7VEyYW4waUKO8s1ko68darnhZpp5PpvW+U5wAy38JJ/ZbFUYabk0k1UzgH4hQ2cerGcbiQzx42o7BRw3c3lmt0xXPto7d7TlBfDSbrohhjce8niK9mh7r7RmJOTJ9Edkvlo5BxciyP/Kb2m5BHk1jhskq3GPsCUw6g7Tb0fJ2Ml6bhZV/TqfrTHbZ7P5ozHj6yw3wWKZthUJWYTXEN1SZ4uTY0hleuqD/M/vQy7zRBkhOcN5rDIyX5bymuX4fr4uI5iGY4J5XQAhxqW6WkL/7Id/R4Tfx0v3W3dMjGwmyuwZ5zuqP3MHyAaCzGi935pGDVsjCO3gmyHKDTTT65fnXfb7XhYwuNijYtxTuPZNCNpM4Grcx+drJd5vH67D/mqeiBV4KlJ3o0oThLiyHTZPRhYxv/cj5kTsVZ0r1cGy806LCVpDxWzaNDQqgf1EdofmaQNVdcUF8ZXewgSBKp/Mbakneps+Wg967hnsxaCBkOc9PDU4km5VHbzxZf9sN1Efnh55r6AR36ec61hZyIaL7l8Sno/XqK+t2b3a77u+F7+K1q55CrHT78eoHsDP3IT8zpaHwLZy6kMCNiZjMKCc0Z0IQN/3W36+5Md1Qgt0mrttSP1A4e8sooDZz32evZLwz16r6IHbOqs+orxpOlNR96u1haYT+mLVpYQcK5JxBcs/xn1+RHupAFXNtm8M3eUNDh3L6V8b+lMGB1BeZke/8lvGjNLjMtcWJj2I4pczRxn3TFRqUdV0FhjOAanmVqVOFxnNL0L8BYHQja4cf1e+VfAYR2jwa1vTvS+gAS1t9lO7p47otf71WIwiKuWktIW9lQgx4lzzSxjLc1+4ZiUqahc5ne97dIFtQi1O5ue9rucdsT6sPmihNkGS5rbNnxlG6cO2XdGP16XKFvfre7v/J0gZ7kzT8zE+ih5KKZsLZP8otLYxPo55/qxZwK7G/+kkg8dBGHv4Taba58cqxQ9l5/FKRWtu/Jzv1uGg9/zP/AhjWr2fJl6i6xwPFKQx/B/EGtUXkkysk7/E42WCaZeuB6za9ntffd5Li47w/8ofS2j85hgfOdzDxxOP25fLnOCpP/OXfB+7UBP83O8WLQffoSt/SbFrkv0//oEe+/2hhXxfhlAQhfzg0v3MJNqukl6md5hvNvD5nXpX4yJAwnd65c9fDomThnreQC6hD3yumynFsOK30hEEIbmHk9wMvkJDBzzl9nSexmuj4EXE7udhkiap3tST2iAEgDTE7p5MaJzibBdUjjMg7ERD4WxP4rJfZgqszEm+Eq2dlCd2ZFT5SjY/MDimMda15v2bjn0jFdOb7hgf7Mtlh9cXdNnji2pmciaeQxBlL5N+NnS7v6wzyvUaF/N6ypqoK7BZJcLUzrJGyszQUaraLykzj/Wk3i50vMe945hfIe3t1Rs02KW7SWd6g25TveJuAh2fdZnx1LX+QU/Sd6LyGLu0Z9tuy/7exvrxL9XVf9spzjtjM4fIDyXbWK6YM49QvvIEjoV7DZfK3fg/MxxCpanBGXFO6lyvX3cKt+t1cUVjQpympv+t3gvYuqQJiDfW3vj3WlsSm35PWctRKHXMKdb5wtSQHk7xb6n+x/VwmHJZNsjTZS1msJqavREUNh709xNTIZyHIO3v2zgVmh7HLJ6oKlpyL29YmTNvtvcjPLYkDdgjS29AoZqLEOgb3B5WzAMp33Sqt5ANmjunZddOO1v5dOWBtMAU8zf8SawDarWQ0GuseTXuAM81fFOki8XJjDvGfd2yOiNsMLew52u8UtgJavuO2OtRN37VvcHfZcPel8Qwe5akJRP5BWeJ0juroc4jmsp0+bKjVjQBZLFnjvrSzI7SXyapFPrSt7kSVy7kbrXLp8yW//L8WeOgo7ZPpiJxXj1Gy06xrgN3Kz3pbIs7fbLwzeduQwnFBDtgjt6zqsKDtG30P+Vd5uwQlJ7kk678DsjJ0hP9bZDxj1dQozPbM2B53SJ/TkD1L0/Z3PZW7YQNI3j2zCc09CVSTWVnBG/UKQveLokZaBwO8eoQCsaRvhY8P0ewTsbf74wpjjo9Olru2ICTPmgSnVM7dzFbqzzCvFa58U6nJnCUj6S2GC+cpTwX7zMHuzFpjzQjIrj53Bqw+v9bvHL3Dr+4W2dLt4JFK/BPEZ+cQsXqdoATFNIvm6BDoiK2nAq18YCZ5ktaD15dSe1ZDrZlAuC9EwPslPG7+kByw8Ro79hag/xkCQ1Ejyf/ZP9gl50neel+pJ0wD7LtjhbhyTOzGnmgBsOLZOyektQ+6syDbMvwF133l+S6GVLEs2EqSS4lRQLhxCNYvSzGWW6yb98QXgTjh5eJ4UYncEb7aPPAGXKZ5d794zoLeYEHZksl3XIH34H6713IIBlPw/rNDuEt7D32jrVilDnhANp0SS0J9RAvD/i1P8vzcafvTxdn1lj1ypP/0M9LkuXc4FwPolfY/z4MuEUDsKdKXwOaRS35vJHAKSuASLHYNeqTlOqYKmLqllRn7jcz56LBLgeHzACJlX6MyWkSwtpoaI6F3rcb0yVLyC9z379JB6JM9oIEDtcPO/toh02A3rK8s+lPatcpdcqtH7H+xdp5tc2tu1F1qSepAAuRG87nfbc9sOkM5tgoP/o6JoulAeH2oD9vTYF+6mAzKzyIEmx+arrF3DTv3gkfveg7okYI+iKLX/+Pc/FiJajssPmwdNmKz2u/WEdqYGftRJH/6AkRjnTkWOR3r9l/1aXqFoFvfjO75fMLwu+R8ixxbBZ3S9Nfj4e6HBxNB6mSqtpG2+BH4e38SjY3jHtYvP1MDzM8Oa39gb8xjRfR0eUriK8BOs70VF0pqtAhcsNdbLt+DuyIUUsjL1chO6/n1antTqNBEDslmzuvNASfOjgA+ahTpT2eZJanUVMrsgXAvWikGtEIlzuDJpFe1RSb2V/hmv4JfIO0u5Ss4xel3Tax3p1KIzsN8urMHMnDd36Z3N3+6qG//FPqbCU6sksAkeMeh27JsdKFOPNAjaXVdHN2UbguU1lUCPTYSx1/KuzJN4/nJDD7Ow/cOBjdxLbELfEsAJwl/7q4X+YOm/mPrkfuCgf/v2GgjKqx+cH8eoQ3A18KFovbega3651rCcRaiUWlP/7e3fP5E94EjRkTgewgZA7mNvIFeudHbmbJqwRlv3JroYAfIuZ14L13ine/XDfipKqW2wEKIoVo/GptkraVq5qDBb1nEM+kLS8aSL72ZrrDjffl2+uYQdPn42t6pp4lRhOB5plPIXpka6+8hE8HSKVZjDxm/W9zEKBLoK4fsrlfQdZJ/JbitQJv9po8KG4QL/cu1rGIJ/kBnfrbXft99E/fRLIPc2oSQ9+SRIL2YIfPT45qHPo3n5BF+RwMRbo+Gg6eqI4INcgxpP8Fdv9NVrc+g3V7/R5qYSDhkYkjQ32mSTswWjqCrEtRkG2UzMTNwu1TFUd3XoLMFskXpX1E98+cHdIfOnvqP9MIcEwoPsWu7v7nQ5TfFGk73DzOsxKZGFbZamihJCmoRYBIjP1S/ujoQ0fg9FJybKf+LgPxfZcL62VKj7huqLecQSFeffl/ebSKL/D0dmK/zqzvs0LBxg90glutLGAzmeF9s63lP9A9AWACa/fI6MNNvu4kz60KcnHaRJQ+t6/neMCtmFc7Q7mCXS+9qB5bp5sh6fWL5lU0ObpIr7+1K0sCU6Xy9sbYvSsQQJYwmcv7lYkWXUhdry+1K/K73N66SvJ4j8ShDrFipQbhFfeVSruf6QjRWPGvm5D/UXJx1wlaMTVAv/SxwYPFKiXhGUPRqlb3uMEe51bcqSI3vYtiLmeRnqDhOzAfSnfWhxSeE31Keg2aiA+JcFG+dQz7WIJdFY8FARHqQxQT+4vv1Gj62Va7yDzTsNHUDvaAewom9NX6/Hy+WcYLcLRVWbv1BB9rTnlO3ZWXYkRhXbb3q2DSjbB0vpZY01K8UUnNFVmk49Q8kfRN2n1f/35aaSgt28cIv9G373kZ79CXLVr0oY0+kHXgz89CQ1aXxki0f1ITtTgVP2JbeQXIvDBwGJLWsaW5WrID/8OkWfzTsrPndrtWD4/3CvmSCRLpfTgomqqFeU0lY7fNAT0RIKUGuQHhcfnnDo7MENPeckDkQqgP/AS3RIhyyD2xDW2XaHgZFCW3HKI4u3balhrjz8DCDcC1/YqvQJ8DQm/vz4vbHtqYE2UWpvwN2X8GMjd1D0CWttPsWeJ6u6n/MWzafE3d4mSUzbhmaHtTazyV1CsIljC+zUizE3THpZavMXcV62qPERSX7QKeQGw57NxHYnS9eNnNLK6iC/LGD2aiaAGQjzX5mrX8D2RLwm3rqTWi+MsObq1oZKB7zyOBh9xT7uXCw1A+Ys/fcwC863r9p0f3G9/e1gF4U/jieGWGY0Gb7Z5GiNuhXiIrl2NNJN7B312bfvN5rxL5aj0G967DYgrotgaIZS94BrP/jaYhPVuWWW5hDb1NsppdxHNKWD1fKFi7inQu4MaJvd9LNDKjKKJBHdKqd2zV8t2BO6RfUA1wKOT4e4iyO7iLQ/FQ4T9sjnnkQCovSi9XblXidjpcnRTvUCBXPTkfeSQy0inmwNZhCV13dqbr/D5xufc6L7Y+cASTA+MY6VcDW2M+Ha3aYDfKjFrA/+5/NH7hXU2t17gMa+K/KhCNp5ryabaIp/26ZINqyY90FFf9LFPd9PlKgXaG7qxV9q9w36lB6h0ktjv5zSLMnKu+hht98N4Ul+hSrePPHCzyDqIOEE36doqerKVlI/h/bhB8iHV+Be8R8zevxhdrbMNox87+cCfHIbJ7J8tGbOuK7jISQTqLEUP07o713TqWu5EuPb9hpzgXjKRZXEBQCbxAUzby9J+61ezmDXAtDDnjYUD7NLG1UOLwzMEpU8HnSZJ+Jls5F4pA3Z9vasv7gL8EzQzm1L868VmJuk0oZqTMXaQ1+PCerZHhAybRXlsSTuAAB3zAFJN0LpfsqhfziFxRBc0m/ZOmquRtXFbqCPYFS7gWhfK7DUIq+/2Blqn9+ZCa01dB1jaJW3+nJTiiu9vOlyeA2upSn9NX/WUSOg9F2GbpPMer7234ocHsHGQvE3p6E+2pFY2IEsE8OlGQQLTwYnMgKoFrlHW0s66meo/K5OfjHZYOAc7NHTp/kRoKtNLXPVdGVZQ9+yZ5SYNDUQ1Al2xPIlXgFrwsP7GPjRw/oqFnSmJL1Th8QWlzeeCJcz3qebxNdJvrDNm4dP+SqdyA95f30qH/tu5NAurJyG5SptUHNddD+iLLNH2UCpWYltWuDn7uoKF7o/WcAYC1L8DHXoTi+u1QsIEOXHRl0mFCglRW7N08T4gn3N3yaX3Th4GRiLrkFWP9+qkhWZKfOUcVP84DR/nmZO4Nam0uL/DrvHMCUeKohnUpPN3vHkz3YMd3Bc+Kdz3WmVzgmBqFrxY0xFkm7C39myRPbj3oNJJTwF/fgtPGFmNVpZDytR7O93ql4EwuG+s9a3/FB2zFEe31PTu/qn3MS9PPwK6eRbkJrEtpsaystf2jpePi9fv6x8/HUOZcX3G0fJ2YkFdVjYl5ngqi/Mn9V/cDUxtDDZfgpF6aAf4e1X4CTZ/hUnnRAEgSidgcuKSzc7eF+1SCd06vL1q56RkzO04BEThmGzfZoGu2uuS2xNenJyFNxJ89nRDuupf7PcKDycUI4lguebOw5e/YQ8Q3abv6ytq04Wi+1ec7qLmBAHUvDdY297qK80NslUza4mQt+wODbmI+TYrk7UzLovVqb85M/kzLoCLY5bUB9oAZawY7CiHa+zv6ZECp/JgOkHQP6/06V/vPGgXEbT/MYwfN6gtSOVdb9fBHkL2uVy1vt4wa/l67hIfxVt0w97fcmhxqGRJNPeljT5TRtk3tLhC+6D9AJoinrzSHvDLxjKz1AyvszMqu3fPNLtnXrs50zBuuOenSTVX/gvrcd68qKnzk6zx32DpnC4N952BVwRjl1jH+BkG+lwpZMHuE5O8qe/Uj8Y3Yati0VXPwtgDT5wfnikpSYVlSAd/UEfCb49B5IjrMyNYWJlDoq1IUpqXyvtJgmOpEyxvoSEfWtOPucNPvDBDb9lS4fzeA5fX2R3ezb6DS6JPaof4iw7HrU8fUq9ND+sRvf2H2Jo/MotK+C/4rLpcSEzWx0e2VwnUbvJtt+//b3vSxgf6ui2ZIHdtXn98DQiR6yX7MU7hLZS8GCqqi5NhH9Cyh2rzpvfR6LwbckYa5M+XiaokjUak/XKmpjq7yV42oDem/t2dgBSbDuxCtopv0xsJwLQ/nSdekAgmm6/ySlLbXfp3mayjYDmmo8nnZSDyeiX3n8Hvej/fsn6zV8HjgGfp6wC49D6/Sshb8pAYVOBpn+Jf89oPJWEFxFeW+U6nwI3X79sjpqXFXt2j4X4WOGSfidLGqif7KsYqxwkP0vone3k2oSp0gNftVaptosIWxr4R41aayMAaNS4xvYfMtvyYk63v68Z+15euCSXDnUSZNzFPTy5Ojd7n7kj9zpXyjhIQv+rwq+stUeahC1LWwrzoJecoJ3my0w/e7QbzGi+MeAyO1Lr1P7UA/jly6xQ5d9hBnb+PRTKxPs+bcldokZ52zjFWLfiwoQ85uBUl1iE9n0EnBBcZOdyt9cNJhGuFmjGebJ9js/92KlXoTsbnpKn0cf5OAb94nu7N87r3FPm+39ahDzqfuWnHJ5FWevWjLslkkWYAbj/cEEoe1Dp8GK3wR2jTTjWDhnuANS5NVfoV/HYSOIxEhKmvcaWTCdV2kmheOHnziV0/s56Zuhgvvl1OpWFkFp59OatyItQl9/luxjm2vv6vulfRM7VRz5Qu+3QJwqmS2bzVeyYtlW+McCUHW33tJrPs0y5t7qpXN6X8LploN7fxkiUnzd4Jao723xYIGyy5EJmgXdH7jX02OF+n0+e38EcbcE6NLOhkd+Os11ToEUPGn/cgRaW0zW9bHN7fO/mXbCu7rRaTac26MZjyvrJ6TRy/4iiup/yrIfnz+CPB0/mISUX+f1e7aAuWoiaXakYrhIyUK2TmpHu49wbmdKu8yz/z1+bMVbJnd275IHkDfrx7tVaLON1QBnpGwsCT4Jn+WWB5VX//tCApOMGx7Hmq5eDEqj0q0kUpe8x3+EA8QkX8euP8BfenxvoXKc5XSzD/Qt/+nAPtetX4l23TFgwF7K6cf39LtvxhP2matGxjTAQ2iQWwJJDlhAAblzL2/02UgsfzZqmT0sRbWTS5QLukQ0za+Qbm/y8a824N8KCzY9Lg24Mem8uEmZrawxRcV4Vn7fQuNqur9GpCZr8zOI0mJrNv6PP3V6Rn3r0p9k/BKfHr5ENquNFA3mQg0q/rF181dArQe88neMqEVoeP1ZV53VzXOeb9EtSMeS76v/gzBsfDt7iEC7KTGxBVu4I2rdSNbleyEI/PHXzN5oJBPsmUQ7AZnh/wE5MLvmFFbEP8tBVrnrhykhMjGgTxHKMZZv5CuNKvwW3WwjaOURoQMbUa9a5gBJ4Gkh58lrEOXQBNexmpqJNkKauNTq0qe23Xqxb8WtE+c6/57YOSSMaZTBzcLiphW0i0pf16zvxiu88poX6xJTPRZ97l8RDl0pG/QalIWUo0LmpqcOoNz7BZNNO10dsdOzEuIvPu9SF/2NiYCRW7YKrzPtfWbtunO2xl0FlNYzFTcf1rRrdrHNDzRhD6+dpXR2W7TLt5vWv7zr+vSqwE6DMhVhGikT8Gc9iSm8ce/54+hOdMVebdcHuWVpqsbEAOfWcqpL1eIae2m345869BbJ86oH/M9fhsnnnPWpQzXQQ9xslIPYXYMHMXlqA/r3OdNbpMSl9aAnxT08/jIdVtx+9/2wzUYrYzGgBgnrNd6Xcr83q7s4PMa0G9BUFKs+Fy5aP/ofZCJNdpLfvp0fhXxSk2Mlp9av6ql/uLwgy1/NchdyYmOPpF6LMTg+AQCrpZGbW1ZNAX88h7rQV9kTn9xGla6K5nIErTlxODSVsr3HE0lwD+1VN7txlTG0opGXxYoaXG/dyKN0jVgZfc+p1GBWRETSHSrnBjR1Uw9eJEaQbBDFq1HgyFzremOiCrINRGBcnVk53J+ukPyltvpjzGBHlfgjAP+8t/fcIN0kZ9UeA6WtYi0DFkmVb8dmU6ddOyeMOn3GPxafuNWnlauVY+80McvT4EA0QxqzmuuPj74JcsQimuVfUETwZ+M6FS8yCf8yLQgni5P6x33f3He+alY8UtknUvkGv3fPB7QmiirfvJKuT6WODvdxiCcjLFiCkS4JlNFL58qa/7uJRjZIqJn3HweCOB+Ym2p2OPkiKuD7nXpbbB5dpeSLfjfW7MmWP3SfSOj0mhT/JQs0GJXGCr8rCNg8DnPpmT96PxNFhFfRi47MiujU8r5a/4iRf31gBS//5IwYMVq6X2Ue3i2zy28TKpar/ohvB8qVkZigRUDi0rmRp3/fGk8CsSoePqUtXZC7eB6IVT4u7UhqhteNiJiMsM0dtq/cOmwXoQ6V6Hebs0cWo4T/r2gJ/GrzBb4z98nf+v+M89msToVHQksvUy5MXXUypLF6czXweQT1hI128bjNXPNP3ByDdixrt7z33puAdYBWrZohziyTJAJCu8no2Ks+/GIAZz651SWRmj45o1xrLq8/FsXI/16fj+0H/TZj5pbVpkGm71h/UVrn9SvgLN+69tofItrspHulkJ+5QagrTVGFVd7UgttONbZH2TSpY1kVZ5R6spZl4mArn8dAtW3nVDvPU4iebv0XoL6v+0Eu9D7oubV9l8gn7JdlSoT1jb/x7PKYNu9nMtuy6GE2AXVQ/oIU6N9i0nYXrW0ig6h0VUnJ2a8TI4QNl93IoTarFVAufa/Y8RXyMFXOmOmbTuAWvOYv1uMSp+DB1lp7dS678TJNMNZW5XTN1Gvgkxxe5itPwpw0L2grGXCRXYhUFpYV278D3nGsklMNXkTe9BBpjFX7Q634G8KP9Dd9dSM+U6196HqWQGCZMCQXRtA6jsQ77CbrVBx2qmc8E/EIATT+BfXh6j5vsZaDX92Mw/WmmN1yw5fVd1zDGihUxNrzM6mwRsUnncZuE4fMsFuiDJW13jfsVXn1yxrLVKnnkgLkWJJtecAJ7lmvbrfKMXRE3srJXme2Z2LoxHDxG074mf+uX1zdGnWiOr9BXDdr4lv5/aGgj+GKmtKSVJdO+2m0gbWihS40TSlIFfbTu5CVNYj8zdNSlR1iy1N2HJyEw5Ol9bOdsz/dnpMUhflfdmQLzTtjzRO6G3HRqX2+DFv3X8wf3Ao46aZCczftC/F2CnqIRWr1zYMcFotT3eblP30Lf1Fp/nWgwHqK9Y+4y/atOAmMSraoYt0K59LdQ7FXzcVdLNo+/qdzWkVciyKBPo9NqPQTygPCJO8decrk7kSSRAzPwlEm/vSi6GJd9vZGsVJjt8v1NK6P/VvXN4/Xqvhq0MhvbkvkkSXRL03flqQypqrEPJdBjf/H7j27U7GiyUl3aP94sGyLCi0sxrYvpjWCmTuaty/nxewMwxqgL1mnyfBo9ULhuzt0zCc2o74lzPGUYeJPjhkxhHLXjjo5QqgrZsjHgMFvn9m4t3Bh1g2onhiAas6Et3O8T8M90KSYpq3al6P6n8/CBfmSiDaQEemrLTe7oB0GMzu3z0dBX+yMQzx+2qW8j+AUqVbnymkdN+jBZT5YBTzjOXfBp5DUNZibbAW+/Wr2Z1rlXWtF3G2ob2cLLvD2as2rb1j8pKZYKVWM/1J84H/UlM7Z+ajU6YTfCVTzHv18BBX+GZ5lRSOW0rrKs6/Y4LSjvvgkYjJCVBMzyFFPplUwn3yAVXoF/WdgB8Ti8/kP2rUrPb6lssZi6yFg6upcGDZ1DvUlXhsW8S6nNJzjJVK55wdvODRf+qjGtVVG1EeobAofvoOf54Nvp4OkpssJXykT91Lk5wYDbx4wgW5Rw5vuvQz3VaNrrRwDToj0LHfzhOZLw0ozsfn6NG3jvR/4WmLI7m9E1MlmVNc4efMY8iuR14cHHf+0xjwIMuV3fBOybrK9RfK2aOg7MpS24U+3ilOhLX+5Kfq/3wgT67cWa5C6sJP+wqmN0yL+0JQLgNBInJ79mfd11S3b9kzvpp5sWBlKTPiXlnhKkt+tvWZdl7LHMDMWCQ6p1Fvv+Gqi79p1RLfmIsU68DDsrORB54zu18/NNDP0/Y+lyi/Pm+rc5R6kkInTY9Ce2MtmMAGh20zdpAAEre+VDTg9PP04Adhf66l4mruAQhfjhOXzZ23zdOBhex/Nb2LvtnQQ5BwVPmUujxSCu8nbngefTkwILp9zrHUPiG05zOZF7CJ1640u4dV8fNfEhn3fOOfu+3Rk1rwEffG1Y+s3piSww7WY34cODu6VpUyeTcgi97Azy38rPDLXKrnbVg9JYUk7uMhCOSo/9K9jR3c0tCMaUZ8n9t3Lqp0liBxOHbhag5isBO3ruT1n+8Ul+huhEx7ZuXxQ9evcED45/fb1g7Zmr8qNG11k+ekxnezVPHIDZYi7MzGc7RW8mgqvJ6HZW8rfmRFugKCVY7LRSTCUKFnc4dFwPfwfuE/Ujtg50cwGZe5MJ8fCP1m1F8IRmUA+uPC3AUh7MqCpGtk8m5sVZfu2c/YrgeMjn518P4i1uERp90xL9aeocgAcf+I3ZDj6+b6+5gJ5hW2pQLn55cEvC723UyxWopAN90eDw64EC6Bvpwwj+Drt/SDDNa1KOjhvnxCBuaGT/JnABdWhRadrgLM8xtqZToiqtCAuQ97JuLP6DewbgIZGAd0v2PI5mOeC32mSxzMGhutbPyanvpu3y4kcI93VBzhPebZ8oE+qes+jCIV46N+c6DVN7l+mwr+mR9dY+kzTLSv374pfKRORt5XQyssdQ+i05pYQtRr7hxfJZy79rc9ksg9mHCPHV92OR/8aMBJ8/ex/PsKMQ//+mAHTP2Vocfu2CGEJQmi79Tk+l31Xv9aF7N2ysxYRyt/BdYWwTw+fbCNnAdXS48SUPy/Yqr69KS1zNdjkZkSGsJBaE9GaURbAi547eeLrj8qaYRbjmVy9JYJnEmz0zPDOettXdYFT552s/R14dvgrmLtu3F8cqOq/VzFXiWWv48RwEx2L9sOAgmNa7p9pe7C+dtHNb2cz+7P8j3p+ExyUJwAmlORw8xpkaEaruRwOUjU6+7gfVYe99Fv4WYvrHbb4KH/KL00wK3vvuIqjlRPu/zBh0OZvanctjvC7BIDiAN9/+xKKPzNFW0LuMhSalT7sJS8zf2VIOrpgaOz0v/QPyn5KQetBhiphEUHvE31B7poXG2lsj/sYg5xHfhZ32gKvCT7Oz9RL2Zo1j/sz+PjylC+YZZ3ciIu7PhR4ZJ2bhbC4YwxvPsVmSj5dZNxxuETb30dZJA6DLS70cBA0MLh0EnLMDmx8TJkTsbcPZXY/+pRs9j1Lef1uHn3enxBxutUUU8YPkXXbh5qbf/zj9CaPX00DhV/iJVOEDEMSw5m86QDB1Roo3H2yLxPCY6ZLcXvZdC9fNm9TIFAWHSBSieDvWM71l1q86dMiSFjBudu7P9UK6nmzf3lNbRVf1nKfgas3QACo/Ec/eML73qxVBNPeERBZOexRgfJV/mK/cfkiMe3JIF7/Q5nTeyiKRXBaPaHeBwgSAAMc01FpyuKae/mG+E3n4jdigDjgsqYJeg07OjG0bcjAOr6WX3YoWdQEgVznkhaEnP2xJJRCn6nzwn8w92u/t6xg+BTSyQS6PMgxw+mN8q7qWHR8r8wPXWXfGWuzJ+mw4cHuey11UWshf8y4c2WXg/Y/ze0/UU2WBAI8Qf+N92kAVe4hlcP6a8WC2Mpo7cJHJAquxDK66chLfiJYTjD/OO6X99Iss9aBGIXULKfV3iKya/hrdYG31umR3tY67eCJxrV2WzJP4Eui558LTg3NSh9869VC53y3mPmmTHMvqYycPtt5aWWSftPQd7SRaBRi31y2cuSjbygO1kJVeQOaj1n/a48G0oGRMX2EaA0BbNfusvVyRw8YCA4fWXW3hTdDnafGJPMAkHwrxi/N/OiLgqr3+aZgdcasRAyvmiukStNsUo5pjmVdCnRfympTbEhcLMX6nI73TbfjLZcXnLSt2nZdmudt6fw941t72TfvJIHGi6tnT0NIbd3Fv5HMuMVre5fiBsqHhK5Nl3S6+kE5BhSzcXxiWuh1IS4JLxcaV9oa2+y4CZTG/g1uXbH77155/yvFFFAmE/2X417ywVa1NwKewXxH6HWqv1Sy8q+eP027pGIuBzODbNf/004RgMN4EgYfDLsO/+B9dLo+y/zfkIQf2ok0aQSGziN1pB1SAxrdPTRVmAe1sKS+utlHFFh94rBtQbXUfiZH01ykmZSBQVZByzRQHXjIfhmz5+xqqo3sYDae3gsxZ561DCI3nfPC4Uqp5y4d8GD9w6OC7+GQ776FN6iR4V9IYUdiuvSpOg3f+jrXHdv9c4h9kxwHNnsZLs/NWsB0vxxF7lQR0+VCOyySOOsr5j+t8/ALnn0+NIIAc+iHmhfpX6yRydGUfSvVkzmj2oVY20Uz1SbqsyUFOF+cMv6Pjf5yFrdrQTWdopFZOlkZp28EqvCefB4UAi3K1xwINajn+RhY/dxZcKHM6Dv4N1x+bLXmU2EjoStxY/Tb+gupEX/rQ37ekQ1eydu/m5NNNaap8U/eHfeTWNo/ElF7Dfc/qT6WW/IMWOI97px2xY6O/iakZbz03v/vMdVPU8dKMM0Hu+GKfya6iJc5EWupUmqn7ZD1/Ust0soKj/bL92+Pnhz0UWipDR8UJWg0Go//cvAYxNiJr1iqfZwdpzYvf8U4dhO7kyemSZv0cFttmiGm26tz86Y1PBQ63av9RbkSRXPUhlocNZ6YnJ+uTZEjNtAnxP/1tRcLVCVM3DeisXO39NOalFqd89kw8fdxfsyzDFoZvrvOz6+E3guX3YZkBagO8uXPfTdOQ+WEbu/iyy8gTebMjnmE3kCi58YAOGntvCCPtcs68rOQEYFELurMvFu/nVLv73etyVqyfZ1DDAWtN/SDKSva8L3/k5uGUoNwFrkN40NDLfkrhv1Nf8FOWwb1N5aUecI+7b8lIxU1IjXrp2bonIsTQQIGm0l18MQWgviXTM01++aMKJjMs7PkFpjzq1oPl2oxxKgyOW04JyfOm17bzaLnnsoavySddZ+DaGS2P+9JHBEc6McSU7KfwB3fnvMnP0ujzghgFX8W+0734xDRaazANKyEhH3I6lI1xwYvdcJRdTyfYH/CvlZX6VPGVMvzFNSXbrXp9jsjDzv1V/OHFQcfL3FSbnwUaQa+36+ixH7K7QkGUJ1Wzx/6dzXdKbsZtqXgx7/sYTs5v58F//nTECEovtaja7wfx3Xgzmx/zAygwyaTTkxNAlPouD0UfEvxH4SXCR3lWMzxurP7of8QGgqVpXVpj1bWSuvVfH4QEqmpBJNXsgH6FWTp3GQ3lpzghYXlGEbXkMxngxUA3bJqFGVP9/PHjOFZy4lvQHvH7/l06vjxizxCWd8f57Mnv2WwZCzQTZ97AGZh20LsYB8oGBnEjGkUHFEnXwnTzcMJZxmVokytrGIsOqRmuUsVpQsPRefqj5XNLkQzWcZ0v7/FwICK+YDooGC4b/dK4sgkLAFQt96mB8PGfNNSh7Pm0avonl9gq/L5ST5V3/IN37k3eeH0xU+blD/7nDujQUMLaRIncF+7+QTIfaGfM0RU4pnMnHfaSRwF1hrWtSyfz7HmgxVLqJu3mludSQXryNv9PxjhHkMPQvh/L7o3XjQQQSFVNcQ+9aI1ndO0r/nj3+rIhhSnT+OWykfV7w4PvMLJO9koprvuqKZZz/cZ31NhXiADsPyCu/K5voPXVgXHYLNjRcyii86//aSqcpmwoLi7Hz3uc134PUy57WsBwOfC6Y/zH+NMru2YCZVISMtouEPPDIiQCO14w9jftQzT+zo23O537+x3qTQR1+5nCENpQyfHdGIzmnF69yRPP52a9pJn+vhFrOfZZ05RX0W0v/eiY8mKEm+E/5lvMwaAzMmzLKXbNEfM1F5pgEzzIYLeTlQ+WtcX0i0HFoJJIT0lV1Gr7h4A/l5xvQbj56wM+nfQaGl8K4p/Rd59NVGQGCEc4IxBXcFTitKDq5gFK7+AbtS/GtjQTPyDb4UlTHpw4hNxEPdtqu4zPKnXM2iRpbre9cA1J9/V66T7o7gD+87u/XN43kcFGCTiX+41RN3cgVXZkq9agfwZMflBL470rNAs95L+S1uAI0CZUUW818EcutXkFSRBY7APvNpoy3kiU8Xo1b9GHJkung+C/kENRapdAVR88OSH6EDnRYxD8WO1QnWbsxQKL8H5eKjD2C5wWOE/CdjsbnPqp89JF2ySIfAPNlmW8b/K2Ab3vYUPY4m8aJ/9aQ0Zn0u2KkHJahMebs7CyfHqtYxguUU4oZy10HFVTGKXKEHY7D/Gjwywh8ZuwoE+gvQi6jaZDr3Jeq51hirdphxt+4FBfMSagw/ygef6oU/PjHV2jTytTLH3Y9WxhRLxaqjZcgcjmvXpKCaoMsTvxYbqJeAeU9iD2qq95fEnm2IMYdIEl3I6g95P3LfPbltbFE+rMBamPhicL6Jk2wQmg5eNvOYQmfnzBU08qr3cwKhdNwZo4giLnm4Nz8PZaO4j18peXsxwe0t36+9DdSxJtRg+B8znkPXsJwCOvxX3udvwkbw2WnzsJSgHUxTlHGmEvdwNaw8h+suqlbERgh3jZI8bTMZ8i+mU7GY/118JrIm60HMmG7dfV0bI/HoyYJa+qjJsRarhXruSp6flQ5l6Oc8PHra3cszJl+m2/4FUaPfz1rt/s29dIk2+bP8v2PFKED82l8fKBYiV558Gl9oaEA8umFt/5LZbbl+e/kPYnef44hz5PCY67OasfSPn57d3/1RTB8Uu/NDvsZNcufVNF1w7wdwUFS1/tfneA4SO2lpMXshHdmz2SrBwhnxisiG0wpSkV2mbA5PVK82cKnW6oYxb6493N3XjxMQeuyOTM4oMDvmfJkfMAbj+C23O1YeH3OwPeTzK5Zt79B+yd9ebN38JyLmjPyo8ISn8WO0dNr5Z5TfL4Wwy7COjgeXGflOfR3uBwWW+4xb8ZZlPaC/6ReJNfCfLSVzgjCrEywSXcxd3KhnNMcsushX7yOT+jG2n6+zVqvj9e7Pt/TT8ADZY/LkmOKoxGlGGYEuX7ukU9DWhRgexL3Wnb3IEOoAtgTResedq6BBDHvk6f3znd4Q+C62iwOFqwFhtYePNrFFnF81NPUI/zgznynQ2iH+MjTfn4/MRH0D03478zFVsd7brJucltR6l2PjwkNGiRJCNLTOR5f6j/jr3VudzR/vxqfnchMK0DNIOvIzsbAc6U9nMXVnXDwSQg8TCA6XnViwjH6A4gG9VpKmwKqAM/fvK4+xTb0nglh/LF5avK7Fa9ChqtMZD430t7Ex4yrORbcl6V090jvWeevBnqfw7vSQ9yQ1WHu1C6b+9Ez2X8q+cEtGHNCwHFTV1RCybcyvvk60JOIec6PLuprxZ+Risa+cdE3eMGeEtXR2YuTa6VU5TnJvH1BEuyiEymA/XF5vjX/m89S1WwroCx/kd6AbBFGtlcf1bLwsXMazF0Kxsea3zyqGiWYyP+w7DXvnvymTclEZYfeLkpJICbyiS4wsI92JVIrPoVOJqtN4dXlBa9jvOgjcbVy3JWEO0X7FSpJWTfveij5smR4CPSqbt2dxAFRhkLb4UqG1lhHCmMYeeScIqRWn7KXDrqp/mkn6U6MxvPcaq6v/SQ27dALrYrCgynH6exJrRmnBF9F+5L42n59i1l/nNHoyvWgfSTqRsv+3N39kU/UoU75PoZ/CRMlvpjFJfDA+gfFIRiAu1vcWNhhygTnsEHubBZMpKfD8Vm8gPHKOO9GVuv5Ykv5rFXy7pEp9xxdsLrAE7QNpzaPxNYLzRWZzGlUhusHdDE8KqLJe7Htl1k7+NnrjDbXFiqc36025ORWkn8Ze9+97xlHZLQmubl4DywkPgbr/GUe1CesHXlwK8I9vz5x0cKErBoad7B6R/KY7r9+x1/PZf/q5gV3O60hLPxcptlad3b2BtJsMkut/sa1n1Leb8yctvn1NKxj0h93l92ws4fd2JqgcD9fSXUJQ540V+g/e3j59FLMbpKKRaXiE3SnX+7syXxTOM0L6GDvrbXx/xB2Un351GHc+oFo4bSyu4I3EtaXCdkrrK6poI16mN20nFeDdj3LIozS7/tSPPM0ZUg8RHj0U+gqucgV61yjeatGZRz4B33Z1OlqKcINdJmIjjVl8uf0J1rcT/I6WKryQwV+MPjIaNpq3sIeP26/0bU3qbfj+bMTCtiZ70I31yhqvl2HbrelLI9UmhnHvGx5lDsN1rbIdfNJ82OVcMuAoUNvm/dHFDdGG1h+6SeSVrHPjbMz9re8y+o6kE3kTozpkakP9ofGLlGGi/87G0i5xsUUVgzJwlRnMCY7+bCF3pwVnr2U1rXiYTbA2SBff+O0KEe0AZnUfT+p9Lbr0qKdP7DqZrfk5fKf3p/+rOjs+Qsvly8qmh0wg1KY9rb4eQFiliCHg31kSqYtn+pP3ZnuNAb74JK/99yN567/Eege+pe+pAtuHrbolPvAWJW2t1S0OxFLbZfYT4hq7zeb63KTyxAQovQFaJkRO0LV6zNeW08eUWm/CCTXhTTk6XTPOPV9ZRK/UC9bN4JbcTOeX6v0PNOO8ZaswSrpjCa0EfX/L+X5f3IU4d2acK8/iD6vVzBwe6HOMcuWvrjl1bZn9mT3ffvL3lhpgts00YzTOaiC8EWvkJ4qXsWBbw92CbZDRK9b77CRzPfq7KH8UbSdOqDXb9e5rMRWzfT/3O8jrNnle0lW9VWmnIzNVX/anEOu8DrmosDapZLd+S03wv7p6lp/HaqDbTSwitZmzrmY+74rJKs3IzWzFL2boyrOkUOl8vQNEdTyipWdhVdy0XKo3ZvSqVxgdmZffk/e2NhPJos2uN8nL7HdUtAm+CAuHC6noRPU1/cG0YUNPGZgBGXjiGONi62xDThst/Jlrx5LpMsQZyK659zO1zW63nXZCSBOu3casx6bg3kCGJ+rGLaMotI+NplqwV9elv7qHq/Cd+k0+3CGlrpgI40hXEyly2Rpss9ZWtaj5cuQpPnYdYLyIcXvOzbgCRxp5Ts6VeNDsxLWoUZ60cVjnHBG3ioSeMf8O6YRc9mJ1VDJ2fSLNrFkyjWggNv+ebY9ag18rzvdX4Xwv/w1a73QAI/VYWHAImnbVADeiOaw/8r7NL7O4v0xSOK4FriO1MYC6f5D4L+69dXVBaCqlmGEjV5F705nIf1mnM83t0HXhJD5lMuIj3F/S/tM63/ZRlcZ4t8QRk5I2529gklCv4fXhoXaL3Q6PFa/l5Cmc1Y8Mfk0xqUkNli99NovMm5uz3j6Iq+wmg41crP+o1MtpL3G4fBzkBCjlJnj6m+lO6kudqJlaB5sjf003If/7eEI17Mz2Sr4Q+0P8XtStXxzsmjK80p/bM0OII7TscCMf18SSeV6ERt/9zKOhQ/zPCkyFuEKP0M7bP+KHM6YQc6ooneyWtys7ihmg+XtfkMytamU62pzkVcJcgDvCmB+hM+K/mFv1ZS/drTOcIc7wIDE3irOP4PgvQ3EZXhrm/5P/n9BJ6xz8mP9GaRe0uQJLq75g9SUegpdvv0OT2EPuHV9hNrBsZPbfomz83hemwjc75uNQeEfX1aliDyCMyoGLUyj5+xAIek5HNOitxHpE+qVrvmu55pR/TPK7GvuwDbah0fveta/A/styX+AjvIs4BXumw2Bf5usFwCKdGCu0yZ1uY2biFkDv8NiyW8GM74P9OPY3K8NCViBVIq4M4sqv5xl5emb1ka96vtuj2IaEq7jvYrFBgVy56/FYn55vgZ2zjuqwNSOaVqRkcRd0Qxq+cstAqJ1c422CV41VbHcIeLzOoAevMJX1SjfUecPI4xddq1olmlafuEtD/sVPqTipsQavkPOu9rFCZvZGl0sLfCbDKjz5qQfdMmGuLf9zgzzT8ccf4nOtaux2Krjnt8341M1WZP3jiwBLDhRHX7ohH3d/XVeq+rFWD9tAX4YAS3Nl4Tvisk1Mnv2zSTL0bhqAExHj8Nqp2w+b/jL82UpXv+Z1/jkVVkOJILIlcvYyxML1h6W4NFoJhgZPN1i37uaR157Yx7mcrxuY8ZSavaU/oYUHM/fo7j0Y+FqTqOvBQRGfd5lYt6oylDyreB/OYl09bRJ5T9aVGEhTazGvob8gajEQjhVapUwcUydThmPE7d8NFRtG0P07v0TxeS2EKiRSU+xZVbzv4iDWMUdSxqR/aT7yHiTFH4R+1i8iLxep+Hk3YumhfSodTPdW1/Poh+TJ/w27GDNR1NdEi56gDat+Hf4bGJLfHKH/NEmHHE5/GCH5Y/e9yowLxJiqaPifdvCKwhsNgAAapNfTPyNtr1h02/sxokP2bNJ/BeqeQsbNw8YyXW5xH6EWrnT2PwFWQI9Z5OxR7EF1/Xs9aYdcm9v7UtuvPXptDTRvCge6IN8DMbdbVZ8ovIx22eYHA37r/XxPLARacVzWJc4HoHHJLKHXwbkPP1DLTtfiWRBvJ5k/UHwiR7155hL0WQ7yU9HjV8Keo3uTcENFo0nPh8solhKzvNZC6QAxkEn+I9kVYxVnXXvJS0F38GtmDXfFDxrujaGQP8J6IAk56Sqm+nSM8tJ+y2nwEaSVNz8lqctd/wce41beJJ/njwHFMWG4E21lI7QHm8TdcT5bA8Rv5p52DDPyLNztmomN6P/g1l7baRjgjO7ydEpYJ2AqVkVKlqzyJI0iEkIX3DLWHPb0hyhC0+26VlcHT5sS03GsY5u9uZE4Rz/iEwr41kCj65EKPBLfar94E983b/gP1HC8IVrpzHdwTkO/39JO8YvhLSdWubHXd05g2NUbfMYp9gb7dX2tpN4Rstbwu37E2Esdh33mcSdOd9T4NVj6TXNbkot3ls0z7C2rTgwklRPUxQA5vpFMwn7teXe5nV27O/VQ5N2hNfSD8+PENkzpaUXZftqOiLEjyBA1sDuCGWnaybNP0obRW6+BWJFcol8Mlr5/2V2O/C1EbEl6SVcZB7rip57Uaq2wiur+Yry95JVswf/b6KfKszZG33yKANZE7dTxmV6QHvj4+cC3US1Ta5KdM73btly0VfPv/ms474d8F3j5kKH813RLY0+qvlg5nnM3pXbJttrYE8bLWKYBFxac5naBw1KobWzzs6fg/EbFtoH4HpoaeuTsOeHHJOFD7H9TELa9W/OuLPmR1xdzMaGXr30CgeItfZJKoJ1+UoLee9UiuXGucxhOpdyS06O5XE7c40+65+T81pFYHNVCVc9UfuZ7DlhIdI9t6/+u8hePyNClXXd1suZV9KHkGoXPLWRjyLhxlCUAG35S7NPLU321vJWHEP693iLH3Na/xp2Op0uVWN6Ox6EcBcSDWC95DVzxpREZ6qnXLGOXw31+Q+3/KCE4jIInJ9i6UA0+5e3ZkO3waxU3nXwkek29LmpRSHSZJ+IaAC75oTyK7moLCakIga1gXmVR5vsvL2TGBBa6Kpc909d5MasEX3Sr/Yn19itWP30Y5+DCX6d8xOV5aWK5VakdbzDMob4ZntJdN0HCX3K6hutQxmbx5kGT7QFf3pAgFtL9NmuGx9XEsfwi1vBlPBxBpsjnibBemmldrsuCe9Y7MKHVnmcfY/7yWtCSlls9Lx9KxBd7EGl7LUunUSuOAarCRwoH0svNA9NXcWTbE6m2QfG5ksQwEscW5+c0gfvjM9sRjyhJAdeDfaOAKIYHdcjP3DO3LyeeS3fmm99ZKGPYbVCpvM4jHFJYIL2CSf5PMXJyj6gfxBCvTFSd/lMqSlpbJPYdfoP3umcT1yXun+Nyjw/uvYnypG2iXd58dwirWDnG9KAmMjiXVhctZ6nlY/ixeUyCeW+aIiEhRlLSn+KXLJbZvGtLLlkWp9Y8M/PtSr7DujxU4rQ/+4PWqeEiZyvOSHrdy1h5T9clK/9XXmVbIC+3F9wRry7RrymUVw9C6r4Gxpyd73DI+Jh5NmqCLhO0j9V+uPWDDiLjXvntO2Yd30YYWerT/pdrzycrKFy0CFbtf8XOPJDV9rHbJoHUfJcifDnzUxzUcBu5d5SlRpvtCAL94yIg5QQkCPdRHZKp4LWCdBA5klXc/R1WArY7Jg4A9l9KnlOSZ7ZFFQ/Pqbo5Z7IZsXMF/x8kkaANIKUpveUZRkl9rG1ReCjEQ6Zm9x0ltG8/gDO5a6JOcxHK6olsB/AmL57FnnA4+d+caljFrs4UlEFbtHL1pp61EluNbGo9F15pyQNJgA6HAVPGWDpgSf4aSVH67bhv1jjQGASK/+zlrMQ/wzAfj17Dd/10E2/RLk2PHSxT7Bz8EqLLG0xGspZ3E5q6UJrCBAXdgjIiX+HdXUpHVrEpybvdhHfr583mNbmxV4XvJm3PVkPaOqTatvSn6sP5NqV/IUZXCZpB+T3GBBc09hIyLng6vVM2rJRr6ZYPr6zZcjRMahx0cYeijDoI55H2SulMWFaXUmYzh38aIDMc/3dbR501N+tbvd0K4eFR0S93Hf+2M/5kzCYv3fLoIgD/EyW43GQj1XNcKh1mJGu3EIX5IVOZZ18ZpKDW211/Urb2bJtW0T+hAamAEV+P2iuU7qKJ946iTuz+jv6g1hJodVamSB4YI5I4zFnlLZ7MqBiHTOMTgkwwMDaheuNj1ft7r+XEkpSX/k7fnWJDX6dAczP/TvbWaQVtlC4Ovxmycj9h8P8yYofGVij/2+pYLQKt6VUXbI7LeekMj9fV9stGm02RogfuuST/59BjspWcMMvK7JcaOTwCbXHjP/jW5U18JPAq+Mgu1ZR1HWrcuGorNmzgH7rAaA5WHF3cx/Bl9v0oNvdFBzg0S8Jqew7xePtTukoAzNgU3oT7+D9WzEK4295dZDs5PU00uVpxbbnM8+37KpF8zauluCuV2Fjy77XhS4ot005G6OmRz0Ci3zTXHO3S8qLe0m1lE489GTY85GabnlzbHObkAbv2HSDi3OTeeZigUYGR+S1Zzxk+txrqBEWeXFvOdK7ng9VvK5VV1j+OHmr6lrq0CrXNf97f3S96a8wNFtacIHJxwQW0u1TBqHsKUhJd+mMs1yknNTRHxBIpwsZ9tS2VXZfWNbVb1+RM/sPzBbQrkQbzb9bSNTvXPZDzJr0CIHJ2jK2yyCDtsPsswvdVm6jUsk37y7OhjizasLa7Z4XRhng8lpRMyCUTjVY+Iyw72rvL7A5PwN+ej6ZWZ5S5vUM3/1vc8/PBDBMpEde7Rn1jBrhGqMkCZZfW3HGh5jY4PGIQBdP9MOJ3n8CuA2N293N9HyASTI5AW4J91h1agJaFMaxPl/VwSY87VETtBCuXVdAN676HK3uwfolBiyPQOS0s1dSOt5PDfI6iT2K5u0rWY8W/20t2q4xzlLz/8Ek9Bn2vTBT/nx65udEK9SGf+o/unVrXh720SrHUsDWPVxpP32MG1dfN7uVVhaoz9gMrMsTRevQO1SSmmJtdodUyD38c0hzET+zHfe+Boqhkz85VbWcJ+YD5f2xy9eKynrVTna/45mierqo/HkJxNSKtmUQn/JaW/b7GX19Sv+Cn19372tYP8CzGhoFd4zIL+9g7dkY9NSHVlHHl33SNJbpCSVK1VdU/mODBJ8fcXBVlRmQTymH02IE+p/A+K+7NC2MKNjwuflIqWO6tVl/WhIiI7nqfv5FrGs3pU3xEWutr146Xfx65Z0oWf5li067xRnojv3j8JW3upG6eKShRwCGOVF4qUagOoR/HgvHN1qv0w2FWfhxvO8uZlnd/AVVw320qtxB2uPD397EaWKk9V56l7jV3r0Q9oCqQoy+OWzkMO9G9Yx3JRUP+pC5V7DkXGzJ/wdcGk89riou6hywTAhCrecoSQ+6l9ndf/3PiaiD/XGFepX/8pjfuwAmLGvXsQ8dzOl69TwnUVMF/z8WUG/98fP8cdQpAjM0mpFwu5Sr71HT8LatdLJS1wzcHB8MqHHZF/LXG4XPW4ppT7od92VeO2Wu/C33WnTaPwcdhSXu/C2vXRT5dzVeJoFB0LXj4VPXzBtKAeR7AxuuaYNIkdH00Cozz7lvOYefFWNqukfTkAo48MT0tGUT7At18fm6+P8ux76TChAG+4Jtbm3G6QXtolhnObBn3XN0Mr9LCA30QbNRDrYMsSXOeVqExzTMQcvpd28EiL9TP22+3/D+tCXRjrcf+Q2EYb4Db+UGOJTQts8nhftpIDuQwhGmlv5MUSgKmzn1DYcuxzkt+PIj/da0wmr1mwUOLJcYSbbSn2QnRDr40RmmBrp+9ZvGfMZJKLZ3gOciZ5/cYtarjAqkYffKaL/ov5bZuhs0JIFXGt9jlyC6qDnBKknOp6oV9h3Ptq+aONiS9ybxuZovpmRXb+SWJ9Qmecz1Ur1+lkJSzpO0LPu8+xIR167KHwJsDLzRWb4QjXLvfbVLM/Bms1WYcVFKBCM+iZYG6eEoc5QMrM5Cfi/AlKrHUPWCbECtUV1gRQVBgoZ9KicAaE9bsw9DLPRpFtP96GYBIMO9Czi1ebovrA5PvJyJnf7odEHW+6t6C/94piz97npwqyuysozmlumLytGWbM4QuwPxIrLUzefEqBYsFdfuQ9ijSBLEkOqao/MtqPxKPV99DucFTwJYqduKzE3ItaBGXB6lrDWGGeA/htp6YC9IQeNUUfT9jkaTc742Hb8SuZagaJoRoNLnt4XdvCY/Vl89rNlyAycZBRGDERvSRXlw3nXN4PRaDquf4rkVxkIfOebiPPw6sTCfWJfLVZvuIf73TJ+l2h36WEo34OU2eTxZ5fiED7Mr/kwIUZg/mq7Lp7HvKZQHweUcru4ubRra6svI+56SS7cFSuZSvSx6rakLZko/CRDp4nT/63B6a8JuKLIlMB/2wTD1eKRjIxdDSlvE4AL9sLTGbIwGWOsfzoUG2oLltDoh6eNb0vmnf9LAa1sfFUZGq3jk6w4XYvttSvS8GqpM42Din4Mylj9bNKpgmTeVJXBKg+wEdUg46/73cmGIUlx8AH75Ay8xrbFGpoAS5fY9+77Y9msque/dtmofWdHtliIdtlaIrv2yLB7QRDfegqAFI+Os0MhXzJ1veD4lfrVR5K7KMXZjwjFZ+3sDdiwZh01CwDb0fsq+y98onjy70loUAN/bLklpjrBl5+onj9sYG1nWXUEk95BJGwh62YqzCcdZTtviamVPYBwj9M2/dRoLhHpO+cuK5kCtDoBUKFwLdPAiy4J7nPH7YHH7NU58lTO/Rj9DKGYbfNTgrjkVrAVRcCir8NP8EFl8bL07jpq1rI9LAEtSlQ7S6eX8YT0O/MN6F8i+UY718hIlJ0+Zm1858/WCPhxfgzOW3H+8MyWwuNKR/f2C0pxq76eesIpndv/RoeQyarXft5bYUDkmWeoUYOQUGE4pC5ePoqLXnBPRhjfICjLRhH5I5WQZrQLZHWfmZ7Q6IwsT8GGhUhSgl8LZqAVGororUcXJ1V9j5hosmAO59CK0V/iiUSVdG6vV35sOQJfUnNSEZwfnlqMrDs1lW1lfxWnv5USl4J+SEti6HifCmS8P4ItGR+NOx4BNYcXovr+CVKWlOjtj82OaWxlkVtmNmFwzJerlRl+Bi7jKs+SvmOm8LVmA6s/W9iHx2qGGmO/YFfkcYzH7kCFWP51ftqKqVLwXUrhXi863p38vJEB2JGY/GFPGMORSo9nbc9Brs5lgC/TcZVKbopt7I1lw6uHebCq8f+euVIUq0TDPg+y96hS+tgMxZCQk4ok83d1iTIN7erOdN9LO+MC/DpbLegH2PP3SdOTWVAiDVzQLYWDQlO0ZW9en1HR7wlzwrpYoliJnjo0TwewgB7pkTZxY7auF+ZCftuZkrlxivkP+xseGDiJ1Evb73m4Gkj2/Rq6WvX0L4AtK2GfsWs0Je7pL1BwZv/Q9aNFUKI+TH7voPw22+pgHX1eE5d7TtqvJuSQkf1Gz5nYJi1i5DLdOA5bLYIhtXtMb80diht1z1iVQmoBVlVjGEjrvK72nLS8Dkmt76cOKGeQ6EUYX1G7RfS+yzOBouw1S6qhIUawzKuEKjTD4XLj8T6KSODhYdNT3v4MYKT2a6bXzqi/PzDBRm7G8S9korwubobPMcNm67C7sqQ0eyEUIbRSGe3hMQy8tGUXELy7qtkKPUQFgH785vCWns2ITDUK6GkIa7PKYgqzlTZ6el96aoBt2MqjP9MzUB0ITt5+fFeId5Z5HuU2hAxVOpGbg0yxTtpafc4dvGNP7X+ZAKdlQ9mBydd5YB+DartSDB6hO2rTWLFsVtP7umd/i7gZyLv7S4H/Atsa46T4PQbuFORBtm92LXucIux1QQ8fv7l9t0GiFiBM8LZrrerxYX2103cfo7zY+k/7OOfYJcq33ZYZwuX5FJrD0v1R8n0T2J8enGNl127d1ucbu64cedl7+RFBjc6RSnYPNS81jqWtR9jr/g9sP6LPCIw7iwxIGScZ0VaRAEMYqIkgQuDK0spdD5z79x4vNra22suxxNB7kjWCw+72dR/dauRXyD8iLO4FH29RQDDVeM6OLt5m749JKVzjmZBZOmAe1bKX73pJtZoku9tOT8NLN/vstsPU/LYl5v6v6uJWpx6f0OhefCLg3by1VNXdKeCTK9ie8l2ZqVLZeE827gR261HqowQD74iWYzYnFD4/XqMZdk7NUwobOh0FKD4jxUmaYRsqzFNshNOXBTFAaYoglrgprHiKA7tFWdJHuuhXFWf/cd7InNHY59vSbCczXo5MKId+Y7Xxzt3BW6xjQxdzMN9xxnIqsPu8Jknp565tuNcdhigfkHFkSMxGOd8brn6SmdT2z3adoDWU4+hUaMoVNzCpv9aom//4oUyq+ORZBg2WRIJVtjk5b7x7RtlEa0xrusE2QsL/oxMztB/2xlju6mtJsWUdYHDqHhLW4vv/VExEIzx9OHzwjmtpvwT7+IHUb1iZ8OiXs3PUoaoF5WTOXrRaYOt6o9GxsZ9Z4VRf6XpkvrHZ9Fph9RjH8GAqbvx2geClUedKcG7vyi96c481bze/ITnTv7GXI+Hq/JvRr/XcZ2idpvrFpJIBzIsunMxnI6dZtQT6vuWrZmpmrmTQ4bpNe/WkvenqLH2WTw2Fh/eGGZBy5/hOiAVH9aTpMq22qtmD/iV35IetS4gDoS4NhHcT9GWTncKluvGygKFxAKPbFx4EKfQQy46yXz9+nJfWPvDWozMVLz8dn86BN+nYkW1AOgmlaTtkWbxKy6VfCfDq9FpOA1BVIl4ID46+ab1uvMVr8TPSSyu5fzWDsMimUeot24G5de+8fazFwVagzu0yb4hoPTinFVfr64nc8B5RzFGANTe9rtL2yyl03KCV0RRAO3fjP1B5K5B9/3Ilsv/XhUUyg8PvzjVff/g1flKOh2+pg1aro/GgdvRoIfKFmtcwVwvGvczeDMtcgU9ovKLdNdJnbT3OfDTWb3OSUrhv7i2ea2lsn8r3cRddFKi++9WnIlV25s/69pWTOD6kVFrDk/+pQyhz+tU+5GsJ2UQdaDxT4ioA6lKyo4OrxdOC666Gytpz/5YfakJJz38cdAyQM/dmwICB9zd0anS/luHUBlxo1Gd6VI9V9YmA2Jv6Mbjuq5uE0coXZuLGEnTZAb+aX8jqwfjLJviGDNKUYdxE3ooyAGwrb+n7FZO62kD51Y/kf3ZgPKZcM9wD8weO33x5v2zhjFBxAVbF/HSn2rqS2aNml7utPPN4ywg7gqN6SFa/1yBLRI2mCPVu2e1UbZKf4hvxzj+HM+4Mq5yqpCRB0QCXrzbmcmCmuHLOflfnDbaN/SgxVTmSb995NIXRU2Jb0cp+NnRYQsIuQH9KvXe6qF9F8H/Sw441kc+TFz25LzKWzCj87HtXlxjx2bcmF3Xso+GOhoSrd9//ZWZKESzpgo564+b45TuPxf+1UpGQfOz6Kai6ehfausRUTnXBlbbevp5VcdcPzcZxlCmsOnEKHx6PqpkY3BwWCByZSQbhC3fevg3SaN6RPyZ3ZGp9c52wE7i2I0HsPns0j8dw2ZvKGrx8bdLdSYcRjx7nVT5girqdcpE6NapZYdeMf+8zkz1yq/Xdno/1sqQYV1VpmmMDr1nBFZWVU1qp9jmkiITcMnDYtI51oVzsDi6Uwkv51/Chq0h7CpSAkkTluK5ychMzoi2VvXEXGJnqxg/2a1hEtj818uz6MXJjV1GNWCt8p3xh2ZH2AShvbn/XPQLuorzgm6xXEaTo25Mthdp0mzyPRnPz8R3UuLNsMqquw+Cd9x1ehS6aDhcDJdDhKSwoO8GP9exFOAAYACc6+M4+dW9hZuB8ppN7xcuT401Cg4DDy8S5NEphp8zXtG93+60lGdUc6sqL8oDbLT/S5AOUmjY9LUHgk3onrJDBi6dsOkuq0UZca52qfVVvXqPJ8Nk6EYuXljtbkeRp39HTc6j2nZPzsMe1+CapH7ucthPS4RJsIFK1DASdRam7LG6qvql6z28gT+1Tmcr09mqCAcZyOeGR8puXalt+MAFz638ulKHayVjoXs8CiRaqV8CEMUv1BFrFR1uHOKYcUsNVEsVSELf4yamgrAd6s8XBIni11oggBECaDvRtKcggYFd+Zfx7xy+T7JQRmY/tWsWTj57WbYgz9r+VxMPaXF/2BCY3W3dQUw96Ih8XlcaVeyEEmrm8REaFif3oHeaXMwFZIOsDnINzAw1QGZ3yJRB2mD/HhcUBT4LeQEAyqcYxveRqPM3YWv866Q7PPwll6frY+ejBTUfG3TF6DUIO49dhRT1Lvu7u9sVBf4xutZ8BFPyZRI+cbdqEtfygtHt/Smkb+W/Rog+uXL9udBLGKb1Z+vM9RwvsHtVJ1DzDYUV0JyO5zuXtS3tOrWqqJkzLlIPaN6jN1NJNIzNzbsswO65tyWy7x90jfAZg9OoFsdm+EKAtSv3KgKyEuyQPZArTV316wfht4Yd7lv67x6X/srg6qxihW7Oo9wwudobbQVH26j5VRIzgZe8dPhMmgGzwhUBc7RZ7N+uL/HLj/c7RmqHVWfLuqNYTHE/Q+w4mfEabxNXM5ymIUFDPBwS6e1/66gveQEZbNoOzHaYCxgxl9wWj0a67F17B1XBwrDsQFcc3imbdLoPz5Gn2KIqNt7NRbCUY7rK99qyHvCTqAqe9VCOxtQqerY9GqWaMrl8bL/pIkNiz7ES5EIR7dS0/WL8cGaXptv/4eGcmQFsP1aXqfn7Y41oMssXWCFx4qRBDo5q0fkPZHb7Lg2tx31/iwbx7pyL5lbrxuhNcRkZWpESTB8oUjjz1FJgUxdRHLmTt4/jq/hm3ZHRg1qXQIm8s5Gs/8+Ks7bct0aamfrgslQe/MfNwpWKgKWk3sMaM88OFCSiYeVinUsNwu+XbdXytzL7X9DRyoCzrDa+ZeCyKHWkOqyZLBHcMhhjiMe1VgMtHFLYKhPn8n9C/XOmur4oWk4+wyklri4pmCEY7ys8p67huawfYNmtVGs8xfa2KxW+bro3CJ1d8SF4NFVN0ZYysawDT8yPqHtb1ipWS40JQeUdN2UIwWusg1I1PjvYrb0JamdyZCXD+C6lQVYsoO/UqtnrC71W9HYpZrKBW5svfN3J+Zq0IPp5HZ8LyQZfiT0VLqkWMP8yTq5M1E8gUzdFL3pLvrHkSGvT6t8SN4MrcEF3xL+4r0HVoRj7+6wCwBTGfM0BLBAaELW9Zajiq/zXR5ySt5EH43w/DZnGCv9Slgnah+pB6aEYWPXDS12sXJNVYNQXrlPsdd9hx2bvnqfk2nZu6rrg9b9sd48dxQvkKRTXtf3xG+Sw9VoKYnT9509wXwrJE2FKBNq5y0xwPA5yUiRcHbzVRzw5MuVb7/hhgGzf79QY2TV/4E4TFngrW6C0D9BkfhCmLlj+4P+c1zX13QKu9MSKtrBWTbuoAYxdF7isngcGY5eHvWhdnOi83SgYqletK8yJ9XkCPiCGjkYFkdy0HNjdZuDRZwFjxNoFtRLkecCHFsE6vtxJqvfAETb8qvhSb8/RfcEiPEwfVloS77S0a3qzqfmWeQKQZhmGySBb1AexRRoVMU2/WEpxSOI+9a3STLm9/VZFb4Xt1c9a/N49sytUPBfSBXExaZyHMl34l+z7q/3jl2mzPLIEzN+NXfeYOuXCBoTLf15/SvtBDZnVoFb6kinPmdve8CbkYDXT9TN51AKh6T+q8UbQEgwIkPpT7fqQrLwGQaUPgm37IaFaSt0Pf6H3rjwCjuK1KTocZQj2uOBffIZe9N2+87DAxzESokWss40aVl4Jeep8tDbMPxdpcNOLmsTkHbugNGWOO6H193vlrPKCos8MToArZnQ0OiZbRTEjOY7la39m6I51V0eQMMT59ecCKN7xFvXb/dlKAb/9H8hRQLj7QOc3PrMXae8W9WzwK67FLX4EsuN4Lzh/2DjW9C9lGMbjfojI9VNQB5wSP/veVharFli0MCwYY2OyHF2fLl0dDmT2cuTnHuMm5/VhKq8Ug2ofCjj13M8IyO1vr7f6edQ/23h6bCUWBenoK0/fO5iS53+rARXqBLKfoDIdanXOuyR4jORP9Re9dlSShgWgAnGHimEhfW2+VfzxyZ3MMF7h7Py92hT4Z9TPtmI5BML6ZYbbJqST0WTsmUfejz5u1DvKnTeH8sqVeh0vGla5fevxMiSnPyo+iXlw2/2y/swnW3fjR/971J11bzLyUTLg6W7Rq5Fmap1rQvtdxqelDydCR51QkH6hVpgW+oJsMx7H+WieIllKkFhaImwKcBS/iruJGr46dSXnZGbL6ZasiR28pcvinA/uPgXek7evkbRvUkmwlHOwzI2dGFvm/IvgW0F/KXmY3S+w8P92WWkFOMPlVSt64r1CGuRkv4x91TT5VG/BSImjbbpEeqVkIWoUuRb9AUJFAY5Ge/ZswzmeV2pzjM9bGgDnkwv8HOYs6wUp91HWr3XR9I0WpN6tJp6Q/JFNN1Y/2o2ubRa1mKZwu7S8SX+HROBD0VfuRzw33rxTdO/wtjl3P/vumhN154nSjdkNV1XRqB9P01ZeyJvsnB7t4eutSgKL+od1ecteFUfgtBx5B7Zn/KYJINXs96pAlOH8IWh+cldu8d9h3Pok22LlQO8Zi2ufzOIBncOB/2hXvcXOGz7SoCNGZBLhnXVsa3k4yo/iaxqvTNozPPTKTORVxhnZzcvNJhRS2YBreXYOyiPsq9onl8WmVtBHHrWqGExuFNne6mJTXGizN4g2hj5N83omzDEkidvjt8Lrc7UrzP1tEizic9vCcUnt4zrOzAL8vmktxkliL0jDpSU65YJACnWZ8Rok1j/KGqWyE2jytbj6jeEcPwUS+r1+GOX6tsgW7LBalGkPJPCph9vhlGL3yv+sPY+y3uBfENTzRt7hAz1xa5u/mY3kPv/e2Idjz+fC8OjgXTkzw8FY9W5nSN23IfmLlSvs4us1eWCUfHZt7gUjORVyZxgc9IZbs77OfifsUSv0NQsg1X8zG8+97Ovd7yw7HuCJ0GvGhFaoWlO4G+gFeQG3Uyv1jy/+puakK5TE8p054G9vVRRNCMY0e8vmJK64+vtBU01X2TX0TX9eDep5jG4E0e576FVWT3uztZCYv0acG00G6LHsIcH6vhUWOC42yydVVTv9KAcx77vpmRMeex+/kBzFDwabPh9rp9/cuif6sN5u/W33LK80pc7Ihg2sZKUJ1sEjC50ul45N/PoxX7NruwLma5QN+gBxjteqqYlfE/l/G9QjtDM7ek8mAXcG/f3uVuijcx5IZre7fvXSUG81/rQURC17kw0u4NuSx6xYWM/B4nlGQlUIqfJL3t+YVCYiFUvuhRXFg3v2cstOp/0Lj84Xjr0F5audDEfh5uJnGxDL21ejQbMI7HlY5dgTpMs5fiviA2aenhYkDBuzoSkKa96si2KI3xSvc5PgaMI8iWomOOofuiV8jVMuXSNvbp/0+ZdC30FU0x2lty624lF1txMHYefCmKcdDXjrFUC7+PqyeZbSCitimgq7fl3eXqeIjOI+rfLNQlust522dCa+jLYY4GAp0FRhtQEyrS/Tt696rwS1slXqVzdG1Ueevtag0Q3FJdkPihvCxYoBKSfrH1RAoj/QXG3i2KL7jLJJhyr3aZvBf2vOTt1J+UxFfUbdaKLQWSZ9IloXa2vQFTF3+GqGzJDxKKprO/qAKTOCpTlqQ30YHRmp5WiaIma6OjVZSLzDdAaNmG25gPXyuEVdygc1rIY8RSWCB9wHiwWyf8h748mizatyXKbaZ4vXaN64wTlZ7AvgbostVmTLyMY4dMNOiWG2pqrFk8MwCOgWV1HNY21u9J40QuZ7qbH8df+XZyAtcLelPX1yaWc9nYfUZnZNK8czE/Vvxy1zqwAneH/JC/d8pb9xbhGmvO5oCL335t+LsCmRGYsvCzx4Pkdv/b+elP7Bi1AM+QZvgR7ffqwJYfi/s8mjy9iIeiRBF8HxCPpQFV0KnTXKMqWdJVV0Z2iGpSxBXQIdNxcAQsKuqqwX8FyY3jhu45/7ttlIbru9hidIJM3WnsezMezZsPKbdLBvIb2S4AElFq1+PO/9jSvArSd002x1+46fei3tlJKNyZ1Ry+ZbHALfx1eE87uzXUe4hkzz6n2uSnIiPEVk4h/2KVOyRw8mGnFHgrBSWdSTQ15GjTLi9lRkNQqISZu9PPK+fH6zzHHXd70MG/cB9//l6hbMFW+m8vFVnx/edT/Jfv/mXXBfvPxrD1hZTSkjdMwgKv4TL+1J7tA3toZK+JrmizCI+GZmlhmE+FyoT7qMltUnPvy6hApQcATDmBLRQcvMF9MLMrt0Vd8KfYXxw05VeTN1TAHEX0fa+rYrYpXEMkQE3B+jgoxi2geYji2+jq7gWDbS4QOb3wegYwaMlTxifYva3vtBFM3q68iKUW9p09hN5pUpNeRO25fsSqZ9m7e1z3Va5eXv6gPWXL0OhsLzzdja8TFY/O9W7oLc9sj9BN1gF9E0tU0kv23/4USh/2yiX3Bt0Rz/gk5HW1i8wpddTav+96KhKrpCtPfUZvkm014E3Mf0dycvrcO1fm+DYnWY8YkxL+fhsDJnZSs0r77RcC+Kymrq7SGbeJN7ZfqJtiiCRpuknLHX3+B5aKF3dVsSbl/pVgp1GNxHHy3eMqDHDgoq7u9MkmbXCWK3xuZd54J0YIG3sRqoR6QdHzzj2sJ3uzegMxHPG57ttkzQ3D/f/PBZYMaPfm7FPsVwp9mLUotolsuBUdbW44f8KqyKoWiot1N1Z92khOd3/lfMrNrOvI8waTmrKo2buft7nZ1/Z9xzezGbvx9gjEykkP38dPBc9zB104rH4taaQH/tKRF14+uYRpBH5DeBAKQrReKtg9pSWpCVM/7Usd30xS/gr7k7qa/0mAG3VnQ2gIaSw67XlMvhEFtid12/7mcrZOdF1cckje7/UYlm1r6H40moh5Ihv/Z21TPTbpmFIUpKncXYwYe9/uE3JePK4Nt2x0dDyrs1Cjprzv3VK6cszK990y74D4k/+DUGAqzqzoU/nsQQ3zTYiYbva7F8v4MGvJOGY82rL/YwWWsmwMX2/r0ROeQJE4FyZ4cYw/ZBRmYxDUuQguLeN75nfqf4X8vzd07tgo9JydhTeptzk3vY4Xr4MxnSejkbkC7C54tb8jed3i7/2Y1oQGQA2WI8yvdvpz1i6JHoJ7hlBiobqzcnZiJ1ZiQkJ/slbv9fUe+PRHXFN/oI4wVxJadzrUxRuc8uDD5DXN6ef74uYBVlx2xrYIwouKo5u/nb17pwm5nKPVcsUKJAtOkrsHx0EY/WLE99sMItjwust7Osf+ulNj6kS0MxyLIGXaL1lzWejIerGM2bWk6Kvnh1sxg71AYL2sD32r88ozQlIAXV8NUgSf+sPS4w47f14mZXUpD0hYe08fYyYs/iwunUy74/szWml/5PXLJeptuvB6M8ZOf9i3lSLc6T6L2JKqoG93kpNYe4vYLHxZ8y3wGThhOskR5FT/2tak9n3ovR1j7RC4CwLsZdMcdYi+clfucUciueo+dz5EslmpDSZBHa3DrqME6eFh9pOzZkVFqGtLtiGpty/uCyeFMU4oz9pb5xSflR4KejAxRNefD8nKAWhb05rN1fV74F+9026u2TLlL6Szzl/HZzLE13l7PfUMmtVbUQ3IMwWfv1bD8/AfvFd7lIbraARjPfGuk8C/fGfZh61nvszRIsh3Yd1MtCBHZC+N/XgqYtaI/1t6g7pweoiU2Clbbrzci1ydr89jqrwEyM1qdrro7FXf7spzdUfF08oZ89v/ecIVtOQ67ANlo/oMTWriiTT2o1whSh6cEE0URNbtXDj9zh/mdyf7pSO8W0MV1AV2Xo1vmFMy2UVb3wcxZLUErJdjpkOua3c4nWly+GuP60nV3Au9UEKWpLUTeZL7qDzRlR+YPsM+lCZGRZyLSFNyqllJsi0Med/VL+D02bcH9Xmtzv2NIPkzSkMRyBcDt7DPr3ttDRK2mF/f+RrMZaoxGj5DmuszVeqvRnZ2zW3i1kjZmNr8IAb/OrBLWqtzzruFFSuS6QqG3ClDj5cJ2bxCM783Y9sZsjRwvJZ20WaeJ0z73bM0aTnqPZvU5NXM+pVKi+hj1bgl6FRGbX2shAnsXyYwd0rQw0WJnCRCdjZcDGN4vdeBv/hIL9JIOaZcjcyl7vkOpTuxHVkuOLeGKEhZtoju3/YMjj9lc7CX7SdKxT6j5+dmZ/ZEMzdhtfDq9acEqGDIcISMHlAK9Duhm8MertSQ4y8wv05SqtRKdVXHdOxzyafyicW0P0WbtKrVg4O16+XmyLc13NtEEvBP9bNucm8byci1VCfuNJT63f5MZNNlTYPssHr/Cf5elf+zQCo8LfweDvtwkMB+FV3Z+VnNVxVweEHels0opz24VQrj5XEUdp+/rmD8vKbpY08KvvSz1FhgMx+RJlUNdrKl7+9Vz2wQHUfG8Ve4Tt5NiXn1kHoNk5jHRinh/rNFK1WanNzZOVyqHo5pCyGP8hZGB7YKCUTz3Lhum5xvQzvP/O3y3ctRRK1qgFH1W99nvDrGHLjqYovnkUB/J3kFDRu6OVqopPT9+06HR24m90aet7En4xXb22Xj1Y9e3+k8PxS/cPnDV+vVuBjMXfbPHh6hp26K+EA81+NVPCR7g3aczQ3XnwIVDt97F1uD5EK1wy2FryZgwU/qZwyr38EqvBvqPvgNyiL1iuP4A5bZKVtProf+JXP7uh1U7W6nOaTnJrajTQpPGukU4pXpzqp9puJhVRYPmnNKI07OA5acPJ62Tu5dHmQb1B4uOw9rHU7zL/1w6WCPogdb3a02KbJP8YH53f46v19dhVZ3SennqoLiLSeCt+pVD653E+GSE60SE8h31615s1JaJVnn5WelgByrAY3ynol7TFjF/dPKjbpH5IiBSIlacm5OpudaM31N6NI73rtUjYUFvMR3P7mUeRckFdm6y2w2JooNG51wfm+Z5rfTy6SfRmsXpOrPgNz5qJtazsPJqFbSwjIJKo+0ELN3uJF4j62+J+k3QcKxUXMrreZRU5mwqmz3BM98H2pCEj+AQK4SpH6SbaDeALOxqOcZGGOgkEObrPiHJyYCF+jPqu/SevndX1rfV9bhcBvzRtnC5U2g9uE/+aOv6HaNoXoyeKTm0hvaFBqs6UUjYbzq7GKvcHugMl7VO28q72YWMqkR7y53nK0QGTfVORkL7Rw4aoikek+9VtFhZQuRUwauhj0+82wMp0XXAtv/sCGFNNg1ySBW3qTPaEVGlh4fhii7GH6Ci/n0qwpQC794q0keMWQvww01/3dLbdk9w3xQheo9w6Pf6aqv6TRRAcwwL1NnltJG/eEw8nFYIYcW8F3OBXLsTiesFOswkicQ5p786GMu2kh63DDLRHJt6ZjrtwoQr22Jaqx3i3qb2ZDxsoew8vQkONYMHkv2Qi567YNswZPs1ZOl9CP/2z1FioNgvZlMKeLl8kv71Ul6MPX09t1bA9wSWPJLSLey/1O3oSFV0fNerEzY37G1//sToDSXxstopqT0NzE/3dn5puV3M1giRGroepra5dM9g+GXI1H/fS5+uaPFZm5oXoHT9XDxbr+ILoZ3/uy7dlzL8Cxdy77XV1C5nIyl6exlEWQWse5xJU/Ybj1wrScWBdFuE8ugbeU1nHoTAo5HNEGk9gpJUXiJcDPwa7KowTmaSdecNmav0dva+15mpFjHfxbZ8d5YPbWO2vfPU8gG9D/avIUuW27Oq/jkx9EfJgAv1E25mVzlL7Jm3iyKvkZjEuzuSzbX9oIX0dmgOcy4gdObQ/QMJ/lRBqG4hV6uWfA6hzPgjCIG/tIhTldzWj2xS3ifrr9yQsiT5pN5tt57i6+NSL12Ffz9kjiSmZK0JyJlorUbGxcKFP3BRZwQ3PcewdEqEtNREBoczdYk7ZCzOhNtay3Q9v0OqtfercPBtB4LXqNMpnlsPsXXNn2zUf6BXNf/nT4W27qE/PhBjLk5MXqwwdG40WtYf7k4XHqpmg/oZdirnkvf/8QybB047h1gzbcS+YHjeobe+y8QZMk91h46kamkmHbzjKSxxTTbft8tnr0XsAdAULbVWyG/ng8Obo+KG2bQzCs8OXWKYFXlbdsq/aqVDrZKsE0I9XSr1XDF4vZ2OzKHO6b+dRZV0PtLO7y0EeTriZnEGsUV1//rH1pmMaGYGR+l1krJHi3ByKdfDutaL2tAvHkXYWOWs2GlKsWaB3euAX0hBUpsGGu7twwRfLKPVgwKY4aON5L605MxCTeMj8Gf5+L3dqQXb+3UByeQV2k1IYjd7jpw5gUIBGFPao1xXk4ayzfJ2WjgmwHvWqXHbmr8/Dr2n8oo17jEfEr0TBM7t93V/su7Qj/dRrLBhSLeVEm+/a7zo93j/N1W8T5g6ouifDsW3HQmopZybws29PgnKtwKTQwGovSdzXk8/3te/ILfcNK6XA7q2zCJAzbRfw81GEvabdFj5DJbe75mXkUCN4L6F66qRKFIgYKuEX7MbQUtzHa0AklEZISyTn8kdec90NH32/6slIqzrHus7jv2yevtQ37WCqAdCk4cuJQTpu2J5+5ZHo+2fxVRHnA5Cl574rrL/+lVomrij2GNd1/vIjoPzBzqrOaeIJFdNxhbxk2V+t7z+fc9grHhWmAF9kbk6v9yNC8bdewots8xT9iFNqFF7I1qIk8jL6iW/ERktTT/sTtTlGRRyIhHB9bLXR+AnN6kwZXaipwCgqmpblN++32VPoftdmZh/l+eTghxpKsuDx8Ql8sCNnrIbYsg9WZYlikTql/6NOVE7jTr3PLP6dENRErH2y7tiudLILg6ty1DF6G5WQ3eGhqKLbhvFBc9mw+729HR+w8qPqjgsBewdU9XWZGBPXFdXNPQU+i9VX+4JLAbVihIQfZohNptrsah+Qxxliu4jqUFilZpSiME7SvUVmZ7v3awFBcP2y8Dq3orB/f6GY137in8DyczQR5WKg933PB4l4IIOMb1Pf7xO3+8d/wPNBCl+EMG8zHqJT+QLkaoO4tgydqdj5jqEeS7ZgdqwSfDDH6qM9zE1yGlcUR76IERqT81JAlo1tgm59+YoPxvu3zINW2B/9JvwYoO0Oo/Wjy+ZR8+BpG83KZMchXynQZHulniNVGNG59/LjZ+R+RG0MXNBHenf29NAuNbU6pd3Yb2dDIdYg+o5XXemH0fNbOS1EXPffRLWaj16bNC9+VTXNvsS5QF/oAmn+L3ntcGt6HLW2qoOe+Mh7Ieq50T3k5D/Ec/aNHaAEnT1lluq/CuO4fp/rl7OVO0dJ+u4/8d7NsQ+DtWjsTZk9lMXK9u9rIU+kZmyHPq/+J7Jp5DUdDmLWY4TZ4oSJ33Pr3FH1XRV0w1/L6EDVJHeN/8Fcjk/2TWB5wKQINb/fdYkWQ4UhrAAiWYhXkPn+5KSg7VP0orc+dPX4mfpJgE8SJ6nTtj6JM4RZ1/WbSErQMRneK516SNY8qo+ZRWPnnu8rzIVxYEutqt/VvNkILT3ojFRu5fmS1jLlzvN8KYSdsT8NbM8Tt80rPzeN0pH/UUwIrNAYI3RAXW1S/9p+LY/vHgHcRQqC5jhyef1718ldKzbOWN1t1QtW0ZTNxAWKbkW9oXsyNVwffor3xVLIEoxLT7Ac99nlNdzYxTgLN9XKo+HZXn3YS0YF9lXeOGFAfvcUTxXAdoHbUv5nz0e3iJagFa64PGF+KW1A/Jq95xh4svfjtEk98zdfPuCtG4JN0P5/7rPE5aQx2rme71zrocVmIYdaV/DCBU8vmdJknoLWu1CQOh7Sf/GNe66FBDl2WJ13nV4iylrqqKOweGaF4T/GApfS2iXoRGU1djd4hO4+0vK/cK800Ign5rfw/2JlQnV5THBulp6GQ25OKiwUz9fWyErLYm2OAzTJpLAEByQguuQqxBH/SJqY6az5vbitzNqKZNjh7NhL7HnIx7WFXi/eaKjvDamjR+3U/brqX4sWwn7kXXbT+ESHvHoWCTfuJ1F2lApgMR9ucs33zqDecYLtLt226u7Zg/3KSa147YYs5MnV7cvIIx8d/b5KbMHn/RznpdJWb0/jAbF6bUk9MZSLB9y7tGKdwfzfnCv1tBjnt/aHKogF9lVwrUS3WvpqRlpChfveMY+jPF3xynekvQt2e+JQ7s7ySufr8GT5Xna7q7j2MozzsoVbm68b40ZbL6GP6kiRoize0uPIdOKpW2VQePdl/Zs/8ncZm8vnlO61MuZzoay0tjk+/TEpEAetRC33qedKNe1P2u5hJWMFfP4hdvb1RqSmy9cGLBvExaSuzAxogX+SX57lxPbLOyU7qYvpEiJTCzdTZ6hXYCi0LXXNB+4/cPn9CDXvI3htsvZQHGLXgRd4evoEw0LKcd6DzMCEfSh2p5myKn7yAtIyIVhdeT8ar+hhR7c5RaKYbKlxMrbGfYJ+W1T/nR7g5IExP5SDC9RtfJtXmjR9ozcM1eK7wyuHXLpcdc0GA5ZNX4+clMd/kz/KK1y4erwPiXfjak/HgbPcyBvpy1+pAi14cnG3ctM+CUY+U0+zjXEeC5oMW5aPx1JTJEbD+ry+1b3BK1lTXiz6hr7I0ZDu2pBbv42pJsK9r7C8B8kM9PH78k6nh7cNAKYkjTU3lvG9gEp3TWro9+o/2Y26e0VawceiG4NuCdeXaGq0FqlN51m2rNsR2oPbKnOjHUaEe+3hbr/pfYgXWr3777Amw5u7ngzZhfFVNtpD5rEtt5L6N16poE7UqKbfc/kivEH9PBj2VRgPFdVE+/4n16tjG/bBJyErOr4DaWeVZvO091GkRAyWx2HXpqLO0ZvefhVrl5Du9lTpYciDngAu3hMCJCLDzktllTWcZi5vHBSdMm0Pyryl6+6BJKm3kZ++VQLO76jMAGtm0yTR9y2rXl9v6h8MuWaqyj6L+aK72Ima6kPKHhblninsKEd/wqvydSeDhC3Nuj+gnpNCO3DRdNbxhVf9yEExxhKjX4NOLjq5+f1ha3TW0I1jrgA3vPWzi7+T/hi6JADNF5Hc3bWHiyNlAEawkiJolRte0Ij0F2Rb47bfJUM1Bmf3NEUB7GubHuf+VJGvVOCXZV+lfnPr6bbhWU42H5wULUE+G+Tv3JNo68nI9G7Q5r2G2+W5goc/mC86BQq6B4aSrzi5yS1D/um0jWs6bFvWHzH9YmlcLIrXdf04T5qsyvkduufthxQkVCHvLiWZx/mw7gbXmlrh/UUu6sJ8yJ4jLRcyasnHCvBXVh8gVc5mZPKWO0a6q4snGWlA46SB3leYvsbSXuI/85S7VwexXbdptRToB3PkhOZG9CXZcvcb3xidle7sltcf0ULrZ1DxT9m6+KHtW9v+Ea5pZllrOjsXO3YEOmy/3NsUi0c9eCYJXUjMjA1/h/ovgrsJsdJP3aptM4jtNn+FvI7sm5B1AsM9aC3z5n+vdpUdTFDSvhCqPF4h6H7SNdtjs2/wD9lprdx7/iObtQ1KBNaQvaBqr9KU3N/l/IyKsT3r9e4CL4ixx6Q5GSmezYy7/fMa33KJJDL7iIz7V795MzxVdKza7/xhPIfyX8KXkVHaKvC5IeBOQoegsteuoL7Ekaa/6AVu8Ai8guZV+OUr0SJztGIYP0pDGhTcAZecxRpzPncwzybNN8NWQGaF1NOjXJQk6fsIOYqZErM/Lz3KOwLLmxuwVD2GWLj2uZPfAbSD3BydVRDKHczPaPMzrHReCRZNYg/8Y3LFPXd2lBv6GC1UcMmEt2b133OSn47YOmKSJ60kxwRsgnGsln9asFG3FzKVfZTTMelAGJ2GHpj9VfNZOt2e8lY6z6IPMqB42vbz+g1rn7/jHpeOQC+fMl2Et+XDW5/IkRjWyxjxfGGvknWaUbNP9dJie9fDSJw/UCE1u223zerAG9GPYZy/Tsn32g5uhJGhYg9QgtC8+lbHtC4EXp/DZf2gv8aNc4WKXb8K3+ZyMtWuqfEw9JI9UalKNWm3A/8zoFBcWaeT/QHKOtHaTYO0dyw4Uxfhn9pFuq5FJZ1hVrtS/ijZ13nEUmPuF5x6+wWmyb5QOHOj72v2SUfLEhquU4Dzpi114aj/oN9yevS+akXJx9jm+0s1t+w8chVpbRxEy0I7gL3xKx4edlD4o5Tq1FtVcJt5OplTxC5+f/Og4s0Tw3eaQVObr8nL3ocm8W+zV1fz7bciREcKfjipk8dPNZmhgjBW2cwg/UbZvSW/ktvaCTBpoQhRxXXSH01zQUQDiBXEamnZWNFfgrG54oM16Lc33d5sZb+lbLFVpg6HnwyhHDx4eTTNQga1XA5OTuyTzWG3Pt261flgN1jI4idt+MGgV42/uhD5f+S8x257c632HKyFInSHx4t/JjtjzEfRYWpW4whbjSZmW1qXTItXitSUQO+DLf2Q6W0+qUDB3BiDcmqelRkI2XPUtn4BG5ivP2k5McpdhFwKpdt3h/fTtudz5YyUDrO3A2ulwH3SC0y9C8ydS+OA8NXnCD8TSinmGldXLvg7Xh3Xp/ngVRwqPi68sDHDETRtzfETy32NbOJ7V0L+eCMGwK6S1GyL8O8AN9yCLzUz0/YPmMRSfs/GUXt+b0XaQpfhMTG7QenIeM8o4aaNMP81oH3tqwGH2kbT2KtmfJmo/ZrUJegQAlp/Yydwmv5xkSztHm/wOXefp3uCnH1q4Yi1xVc1Ob5kuVKYMp0/kEU0lEjB7DbTzgtqw/lvG9fxrMnp1MljrRGAQ/8yxJY6MAMnvVy+1nf9NA0+0SdVVHtsUeOkdXSy61jS9HgicyjSvkNss7l4bLz8dF96oibzlB2QW6TV2Id/sLLMuXoO/5/UUKpo7sxTz0oUChtupIenI5tSRAdqC8Z8XXoSU8lgYmT4u8C0443+u1Y8dUvwMAh5ESd4WLA7LCX1vm97in99qxvl41WF/pnDgv2BYFo0fvzNSZqvuMClOPy7NYpP7rYoGkXpxVHf4Vxj+2Am/jqnS8EU9nlfbbatSZEoOKq8V1KctebxDvstyPLifH9Ezy6iN+LgNus9l7mAdXERxiRLa+5sxUq+HShZnGa0Y9N76Dh7XyFtjg3nnhTTLCz2B2tjV53AAvZ/3A2rHlDU0HSYDQtCTUySdpYEpax0wmmkWm3S7r7Fs/rSrDK3kT3/yVGXJg95rjWjtoTfl73ltoE4vgU2NYBWl+bJAau8RuUTRLGIbr82q9Nuuo1xXCVuwOPOfGu0Dw+ayY7eXKm9Omav/N/G1qwCRG+4PW5uKEvjzn76GWJFew9rRKw7uM1qSKsr6A58khMI7ze+Gwl3O7emWQHfPBWURbNXsRv0s9LLE1CumQ5ju3b33wQSsTF+nzUX0dVfyNhXRFgUijVT5MFR8ExUg1KINIjbe9P/X3SsuvqnpcQBXyNbFzO9BzTpQvEOfsi6aIlZT7D5Stf5xieAiuYhXePOTHiIa9RffRFs8suoqKzAjA0trIbZy0UGXvmbPZpBtYPupV6KxOixgG+6OmjDN6Pdy+AQxbhC1G8p61m8TJ8LZx47Qv7AIjrw++rTfiEgufDuzacXHGALMkz3uZew/sOSOw6sQioKdueO2M3oBjyufMFA2l+Mi/JJWSt4Hx8/mW164a+WfoPK51+qJ7pHxBW7J4rYo2gN0vtxx4LnFddAzpAOhCpIgHQ3/WweuEn3w355cw+w5YOtfXZ3xN2uz5lwqPq939CwOxxR6eSjW/7QGFm96LxCPqutS+te+RbPSr50dVEK35J3afsy02YuxB0wM8xvAeq7B3T4Lj1BGkaLE4d2V+fzj9TIx++8wv5Rm9OUXkUeCRzsxf708gppTAPnP8wCAvuLySZQmk7tmdrgsfkZyzaEQyI7AuPFHjVBq6SMjIb+gouEg1ydPikYHuWWhvz/Tf+NP1uYWmAdPPvEuKHrGCU2hHU/kd57Fy9aoEGxpAWxDHPkzZTCFjF/BWNciYPcSFAu/lwfXtknyd+uYond+J6EP7c8zO+Lh1WQIu444Z5tHQG/cQY8a6Ye7acOKD7Q9fV8A2Ni+fj5VyD06laMUoQLT9PPCPUtNpksqlZNgEumWoLWya0arvZpiPsB13qPtFlK6oFWf6uR0YmxhfLFuXD2UC3B7bFZrA03ZjT8HCoCC4l3V1v3BP7ik3/I35US2VgK7eT1wZCwmCH+y50oXqPHOR6oWlS3AAysjAWaiwV4fNE6w9UeD3lPoad4Z2cCTJOyk2PVmhKNuvINjPChl8HwqQTbUw5+Uyqs4crl+UJJ8ApKIYt4cYVbIEpE3GPdKSNC3QoYek76HLsjpkSDO0uRznO/di5xHg77pP+E6LrqHY9GM7KnX/liJc9SxXhRcsPWPRvMqqx++QPzqR8qMGvV6t66ha1f1i8wJRPZQkq7aBM/kTmVWr65o9Nhh0+pc60tRyeWX2MNmluH+DhhOAa4Yvi1/ZCTIC9rnWDVoVonGS7fqdnH8SV2useYl05sNyg8BRQon6Y0+/ve6hgt6+QpE3N7t2tdPIxrQ9pXL5zTYQ95yoGL7Bl9F17MqWyooPbe1jRNq7FVDJt1XBNhjkALEPuvT+Kt9eq778ieGTvnu4q8tHK+6aG1MUetOAObznCxbuu/tVeL0meLT0nbnGQMPsde2VW82zQFUb4WLpH2W+lD3e1DWkY6hqHxtCPRs6WmY/Kr6sKHZtF/TvyKdw9R9asHxS7U2xga/Fsk6F2MrtTOo7G65Yn78U9kfi7LRj1k2UzEIMz/1xd4NlVckswZZoMu9i1BYap8twmrILLJc8gCOIzIS2naLkhqE1st6ylAvmG1rJEe81yfpz5nMzzVi+rVYEbuMe21LV7d/ub3HBoy2zsXQD0ba6PU/WER5OAVNlgePgfe7E/F7XZpRWtm53G6dTfRt5cPEq5ST5rAsY2sv3xfxtjH6s9nXHg6TkYsO337Hy7tGRe/OsG8C4JhPeqGAUDQ3WscoMcacwrUhYQtZ2jCC+KhV+MPX7xMXb3hp02Y1INvqXhF3PPdcL7yDuPDOzUFIcOcMEMc6D9MBzsrMitlkt8k6i5jBqqWg27vhtd8ws4iZctnCOO6UASVFJgZUd1vvaXPtjGq1AWsplzy4HKutpoAo1RfY63l14hOB1DO4gzmIg/ZCWZ+N2jAxYKzPZ58VO+x+iRa6ryxRSiKvrj59R65SFWx77n53lxkX/en2Eb6+tT79bvT/WMNFuoMeURMszPG1rYzL7xhen0TFeKyrqmqeC6BWvROb6V9QAb4pi74slxee+PfDTj1EvY7WsEFeJ5BPKl3yQwBLDsGjiULse0Q3NgQgJMVXbuE8XMf4er6ZLRLqFbxtcL9hEt2ezOeCjfoE2MU2d3chEbbFzRGTaLm00mweoZzc1Fx0MVG+EyJuNZ6EMQ0Mf0ac4zp+gLQ9L2Y0JSP8hHW+pszGU++dMvNg1vg8XEtQKi8GA6Ufj5fmwPzVJmqy2plBPXgR7v6RY0XIZTXK5CMb+L8snHuEMH+bw59W0KzBTuVU6F5jPwLJ1dfjhHw/ZbdLF9hUfjvHIA9MA95292jTYHbY9nBSIn+JCu2rMqfvVkQ34+B7KbdT1rQI/iQ1SOg7tRFkK4DPHbVgUJLfAQzlk9bFSNfUZfTBalcILoO+HLbZ6xV5XVC13t8wP/4HFITJAjvZ9wMcaJhuTm8bRp7QZHtjvogOubNfec3+1wrCPEVdjFetEHZMDPOfNwvwmVKW9m/JropiR4vP7EMC0Hq5lSeHIZfkkWtvXxBEAfxJx10bLsTzXzKrX9FXnh56HKxqnSnGfedvUZq2uOc3JFeJ8hs3UDcfNE96W8UtLFNRmdhkUMug53vqfq2fnw/h7bS+5nAu/+yb6PdjBsS/uqdqlF1Gd2ikJ/A0GuXsy+sTshe8b+qYdXraCsAO+CInsWJsj8c4ph+n5g=' +} diff --git a/src/shared/dictation-near-miss.ts b/src/shared/dictation-near-miss.ts new file mode 100644 index 00000000000..d8268dacc41 --- /dev/null +++ b/src/shared/dictation-near-miss.ts @@ -0,0 +1,103 @@ +import { levenshtein } from './edit-distance' +import { createWordFilterLookup } from './dictation-word-filter' + +// Shorter terms sit too close to ordinary words ("Vite" and "vote", "Hono" and "honey"). +const MIN_TERM_KEY_LENGTH = 5 +const LATIN_LETTERS_RE = /^[a-z]+$/ + +// Only a dictionary with a term to near-miss loads the word filter; until then nothing near-misses. +let isCommonWord: ((word: string) => boolean) | undefined +let commonWordsLoad: Promise | undefined +function loadCommonWords(): void { + commonWordsLoad ??= import('./dictation-english-words.js').then( + ({ COMMON_ENGLISH_WORDS }) => { + isCommonWord = createWordFilterLookup(COMMON_ENGLISH_WORDS) + }, + () => { + commonWordsLoad = undefined + } + ) +} + +/** + * Roughly how a lowercase a-z key sounds: its consonants and where its vowels fall, after the spellings + * English writes one sound several ways ("ph", "c", "x") and the "r" non-rhotic accents drop ("super" as "supa"). + */ +function soundKey(key: string): string { + return key + .replace(/ph/g, 'f') + .replace(/c(?=[eiy])/g, 's') + .replace(/[cq]/g, 'k') + .replace(/x/g, 'ks') + .replace(/z/g, 's') + .replace(/(?<=[aeiouy])r(?![aeiouy])/g, '') + .replace(/[aeiouy]+/g, 'a') + .replace(/(.)\1+/g, '$1') +} + +type NearMissTerm = { key: string; consonants: number; term: T } + +/** For a text's word keys, the term each word is a near miss of, or null when none is or it may not be English. */ +export type NearMissMatcher = (keys: readonly string[]) => (T | undefined)[] | null + +/** + * Matches near misses of `terms` (by word key) in English text: text whose only language is English, or that + * reads as English; null when no term is long enough or English isn't among the known `languages`. + */ +export function createNearMissMatcher( + terms: readonly (readonly [key: string, term: T])[], + languages: readonly string[] | undefined +): NearMissMatcher | null { + const bySound = new Map[]>() + for (const [key, term] of terms) { + if (key.length >= MIN_TERM_KEY_LENGTH && LATIN_LETTERS_RE.test(key)) { + const sound = soundKey(key) + const consonants = sound.replaceAll('a', '').length + bySound.set(sound, [...(bySound.get(sound) ?? []), { key, consonants, term }]) + } + } + if (bySound.size === 0 || (languages && !languages.includes('en'))) { + return null + } + loadCommonWords() + const match = (key: string, isCommon: (word: string) => boolean): T | undefined => { + // An ordinary word or name is never a misheard term. + if (!LATIN_LETTERS_RE.test(key) || isCommon(key)) { + return undefined + } + let best: { term: T; distance: number } | undefined + for (const candidate of bySound.get(soundKey(key)) ?? []) { + // A term that is itself a word ("Drizzle") is heard right, so what sounds like it is another word. + if (isCommon(candidate.key)) { + continue + } + // Sounding the same and spelled close ("Versal" for "Vercel"), with fewer edits than the consonants + // that agree, since a term of few consonants ("Sonar") sounds like many words ("Sonner"). + const distance = levenshtein(key, candidate.key) + const allowed = Math.min( + Math.floor(Math.max(key.length, candidate.key.length) / 3), + candidate.consonants - 1 + ) + if (distance <= allowed && (!best || distance < best.distance)) { + best = { term: candidate.term, distance } + } + } + return best?.term + } + return (keys) => { + const isCommon = isCommonWord + const found = isCommon ? keys.map((key) => match(key, isCommon)) : [] + if (!isCommon || !found.some(Boolean)) { + return null + } + // A bilingual model ("zh-en") may have heard either language, so only an English-only one skips the check. + if (languages?.every((language) => language === 'en')) { + return found + } + // Short words ("I", "a") and the misheard words say little about the language; two thirds of the rest must + // be English, since other languages share a few words with it ("den", "part"). + const rest = keys.filter((key, index) => key.length > 2 && !found[index]) + const english = rest.filter(isCommon).length + return rest.length > 0 && english * 3 >= rest.length * 2 ? found : null + } +} diff --git a/src/shared/dictation-term-spelling.test.ts b/src/shared/dictation-term-spelling.test.ts new file mode 100644 index 00000000000..181ad84a45f --- /dev/null +++ b/src/shared/dictation-term-spelling.test.ts @@ -0,0 +1,82 @@ +import { describe, expect, it } from 'vitest' +import { createDictationDictionary } from './dictation-term-spelling' + +const spell = (terms: Parameters[0], text: string): string => + createDictationDictionary(terms)!(text) + +describe('createDictationDictionary', () => { + // Parakeet v3's real output for the dev bench's code-term clips. + it('joins the words a model splits a code term into back into the term', () => { + const terms = [ + { term: 'useAudioCapture' }, + { term: 'usePcmWorklet' }, + { term: 'stt-worker.ts' }, + { term: 'numThreads' }, + { term: 'useMemo' }, + { term: 'onChange' } + ] + expect( + spell(terms, 'Rename use audio capture to use PCM worklet in the dictation controller.') + ).toBe('Rename useAudioCapture to usePcmWorklet in the dictation controller.') + expect(spell(terms, 'Open stt worker TS and set num threads to 4.')).toBe( + 'Open stt-worker.ts and set numThreads to 4.' + ) + expect(spell(terms, 'Wrap it in use memo and pass the on-change call back.')).toBe( + 'Wrap it in useMemo and pass the onChange call back.' + ) + }) + + // Parakeet v3 and Whisper large-v3 turbo's real output for `say` clips of the terms. + it('spells a misheard word as the term when it sounds the same and is spelled close', async () => { + const clean = createDictationDictionary([ + { term: 'Supabase' }, + { term: 'Vercel' }, + { term: 'oxlint' }, + { term: 'Silero' } + ])! + await expect + .poll(() => clean('We moved the auth tables from Superbase to Postgres last week.'), { + timeout: 5000 + }) + .toBe('We moved the auth tables from Supabase to Postgres last week.') + expect(clean('Deploy the preview to Versal before the review.')).toBe( + 'Deploy the preview to Vercel before the review.' + ) + expect(clean('Run auxlint on the changed files before you push.')).toBe( + 'Run oxlint on the changed files before you push.' + ) + expect(clean('Parakeet and Celero both run inside the speech child.')).toBe( + 'Parakeet and Silero both run inside the speech child.' + ) + expect(clean("It doesn't work on Versal.")).toBe("It doesn't work on Vercel.") + }) + + it('leaves ordinary words, names, runs of words and other languages alone', async () => { + const clean = createDictationDictionary([ + { term: 'Bazel' }, + { term: 'Jupyter' }, + { term: 'Istio' }, + { term: 'Saleor' }, + { term: 'Vercel' } + ])! + await expect + .poll(() => clean('Deploy the preview to Versal before the review.'), { timeout: 5000 }) + .toBe('Deploy the preview to Vercel before the review.') + for (const text of [ + 'The vessel reached the harbor just after noon.', + 'The phone has a thin bezel.', + 'The planet Jupiter is bright tonight.', + 'It is too late to call him now.', + 'The ship was a very fast sailer.', + 'Despliega la vista previa en Versal antes de la revisión.' + ]) { + expect(clean(text)).toBe(text) + } + // A bilingual model's Chinese sentence is not English, even though English is one of its languages. + const bilingual = createDictationDictionary([{ term: 'Vercel' }], ['zh', 'en'])! + expect(bilingual('请在 Versal 上部署预览。')).toBe('请在 Versal 上部署预览。') + expect(bilingual('Deploy the preview to Versal before the review.')).toBe( + 'Deploy the preview to Vercel before the review.' + ) + }) +}) diff --git a/src/shared/dictation-term-spelling.ts b/src/shared/dictation-term-spelling.ts new file mode 100644 index 00000000000..7983e831197 --- /dev/null +++ b/src/shared/dictation-term-spelling.ts @@ -0,0 +1,134 @@ +import type { DictationTerm } from './dictation-dictionary' +import { createNearMissMatcher, type NearMissMatcher } from './dictation-near-miss' + +// Symbols that belong to a name ("C#", "C++", "@orca") count as part of a word, so a term never doubles them. +const WORD_RE = /[\p{L}\p{M}\p{N}#+@$&]+/gu +// Spaces, or one joiner as in "on-change" or "stt-worker.ts": what may sit between the words of one term. +const JOINING_GAP_RE = /^(?:\s+|[-_./])$/u +// A term's own leading or trailing joiners, as in ".NET", taken along when the text already has them. +const EDGE_JOINERS_RE = /^([-_./]*)[\s\S]*?([-_./]*)$/u +// Scripts written without spaces, where a phrase can only be found inside a longer run of letters. +const UNSPACED_SCRIPT_RE = + /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Thai}\p{Script=Lao}\p{Script=Khmer}\p{Script=Myanmar}]/u +const MAX_WORDS_PER_TERM = 8 + +// "Use Audio Capture", "use-audio-capture" and "useAudioCapture" all key as "useaudiocapture". +function wordKey(text: string): string { + return (text.normalize('NFKC').toLowerCase().match(WORD_RE) ?? []).join('') +} + +type Spelling = { term: string; lead: string; trail: string } + +function spelling(term: string): Spelling { + const [, lead = '', trail = ''] = EDGE_JOINERS_RE.exec(term) ?? [] + return { term, lead, trail } +} + +/** + * Rewrites words that key the same as a term (or its "heard as") to the term's spelling, then, in English + * text (`languages` includes "en", or the text reads as English when they are unknown), single words that are + * near misses of a long, uncommon term ("Superbase" for "Supabase"); null when there are no terms. + */ +export function createDictationDictionary( + terms: readonly DictationTerm[] | undefined, + languages?: readonly string[] +): ((text: string) => string) | null { + const byKey = new Map() + const unspacedAliases: [heardAs: string, term: string][] = [] + const add = (key: string, term: string): void => { + if (key && !byKey.has(key)) { + byKey.set(key, spelling(term)) + } + } + // A "heard as" is what the user asked for, so it beats another term spelled the same. + for (const { term, heardAs } of terms ?? []) { + if (heardAs && UNSPACED_SCRIPT_RE.test(heardAs)) { + unspacedAliases.push([heardAs, term]) + } else if (heardAs) { + add(wordKey(heardAs), term) + } + } + // The repo name is a guess, not something the user typed, so it never takes a near miss. + const typedTerms: [key: string, spelling: Spelling][] = [] + for (const { term, hint } of terms ?? []) { + // A one-word repo name like "orca" would recase every "Orca"; only a name of several words joins. + if (!hint || (term.match(WORD_RE)?.length ?? 0) > 1) { + add(wordKey(term), term) + } + if (!hint) { + typedTerms.push([wordKey(term), spelling(term)]) + } + } + if (byKey.size === 0 && unspacedAliases.length === 0) { + return null + } + const nearMiss = createNearMissMatcher(typedTerms, languages) + const longestKey = Math.max(0, ...[...byKey.keys()].map((key) => key.length)) + return (text) => { + for (const [alias, term] of unspacedAliases) { + text = text.split(alias).join(term) + } + return byKey.size > 0 ? replaceWordRuns(text, byKey, longestKey, nearMiss) : text + } +} + +// At each word, the longest run of joined words whose key is a term's becomes that term; failing +// that, the word alone when it is a near miss of one. +function replaceWordRuns( + text: string, + byKey: Map, + longestKey: number, + nearMiss: NearMissMatcher | null +): string { + const words = [...text.matchAll(WORD_RE)].map((word) => ({ + from: word.index, + to: word.index + word[0].length, + key: wordKey(word[0]) + })) + // joinsNext[i]: words i and i + 1 may belong to one term. + const joinsNext = words.map( + (word, i) => i + 1 < words.length && JOINING_GAP_RE.test(text.slice(word.to, words[i + 1].from)) + ) + const nearMisses = nearMiss?.(words.map((word) => word.key)) ?? null + let out = '' + let cursor = 0 + for (let start = 0; start < words.length;) { + let key = '' + let match: { end: number; spelling: Spelling } | null = null + for (let end = start; end < words.length && end - start < MAX_WORDS_PER_TERM; end++) { + if (end > start && !joinsNext[end - 1]) { + break + } + key += words[end].key + if (key.length > longestKey) { + break + } + const found = byKey.get(key) + if (found) { + match = { end, spelling: found } + } + } + // Only a single word: in ordinary speech, words run together sound like terms ("is to" and "Istio"). + const nearMissOf = match ? undefined : nearMisses?.[start] + if (nearMissOf) { + match = { end: start, spelling: nearMissOf } + } + if (!match) { + start++ + continue + } + const { term, lead, trail } = match.spelling + let from = words[start].from + let to = words[match.end].to + if (lead && from - lead.length >= cursor && text.slice(from - lead.length, from) === lead) { + from -= lead.length + } + if (trail && text.startsWith(trail, to)) { + to += trail.length + } + out += text.slice(cursor, from) + term + cursor = to + start = match.end + 1 + } + return out + text.slice(cursor) +} diff --git a/src/shared/dictation-text-pipeline.ts b/src/shared/dictation-text-pipeline.ts index 7adc116ea53..5974be53291 100644 --- a/src/shared/dictation-text-pipeline.ts +++ b/src/shared/dictation-text-pipeline.ts @@ -1,4 +1,5 @@ -import { createDictationDictionary, type DictationTerm } from './dictation-dictionary' +import type { DictationTerm } from './dictation-dictionary' +import { createDictationDictionary } from './dictation-term-spelling' import { engineDictationLanguage } from './dictation-language' import { removeFillerWords } from './dictation-filler-words' import type { SpeechModelManifest, VoiceSettings } from './speech-types' @@ -41,7 +42,7 @@ export function createDictationTextPipeline( settings?.removeFillerWords === false ? null : (text: string) => removeFillerWords(text, languages, settings?.correctionWord) - const spellTerms = createDictationDictionary(terms) + const spellTerms = createDictationDictionary(terms, languages) if (!dropFillers || !spellTerms) { return dropFillers ?? spellTerms } diff --git a/src/shared/dictation-word-filter.ts b/src/shared/dictation-word-filter.ts new file mode 100644 index 00000000000..b75ca5b9c1c --- /dev/null +++ b/src/shared/dictation-word-filter.ts @@ -0,0 +1,31 @@ +/** + * A Bloom filter over lowercase a-z words: about 1 byte a word instead of the word, so a ~90k-word list fits + * in a phone's page bundle. A false "yes" (~2%) only makes dictation leave a word alone. + */ +export type WordFilter = { bits: number; hashes: number; base64: string } + +// Double hashing over two FNV-1a variants; the generator and the reader must agree on this. +export function wordFilterSlots(word: string, bits: number, hashes: number): number[] { + let first = 0x811c9dc5 + let second = 0x01000193 + for (let index = 0; index < word.length; index++) { + const code = word.charCodeAt(index) + first = Math.imul(first ^ code, 0x01000193) + second = Math.imul(second ^ code, 0x5bd1e995) ^ (second >>> 15) + } + const step = (second | 1) >>> 0 + const slots: number[] = [] + for (let index = 0; index < hashes; index++) { + slots.push(((first >>> 0) + index * step) % bits) + } + return slots +} + +/** Whether `word` may be in the filter: false is certain, true is right ~98% of the time. */ +export function createWordFilterLookup(filter: WordFilter): (word: string) => boolean { + const bytes = Uint8Array.from(atob(filter.base64), (char) => char.charCodeAt(0)) + return (word) => + wordFilterSlots(word, filter.bits, filter.hashes).every( + (slot) => (bytes[slot >>> 3] & (1 << (slot & 7))) !== 0 + ) +} From 560643500ce6f866f5cd77b5f0bf5e7ca27ce21a Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 13 Nov 2026 01:41:32 -0800 Subject: [PATCH 061/178] Show remote files created while a restarted host's watcher starts (#41874) * fix(explorer): resync a recovered remote watch only after the host watcher is installed The contact-regained resync fired when the files.watch subscription was accepted, before the restarted host reported its watcher ready, so a file created in that gap was neither in the re-read listing nor reported by the watcher. The managed-orcad explorer recovery spec now waits for that resync before writing its probe file. * fix(explorer): re-read a recovered remote explorer once when its host watcher fails to install * test(explorer): the recovered watch resyncs after its ready signal * fix(explorer): re-read a remote watch on acceptance and again once its host watcher is installed A recovered watch re-reads when the restarted host accepts it, so outage changes still appear if the watcher install stays pending, and again on ready so writes made before the install appear. A first subscription also re-reads on ready, which replaces the trimmed-watch first-ready catch-up for remote targets. * test(explorer): the recovered explorer shows a file written while the host watcher installs Counting resyncs is not an install barrier: two recovery attempts can both re-read on acceptance before any watcher exists, and a ready that lands before acceptance re-reads once. Assert the user-visible result instead, which holds for any order of acceptance, install and the write. --- .../hooks/editor-runtime-file-watch.test.ts | 69 ++++++++++++++++--- .../src/hooks/editor-runtime-file-watch.ts | 52 +++++++------- ...itorExternalWatch-background-trim.test.tsx | 4 +- ...seEditorExternalWatch-shared-root.test.tsx | 4 +- ...EditorExternalWatch-subscriptions.test.tsx | 4 +- .../src/hooks/useEditorExternalWatch.ts | 36 +++++----- .../ssh-orcad-explorer-watch-recovery.spec.ts | 16 ++--- 7 files changed, 120 insertions(+), 65 deletions(-) diff --git a/src/renderer/src/hooks/editor-runtime-file-watch.test.ts b/src/renderer/src/hooks/editor-runtime-file-watch.test.ts index 5ead255db73..222467f143e 100644 --- a/src/renderer/src/hooks/editor-runtime-file-watch.test.ts +++ b/src/renderer/src/hooks/editor-runtime-file-watch.test.ts @@ -57,15 +57,40 @@ beforeEach(() => { runtime.subscribe.mockReset() }) +// The explorer's view of the host directory: whatever the last catch-up re-read saw. +function hostDirectory(): { + host: Set + shown: Set + onCatchUp: () => void +} { + const host = new Set() + const shown = new Set() + return { + host, + shown, + onCatchUp: () => { + shown.clear() + host.forEach((name) => shown.add(name)) + } + } +} +function streamCallback(call: number, index: 2 | 3): (error?: Error) => void { + const callback: unknown = runtime.subscribe.mock.calls[call]?.[index] + if (typeof callback !== 'function') { + throw new Error(`stream ${call} registered no ${index === 2 ? 'error' : 'ready'} callback`) + } + return (error) => callback(error) +} + describe('editor runtime file watch recovery', () => { it('disposes late old stream handles without replacing the current handle', async () => { const old = pendingSubscription() const fresh = pendingSubscription() const stopOld = vi.fn() const stopFresh = vi.fn() - const onPayload = vi.fn() + const onCatchUp = vi.fn() runtime.subscribe.mockReturnValueOnce(old.promise).mockReturnValueOnce(fresh.promise) - const stop = subscribeEditorRuntimeFileWatch(target, onPayload, vi.fn()) + const stop = subscribeEditorRuntimeFileWatch(target, vi.fn(), vi.fn(), onCatchUp) runtime.contact.get('host-a')?.() fresh.resolve(stopFresh) await settle() @@ -73,13 +98,41 @@ describe('editor runtime file watch recovery', () => { await settle() expect(stopOld).toHaveBeenCalledOnce() expect(stopFresh).not.toHaveBeenCalled() - expect(onPayload).toHaveBeenCalledOnce() - expect(onPayload).toHaveBeenCalledWith({ - worktreePath: '/repo', - events: [{ kind: 'overflow', absolutePath: '/repo' }], - resync: true - }) + expect(onCatchUp).toHaveBeenCalledOnce() stop() expect(stopFresh).toHaveBeenCalledOnce() }) + + it('shows files written before the host watcher was installed', async () => { + const dir = hostDirectory() + runtime.subscribe.mockResolvedValue(vi.fn()) + subscribeEditorRuntimeFileWatch(target, vi.fn(), vi.fn(), dir.onCatchUp) + await settle() + dir.host.add('BEFORE_FIRST_READY.txt') + streamCallback(0, 3)() + expect([...dir.shown]).toEqual(['BEFORE_FIRST_READY.txt']) + + runtime.contact.get('host-a')?.() + dir.host.add('DURING_OUTAGE.txt') + await settle() + // Install can stay pending on the restarted host, so outage changes must not wait for it. + expect(dir.shown.has('DURING_OUTAGE.txt')).toBe(true) + dir.host.add('BEFORE_RECOVERED_READY.txt') + streamCallback(1, 3)() + expect(dir.shown.has('BEFORE_RECOVERED_READY.txt')).toBe(true) + }) + + it('re-reads once more when the recovered host watcher fails to install', async () => { + const dir = hostDirectory() + const onError = vi.fn() + runtime.subscribe.mockResolvedValue(vi.fn()) + subscribeEditorRuntimeFileWatch(target, vi.fn(), onError, dir.onCatchUp) + runtime.contact.get('host-a')?.() + await settle() + dir.host.add('BEFORE_INSTALL_FAILED.txt') + const failure = new Error('watcher capacity') + streamCallback(1, 2)(failure) + expect([...dir.shown]).toEqual(['BEFORE_INSTALL_FAILED.txt']) + expect(onError).toHaveBeenCalledWith(failure) + }) }) diff --git a/src/renderer/src/hooks/editor-runtime-file-watch.ts b/src/renderer/src/hooks/editor-runtime-file-watch.ts index 8ea1cf995dc..34c18b16274 100644 --- a/src/renderer/src/hooks/editor-runtime-file-watch.ts +++ b/src/renderer/src/hooks/editor-runtime-file-watch.ts @@ -9,14 +9,13 @@ export function subscribeEditorRuntimeFileWatch( target: EditorExternalWatchTarget & { runtimeEnvironmentId: string }, onPayload: (payload: FsChangedPayload) => void, onError: (error: unknown) => void, - onFirstReady?: () => void + onCatchUp: () => void ): () => void { - let readySeen = false let disposed = false let attempt = 0 let unsubscribe: (() => void) | null = null - const start = (resync: boolean): void => { + const start = (recovering: boolean): void => { if (disposed) { return } @@ -24,6 +23,26 @@ export function subscribeEditorRuntimeFileWatch( unsubscribe?.() unsubscribe = null const isCurrent = (): boolean => !disposed && attempt === currentAttempt + // Writes between a directory read and the host watcher's install reach neither, so every + // install re-reads. A recovery also re-reads on acceptance, since install can stay pending. + let acceptancePending = recovering + let installPending = true + const settleInstall = (catchUp: boolean): void => { + acceptancePending = false + if (!installPending) { + return + } + installPending = false + if (catchUp) { + onCatchUp() + } + } + const fail = (error: unknown): void => { + if (isCurrent()) { + settleInstall(recovering) + onError(error) + } + } void subscribeRuntimeFileChanges( { target: runtimeTargetForOwnerEnvironment(target.runtimeEnvironmentId), @@ -36,15 +55,10 @@ export function subscribeEditorRuntimeFileWatch( onPayload(payload) } }, - (error) => { - if (isCurrent()) { - onError(error) - } - }, + fail, () => { - if (isCurrent() && !readySeen) { - readySeen = true - onFirstReady?.() + if (isCurrent()) { + settleInstall(true) } } ) @@ -54,20 +68,12 @@ export function subscribeEditorRuntimeFileWatch( return } unsubscribe = stop - // A restored stream cannot replay file changes missed while contact was lost. - if (resync) { - onPayload({ - worktreePath: target.worktreePath, - events: [{ kind: 'overflow', absolutePath: target.worktreePath }], - resync: true - }) - } - }) - .catch((error: unknown) => { - if (isCurrent()) { - onError(error) + if (acceptancePending) { + acceptancePending = false + onCatchUp() } }) + .catch(fail) } const stopContact = subscribeRuntimeHostContactRegained(target.runtimeEnvironmentId, () => start(true) diff --git a/src/renderer/src/hooks/useEditorExternalWatch-background-trim.test.tsx b/src/renderer/src/hooks/useEditorExternalWatch-background-trim.test.tsx index 4b088dd29e7..25e516c45ff 100644 --- a/src/renderer/src/hooks/useEditorExternalWatch-background-trim.test.tsx +++ b/src/renderer/src/hooks/useEditorExternalWatch-background-trim.test.tsx @@ -50,9 +50,9 @@ vi.mock('./editor-runtime-file-watch', () => ({ _target: unknown, _onPayload: unknown, _onError: unknown, - onFirstReady?: () => void + onCatchUp?: () => void ) => { - harness.runtimeReady.current = onFirstReady + harness.runtimeReady.current = onCatchUp return harness.runtimeUnsubscribe } })) diff --git a/src/renderer/src/hooks/useEditorExternalWatch-shared-root.test.tsx b/src/renderer/src/hooks/useEditorExternalWatch-shared-root.test.tsx index 4e65ff951da..34567b4ef56 100644 --- a/src/renderer/src/hooks/useEditorExternalWatch-shared-root.test.tsx +++ b/src/renderer/src/hooks/useEditorExternalWatch-shared-root.test.tsx @@ -53,9 +53,9 @@ vi.mock('./editor-runtime-file-watch', () => ({ _target: unknown, _onPayload: unknown, _onError: unknown, - onFirstReady?: () => void + onCatchUp?: () => void ) => { - onFirstReady?.() + onCatchUp?.() return vi.fn() } })) diff --git a/src/renderer/src/hooks/useEditorExternalWatch-subscriptions.test.tsx b/src/renderer/src/hooks/useEditorExternalWatch-subscriptions.test.tsx index 0c6c1787cb8..8fd792b1898 100644 --- a/src/renderer/src/hooks/useEditorExternalWatch-subscriptions.test.tsx +++ b/src/renderer/src/hooks/useEditorExternalWatch-subscriptions.test.tsx @@ -172,9 +172,11 @@ describe('useEditorExternalWatch subscriptions', () => { { worktreePath: '/runtime/repo', events: [{ kind: 'overflow', absolutePath: '/runtime/repo' }], + connectionId: 'nested-ssh', resync: true }, - 'runtime-1' + 'runtime-1', + runtimeTarget() ) } finally { warning.mockRestore() diff --git a/src/renderer/src/hooks/useEditorExternalWatch.ts b/src/renderer/src/hooks/useEditorExternalWatch.ts index af7251b9c8c..0857454aced 100644 --- a/src/renderer/src/hooks/useEditorExternalWatch.ts +++ b/src/renderer/src/hooks/useEditorExternalWatch.ts @@ -78,22 +78,21 @@ export function useEditorExternalWatch(): void { targetsRef.current = nextTargets // Why: acquire before release so a root shared by an added and a removed target is never dropped in between. for (const target of added) { - // After reacquiring trimmed background watches, overflow-resync missed changes through the normal pipeline. - const resync = previousUnwatchedKeys.has(getEditorExternalWatchTargetKey(target)) - ? () => - fsChangedHandlerRef.current?.( - markWorktreeCatchUpPayload({ - worktreePath: target.worktreePath, - events: [{ kind: 'overflow', absolutePath: target.worktreePath }], - connectionId: target.connectionId, - resync: true - }), - target.runtimeEnvironmentId, - target - ) - : undefined + // Overflow-resync missed changes through the normal pipeline, scoped to this target. + const resync = (): void => { + fsChangedHandlerRef.current?.( + markWorktreeCatchUpPayload({ + worktreePath: target.worktreePath, + events: [{ kind: 'overflow', absolutePath: target.worktreePath }], + connectionId: target.connectionId, + resync: true + }), + target.runtimeEnvironmentId, + target + ) + } if (target.runtimeEnvironmentId) { - // Why: the host's ready event means its watcher is installed, so nothing after the resync is lost. + // Why: the remote watch decides when to resync (each host watcher install and each recovery). subscribeRuntimeTarget(target, remoteWatchUnsubsRef.current, fsChangedHandlerRef, resync) continue } @@ -101,7 +100,8 @@ export function useEditorExternalWatch(): void { // Provider disappearance must report watch failure without uncaught renderer rejection. warnExternalWatchFailure(target, err) }) - if (resync) { + // After reacquiring trimmed background watches, catch up on changes made while unwatched. + if (previousUnwatchedKeys.has(getEditorExternalWatchTargetKey(target))) { void installed.then(resync) } } @@ -168,7 +168,7 @@ function subscribeRuntimeTarget( target: EditorExternalWatchTarget, remoteWatchUnsubs: Map void>, fsChangedHandlerRef: { current: FsChangedHandler | null }, - onFirstReady: (() => void) | undefined + onCatchUp: () => void ): void { const runtimeEnvironmentId = target.runtimeEnvironmentId if (!runtimeEnvironmentId) { @@ -181,7 +181,7 @@ function subscribeRuntimeTarget( { ...target, runtimeEnvironmentId }, (payload) => fsChangedHandlerRef.current?.(payload, runtimeEnvironmentId), (error) => warnExternalWatchFailure(target, error), - onFirstReady + onCatchUp ) ) } diff --git a/tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts b/tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts index 75620958670..4a8ba2fc960 100644 --- a/tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts +++ b/tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts @@ -101,12 +101,8 @@ for (const workspace of ['worktree', 'folder'] as const) { '[managed-server-restart]', JSON.stringify({ before, after: serverPids(), workspace, worktreeId }) ) - const resumed = page.waitForEvent('console', { - predicate: (message) => - message.text().startsWith('[file-change-event]') && - message.text().includes('WATCH_RECOVERED.txt'), - timeout: 30_000 - }) + // Written while the restarted host may still be installing its watcher; the explorer must + // show it whichever of the watcher event or the post-install re-read reports it. execute(`printf 'watcher recovered\n' > '${rootPath}/WATCH_RECOVERED.txt'`) const created = await serverCall(page, environmentId, 'files.read', { worktree: `id:${worktreeId}`, @@ -114,15 +110,13 @@ for (const workspace of ['worktree', 'folder'] as const) { }) console.log('[host-post-restart-file]', created) expect(created).toContain('watcher recovered') - await resumed - console.log('[recovered-broker-event]', 'WATCH_RECOVERED.txt') + await expect(page.getByText('WATCH_RECOVERED.txt', { exact: true })).toBeVisible({ + timeout: 180_000 + }) await page.screenshot({ path: testInfo.outputPath('remote-explorer-observed.png') }) await expect(page.getByText('DURING_OUTAGE.txt', { exact: true })).toBeVisible({ timeout: 10_000 }) - await expect(page.getByText('WATCH_RECOVERED.txt', { exact: true })).toBeVisible({ - timeout: 10_000 - }) await page.screenshot({ path: testInfo.outputPath('remote-explorer-refreshed.png') }) }) }) From 37f8222979b95dade02d152b23e3e2e063aa0468 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 01:43:44 -0800 Subject: [PATCH 062/178] test: remove low-value cleanup suites and generation replays (#41941) * test: remove cleanup wrappers and redundant settlement coverage * test: remove ineffective preview and generation failure replays --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 3 - .../failures/agent-failure-output.test.ts | 33 ------- ...essage-text-generation-branch-name.test.ts | 36 -------- ...nversation-name-generation-request.test.ts | 36 +------- ...-chat-github-routed-preview-store.test.tsx | 23 ----- .../workspace-cleanup-flat-list.test.tsx | 90 ------------------- ...anupDialog.stale-while-revalidate.test.tsx | 55 ------------ .../workspace-cleanup-facet-sort.test.ts | 26 ------ ...rkspace-cleanup-removal-settlement.test.ts | 57 ------------ 9 files changed, 1 insertion(+), 358 deletions(-) delete mode 100644 src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-flat-list.test.tsx delete mode 100644 src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.test.ts delete mode 100644 src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index a2b1f69743b..a3d35790db0 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -5391,10 +5391,7 @@ "src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal-late-settlement.test.ts": 1880, "src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-background-removal.test.ts": 283, "src/renderer/src/components/workspace-cleanup/candidate-evidence/workspace-cleanup-host-index.test.ts": 341, - "src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.test.ts": 264, - "src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-flat-list.test.tsx": 2594, "src/renderer/src/components/workspace-cleanup/removal/workspace-cleanup-removal-candidates.test.ts": 206, - "src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.test.ts": 203, "src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-timeout-recovery.test.ts": 1817, "src/renderer/src/components/workspace-emoji/WorkspaceEmojiPickerButton.test.tsx": 1086, "src/renderer/src/components/workspace-emoji/WorkspaceEmojiPickerPanel.test.tsx": 763, diff --git a/src/main/text-generation/failures/agent-failure-output.test.ts b/src/main/text-generation/failures/agent-failure-output.test.ts index b4c24617d9e..4a17870e634 100644 --- a/src/main/text-generation/failures/agent-failure-output.test.ts +++ b/src/main/text-generation/failures/agent-failure-output.test.ts @@ -8,15 +8,6 @@ const ESC = String.fromCharCode(27) const BEL = String.fromCharCode(7) describe('captureAgentGenerationFailureOutput', () => { - it('returns null when both streams are blank', () => { - expect(captureAgentGenerationFailureOutput('Pi', 1, ' \n', '\t')).toBeNull() - }) - - it('keeps short streams verbatim', () => { - const capture = captureAgentGenerationFailureOutput('Pi', 1, 'out', 'err') - expect(capture).toEqual({ label: 'Pi', exitCode: 1, stdout: 'out', stderr: 'err' }) - }) - it('bounds an oversized stream to its head and tail with an omission marker', () => { const oversized = `${'h'.repeat(20_000)}${'m'.repeat(40_000)}${'t'.repeat(50_000)}` const capture = captureAgentGenerationFailureOutput('Pi', 1, '', oversized) @@ -29,30 +20,6 @@ describe('captureAgentGenerationFailureOutput', () => { }) describe('formatAgentGenerationFailureOutputForDisplay', () => { - it('renders a header plus the streams that had content, stderr first', () => { - const text = formatAgentGenerationFailureOutputForDisplay({ - label: 'Pi', - exitCode: 1, - stdout: 'partial result', - stderr: 'No API key found for github-copilot.' - }) - expect(text).toBe( - 'Pi exited with code 1.\n\n[stderr]\nNo API key found for github-copilot.\n\n[stdout]\npartial result' - ) - }) - - it('omits a blank stream section and reports an unknown exit code', () => { - const text = formatAgentGenerationFailureOutputForDisplay({ - label: 'Claude', - exitCode: null, - stdout: 'Not logged in · Please run /login', - stderr: ' ' - }) - expect(text).toBe( - 'Claude exited with code unknown.\n\n[stdout]\nNot logged in · Please run /login' - ) - }) - it('strips ANSI, OSC, and bidi-override characters while keeping line structure', () => { const text = formatAgentGenerationFailureOutputForDisplay({ label: 'Pi', diff --git a/src/main/text-generation/requests/commit-message-text-generation-branch-name.test.ts b/src/main/text-generation/requests/commit-message-text-generation-branch-name.test.ts index 52e3c8d6945..d50ffbe63ad 100644 --- a/src/main/text-generation/requests/commit-message-text-generation-branch-name.test.ts +++ b/src/main/text-generation/requests/commit-message-text-generation-branch-name.test.ts @@ -1,43 +1,7 @@ -import { realKimiOccupiedOutput } from '../../../shared/__fixtures__/kimi-generation-source-shaped' import { describe, expect, it } from 'vitest' import { generateBranchNameFromContext } from '../commit-message-text-generation' describe('generateBranchNameFromContext', () => { - it.each(['pi', 'kimi'] as const)('%s failure diagnostics', async (agentId) => { - const result = await generateBranchNameFromContext( - { firstPrompt: 'Fix login flow' }, - { - agentId, - model: agentId === 'kimi' ? 'default' : 'github-copilot/gpt-5.5' - }, - { - kind: 'remote', - cwd: '/repo', - missingBinaryLocation: 'remote PATH', - execute: async () => ({ - stdout: agentId === 'kimi' ? realKimiOccupiedOutput : 'partial', - stderr: 'No API key found for github-copilot.', - exitCode: agentId === 'kimi' ? 4 : 1, - timedOut: false - }) - } - ) - - expect(result.success).toBe(false) - if (result.success) { - throw new Error('expected a failure result') - } - if (agentId === 'kimi') { - expect(result.error).toMatch(/Kimi.*active.*Stop.*exit.*try again/) - } - expect(result.failureOutput).toEqual({ - label: agentId === 'kimi' ? 'Kimi' : 'Pi', - exitCode: agentId === 'kimi' ? 4 : 1, - stdout: agentId === 'kimi' ? realKimiOccupiedOutput : 'partial', - stderr: 'No API key found for github-copilot.' - }) - }) - it('reruns Copilot once on its default model when the plan hides the saved model', async () => { const calls: string[][] = [] const result = await generateBranchNameFromContext( diff --git a/src/main/text-generation/requests/conversation-name-generation-request.test.ts b/src/main/text-generation/requests/conversation-name-generation-request.test.ts index 8fbdf1bf3f9..ee6ce11608a 100644 --- a/src/main/text-generation/requests/conversation-name-generation-request.test.ts +++ b/src/main/text-generation/requests/conversation-name-generation-request.test.ts @@ -1,10 +1,5 @@ -import { realKimiOccupiedOutput } from '../../../shared/__fixtures__/kimi-generation-source-shaped' -import { describe, expect, it, vi } from 'vitest' -import { createMockDiscoveryChild } from '../execution/commit-message-text-generation-test-harness' +import { describe, expect, it } from 'vitest' import { generateConversationNameFromContext } from '../commit-message-text-generation' -import { cancelLocalGeneration } from '../execution/source-control-generation-lanes' -import { generateConversationName } from './conversation-name-generation-request' -import type { SpawnedSourceControlAgentProcess } from '../source-control-text-generation-types' describe('generateConversationName', () => { it.each([ @@ -76,33 +71,4 @@ describe('generateConversationName', () => { error: 'Generated chat name was empty after sanitization.' }) }) - - it.each(['cancel', 'occupied'])('handles a local %s outcome', async (outcome) => { - const child = createMockDiscoveryChild() - const spawnAgent = vi.fn(() => { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: The test child implements every process member the generation lane reads. - return child as unknown as SpawnedSourceControlAgentProcess - }) - const pending = generateConversationName({ - context: { firstPrompt: 'Fix login' }, - params: - outcome === 'cancel' - ? { agentId: 'custom', model: '', customAgentCommand: 'agent' } - : { agentId: 'kimi', model: 'default' }, - target: { kind: 'local', cwd: '/repo' }, - spawnAgent - }) - await vi.waitFor(() => expect(spawnAgent).toHaveBeenCalledOnce()) - if (outcome === 'occupied') { - child.stdout.emit('data', Buffer.from(realKimiOccupiedOutput)) - child.emit('close', 4) - expect(await pending).toMatchObject({ - success: false, - error: expect.stringMatching(/Kimi.*active.*Stop.*exit.*try again/) - }) - return - } - cancelLocalGeneration('conversation-name', '/repo') - expect(await pending).toMatchObject({ success: false, canceled: true }) - }) }) diff --git a/src/renderer/src/components/native-chat/links/previews/native-chat-github-routed-preview-store.test.tsx b/src/renderer/src/components/native-chat/links/previews/native-chat-github-routed-preview-store.test.tsx index 3c5b69f1614..c3c746bef90 100644 --- a/src/renderer/src/components/native-chat/links/previews/native-chat-github-routed-preview-store.test.tsx +++ b/src/renderer/src/components/native-chat/links/previews/native-chat-github-routed-preview-store.test.tsx @@ -83,29 +83,6 @@ afterEach(() => { vi.unstubAllGlobals() }) -it('shows the pending response after an unrelated store update', async () => { - const response = Promise.withResolvers() - const started = Promise.withResolvers() - mocks.endpoint.mockImplementationOnce(() => { - started.resolve() - return response.promise - }) - const link = parseGitHubIssueOrPRLink('https://github.com/acme/repo-2/pull/42')! - await act(async () => { - root.render( - - ) - }) - await started.promise - await act(async () => { - mocks.store.setState({ tick: 1 }) - }) - await act(async () => { - response.resolve(preview(42, 'Pending preview completed')) - }) - expect(container.textContent).toContain('Pending preview completed') -}) - it('shows the new route response and ignores a late response from the removed route', async () => { const oldResponse = Promise.withResolvers() const newResponse = Promise.withResolvers() diff --git a/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-flat-list.test.tsx b/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-flat-list.test.tsx deleted file mode 100644 index b5f2c6b64af..00000000000 --- a/src/renderer/src/components/workspace-cleanup/candidate-list/workspace-cleanup-flat-list.test.tsx +++ /dev/null @@ -1,90 +0,0 @@ -// @vitest-environment happy-dom -import type { ReactNode } from 'react' -import { act } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { CandidateRow } from './workspace-cleanup-candidate-row' -import { WorkspaceCleanupCandidateList } from './workspace-cleanup-candidate-list' -import { makeNamedFacets } from '../query/workspace-cleanup-facet.test.fixture' - -import type { WorkspaceCleanupFacets } from '../query/workspace-cleanup-facets' - -vi.mock('@/components/ui/tooltip', () => ({ - Tooltip: ({ children }: { children: ReactNode }) => <>{children}, - TooltipContent: () => null, - TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children} -})) - -let root: Root | null = null -let container: HTMLDivElement | null = null - -function renderRows( - rows: readonly WorkspaceCleanupFacets[], - onForgetLocally?: (candidate: WorkspaceCleanupFacets['candidate']) => void -): void { - act(() => - root?.render( - row.worktreeId} - scrollElement={null} - renderRow={(row, index) => ( - - )} - /> - ) - ) -} - -describe('workspace cleanup flat list', () => { - beforeEach(() => { - container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) - }) - - afterEach(() => { - if (root) { - act(() => root?.unmount()) - } - container?.remove() - root = null - container = null - }) - - it('offers local removal for a disconnected SSH row without making it selectable', () => { - const onForgetLocally = vi.fn() - const disconnected = makeNamedFacets('disconnected', { - candidate: { blockers: ['ssh-disconnected'] } - }) - - renderRows([disconnected], onForgetLocally) - - expect(container?.querySelector('[aria-label^="Select disconnected"]')).toBeNull() - const forgetButton = container?.querySelector('[aria-label="Remove from Orca"]') - expect(forgetButton).not.toBeNull() - act(() => forgetButton?.click()) - expect(onForgetLocally).toHaveBeenCalledWith(disconnected.candidate) - }) -}) diff --git a/src/renderer/src/components/workspace-cleanup/dialog/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx b/src/renderer/src/components/workspace-cleanup/dialog/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx index b8bd91fbbf0..4453a68a0b9 100644 --- a/src/renderer/src/components/workspace-cleanup/dialog/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx +++ b/src/renderer/src/components/workspace-cleanup/dialog/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx @@ -182,21 +182,6 @@ function cachedFleet(): WorkspaceCleanupScanResult { } } -function progressTick( - rig: ScanRig, - progress: Partial & Pick -): void { - rig.progressCallbacks.at(-1)?.({ - scanId: 'scan-1', - scannedAt: NOW, - scannedWorktreeCount: progress.candidates.length, - totalWorktreeCount: 3, - errors: [], - candidateMode: 'append', - ...progress - }) -} - describe('WorkspaceCleanupDialog stale-while-revalidate', () => { beforeEach(() => { globalThis.IS_REACT_ACT_ENVIRONMENT = true @@ -299,46 +284,6 @@ describe('WorkspaceCleanupDialog stale-while-revalidate', () => { expect(rowCheckbox('alpha')?.getAttribute('aria-checked')).toBe('false') }) - it('adopts the in-flight scan on reopen instead of starting a duplicate', async () => { - const rig = installApi(cachedFleet()) - await renderDialog() - await openDialog() - expect(rig.scan).toHaveBeenCalledTimes(1) - - progressTick(rig, { - candidates: [makeCandidate({ worktreeId: 'repo1::/tmp/gamma', displayName: 'gamma' })], - scannedWorktreeCount: 1 - }) - await flush() - - await act(async () => { - holders.store.setState({ activeModal: null }) - }) - await openDialog() - expect(rig.scan).toHaveBeenCalledTimes(1) - - progressTick(rig, { - candidates: [makeCandidate({ worktreeId: 'repo1::/tmp/delta', displayName: 'delta' })], - scannedWorktreeCount: 2 - }) - await flush() - await act(async () => { - rig.resolvers.at(-1)?.({ - scannedAt: NOW, - candidates: [ - ...cachedFleet().candidates, - makeCandidate({ worktreeId: 'repo1::/tmp/gamma', displayName: 'gamma' }), - makeCandidate({ worktreeId: 'repo1::/tmp/delta', displayName: 'delta' }) - ], - errors: [] - }) - }) - await flush() - - expect(rig.scan).toHaveBeenCalledTimes(1) - expect(rowNames()).toEqual(expect.arrayContaining(['alpha', 'beta', 'gamma', 'delta'])) - }) - it('keeps selections across a refresh and surfaces vanished selections', async () => { const rig = installApi(cachedFleet()) await renderDialog() diff --git a/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.test.ts b/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.test.ts deleted file mode 100644 index 30d335b3083..00000000000 --- a/src/renderer/src/components/workspace-cleanup/query/workspace-cleanup-facet-sort.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { makeNamedFacets } from './workspace-cleanup-facet.test.fixture' -import { sortWorkspaceCleanupFacets } from './workspace-cleanup-facet-sort' -import type { WorkspaceCleanupSortField } from '../../../../../shared/workspace-cleanup/workspace-cleanup-filter-model' - -function names( - rows: ReturnType[], - field: WorkspaceCleanupSortField, - direction: 'asc' | 'desc' -): string[] { - return sortWorkspaceCleanupFacets(rows, { field, direction }).map((row) => row.displayName) -} - -describe('ranked fields', () => { - it('breaks equal blocker counts by the worst blocker', () => { - const rows = [ - makeNamedFacets('softest', { - candidate: { blockers: ['git-status-error'] } - }), - makeNamedFacets('hardest', { - candidate: { blockers: ['main-worktree'] } - }) - ] - expect(names(rows, 'blocker-count', 'desc')).toEqual(['hardest', 'softest']) - }) -}) diff --git a/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.test.ts b/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.test.ts deleted file mode 100644 index 57d428bd218..00000000000 --- a/src/renderer/src/components/workspace-cleanup/removal/settlement/workspace-cleanup-removal-settlement.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { - trackWorkspaceCleanupLateSettlement, - type WorkspaceCleanupRemovalSettlement -} from './workspace-cleanup-removal-settlement' -import { makeCandidate } from '../../query/workspace-cleanup-candidate-state.test-fixture' - -describe('workspace cleanup late settlement tracking', () => { - it('detaches the batch callback and reports with a compact candidate identity', async () => { - const candidate = makeCandidate() - let resolveSettlement: (settlement: WorkspaceCleanupRemovalSettlement) => void = () => {} - const settlement = new Promise((resolve) => { - resolveSettlement = resolve - }) - const reconcileBeforeBatchResult = vi.fn() - const reportAfterBatchResult = vi.fn() - const tracker = trackWorkspaceCleanupLateSettlement( - settlement, - candidate, - reconcileBeforeBatchResult - ) - - // The compact copy keeps the host facts a late failure needs to name its row. - expect(tracker.candidate).toEqual({ - worktreeId: candidate.worktreeId, - connectionId: candidate.connectionId, - displayName: candidate.displayName - }) - expect(tracker.candidate).not.toBe(candidate) - tracker.detach(reportAfterBatchResult) - resolveSettlement({ status: 'rejected', error: new Error('remote removal failed') }) - await settlement - await Promise.resolve() - - expect(reconcileBeforeBatchResult).not.toHaveBeenCalled() - expect(reportAfterBatchResult).toHaveBeenCalledWith( - { - worktreeId: candidate.worktreeId, - connectionId: candidate.connectionId, - displayName: candidate.displayName - }, - { - removedIds: [], - removedIdentities: [], - failures: [ - { - worktreeId: candidate.worktreeId, - executionHostId: 'local', - displayName: candidate.displayName, - message: 'remote removal failed' - } - ] - } - ) - expect(reportAfterBatchResult.mock.calls[0][0]).not.toBe(candidate) - }) -}) From d1d338cd02596a84061ede488295fea0cf0ccb70 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 01:52:46 -0800 Subject: [PATCH 063/178] test: remove generation replays and cache counter cases (#41944) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 1 - .../integration-state-file-cache.test.ts | 60 ------------ .../integration-token-file-cache.test.ts | 19 ---- .../commit-message-agent-environment.test.ts | 16 ---- ...e-text-generation-local-subprocess.test.ts | 33 +------ ...e-text-generation-remote-execution.test.ts | 40 -------- .../source-control-agent-cli-path.test.ts | 3 +- .../source-control-agent-launch.test.ts | 93 ------------------- 8 files changed, 2 insertions(+), 263 deletions(-) delete mode 100644 src/main/text-generation/execution/source-control-agent-launch.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index a3d35790db0..38ac05a3f8c 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -3903,7 +3903,6 @@ "src/main/text-generation/requests/jira-issue-summary-requests.test.ts": 276, "src/main/text-generation/failures/kimi-generation-failure.test.ts": 151, "src/main/text-generation/execution/source-control-agent-cli-path.test.ts": 615, - "src/main/text-generation/execution/source-control-agent-launch.test.ts": 1879, "src/main/text-generation/execution/source-control-local-process.test.ts": 1834, "src/main/text-generation/prompts/source-control-omp-prompt-file.test.ts": 6676, "src/main/text-generation/execution/source-control-wsl-launch.test.ts": 482, diff --git a/src/main/credentials/integration-state-file-cache.test.ts b/src/main/credentials/integration-state-file-cache.test.ts index 92673a31041..a2bb7cee926 100644 --- a/src/main/credentials/integration-state-file-cache.test.ts +++ b/src/main/credentials/integration-state-file-cache.test.ts @@ -28,23 +28,6 @@ afterEach(() => { }) describe('createIntegrationStateFileCache', () => { - it('memoizes while the file is unchanged', () => { - const path = tempFile() - writeFileSync(path, '1') - let reads = 0 - const cache = createIntegrationStateFileCache({ - filePath: () => path, - readFromDisk: () => { - reads += 1 - return { reads } - } - }) - - expect(cache.get()).toEqual({ reads: 1 }) - expect(cache.get()).toEqual({ reads: 1 }) - expect(reads).toBe(1) - }) - it('notices a same-size atomic replacement with the original modification time', () => { const path = tempFile() writeFileSync(path, 'old') @@ -63,18 +46,6 @@ describe('createIntegrationStateFileCache', () => { expect(cache.get()).toBe('new') }) - it('distinguishes backing paths when both files are initially absent', () => { - let path = tempFile() - let reads = 0 - const cache = createIntegrationStateFileCache({ - filePath: () => path, - readFromDisk: () => ++reads - }) - expect(cache.get()).toBe(1) - path = join(dir, 'another.json') - expect(cache.get()).toBe(2) - }) - it('reloads when a file appears after an absent first read', () => { const path = tempFile() const cache = createIntegrationStateFileCache({ @@ -101,37 +72,6 @@ describe('createIntegrationStateFileCache', () => { expect(cache.get()).toEqual({ present: false }) }) - - it('recovers when another process writes between the stamp and the read', () => { - const path = tempFile() - writeFileSync(path, 'v1') - let reads = 0 - let writeDuringRead = false - const cache = createIntegrationStateFileCache({ - filePath: () => path, - readFromDisk: () => { - reads += 1 - if (writeDuringRead) { - writeDuringRead = false - writeFileSync(path, 'theirs') - } - return { body: readBody(path) } - } - }) - expect(cache.get()).toEqual({ body: 'v1' }) - - writeFileSync(path, 'ours') - cache.invalidate() - writeDuringRead = true - - // Content read after a concurrent write still carries the earlier stamp, forcing one more read. - expect(cache.get()).toEqual({ body: 'theirs' }) - expect(cache.get()).toEqual({ body: 'theirs' }) - expect(reads).toBe(3) - - expect(cache.get()).toEqual({ body: 'theirs' }) - expect(reads).toBe(3) - }) }) function readBody(path: string): string { diff --git a/src/main/credentials/integration-token-file-cache.test.ts b/src/main/credentials/integration-token-file-cache.test.ts index f574052cc79..143b6b03b26 100644 --- a/src/main/credentials/integration-token-file-cache.test.ts +++ b/src/main/credentials/integration-token-file-cache.test.ts @@ -29,25 +29,6 @@ function createCache() { } describe('integration token file cache', () => { - it('bounds errors, keeps status lazy, and permits retry after keychain approval', () => { - const cache = createCache() - decrypt.mockImplementation(() => { - throw new Error('Keychain access denied') - }) - for (let index = 0; index < 132; index += 1) { - const id = `credential-${index}` - writeFileSync(filePath(id), Buffer.from([0x76, 0x31, 0x30, 0xff, 0xfe])) - expect(() => cache.read(id)).toThrow('Could not decrypt') - } - expect(cache.error('credential-131')).toContain('Could not decrypt') - expect(cache.error('credential-0')).toBeUndefined() - expect(cache.has('credential-131')).toBe(true) - expect(decrypt).toHaveBeenCalledTimes(132) - decrypt.mockReturnValue('approved-token') - expect(cache.read('credential-131')).toBe('approved-token') - expect(cache.error('credential-131')).toBeUndefined() - }) - it('clears an old decryption error after external replacement without prompting on status', () => { const cache = createCache() writeFileSync(filePath('site'), Buffer.from([0xff, 0xfe])) diff --git a/src/main/text-generation/execution/commit-message-agent-environment.test.ts b/src/main/text-generation/execution/commit-message-agent-environment.test.ts index f90cc8cb368..1d1c243eb64 100644 --- a/src/main/text-generation/execution/commit-message-agent-environment.test.ts +++ b/src/main/text-generation/execution/commit-message-agent-environment.test.ts @@ -71,7 +71,6 @@ describe('prepareLocalCommitMessageAgentEnv', () => { ANTHROPIC_AUTH_TOKEN: 'chosen-token' } }) - expect(await prepareLocalCommitMessageAgentEnv('custom', undefined)).toEqual({ ok: true }) }) it('hydrates OpenCode config dir from shell startup files for headless generation', async () => { @@ -89,21 +88,6 @@ describe('prepareLocalCommitMessageAgentEnv', () => { }) }) - it('hydrates Pi agent dir from shell startup files for headless generation', async () => { - const home = makeHome() - delete process.env.PI_CODING_AGENT_DIR - writeFileSync(join(home, '.zshrc'), 'export PI_CODING_AGENT_DIR="$HOME/.config/pi-agent"\n') - - const result = await prepareLocalCommitMessageAgentEnv('pi', undefined) - - expect(result).toEqual({ - ok: true, - env: expect.objectContaining({ - PI_CODING_AGENT_DIR: `${home}/.config/pi-agent` - }) - }) - }) - // STA-4422: failed managed-home preparation must not fall through to the user's real account while UI shows a managed account. it('strips a nested-Orca CODEX_HOME override when the launch resolves to the real home', async () => { diff --git a/src/main/text-generation/execution/commit-message-text-generation-local-subprocess.test.ts b/src/main/text-generation/execution/commit-message-text-generation-local-subprocess.test.ts index 28e3af02068..4206ed42778 100644 --- a/src/main/text-generation/execution/commit-message-text-generation-local-subprocess.test.ts +++ b/src/main/text-generation/execution/commit-message-text-generation-local-subprocess.test.ts @@ -2,10 +2,7 @@ import { spawn } from 'node:child_process' import type * as ChildProcess from 'node:child_process' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { generateCommitMessageFromContext } from '../commit-message-text-generation' -import { - createChildTerminationExpectation, - withPlatform -} from './commit-message-text-generation-test-harness' +import { createChildTerminationExpectation } from './commit-message-text-generation-test-harness' const { terminateWindowsProcessTreeMock } = vi.hoisted(() => ({ terminateWindowsProcessTreeMock: vi.fn(async () => {}) @@ -84,32 +81,4 @@ describe('generateCommitMessageFromContext on the Windows direct-child path', () }) await expectChildTerminated(child) }) - - it('rejects unsafe argv prompts for Windows batch-script agent commands', async () => { - await withPlatform('win32', async () => { - const result = await generateCommitMessageFromContext( - { - branch: 'main', - stagedSummary: 'M\tREADME.md', - stagedPatch: '+hello & goodbye' - }, - { - agentId: 'custom', - model: '', - customAgentCommand: 'C:/tools/agent.cmd {prompt}' - }, - { - kind: 'local', - cwd: 'C:\\repo' - } - ) - - expect(result).toMatchObject({ - success: false, - error: - "C:/tools/agent.cmd runs through a Windows batch script that cannot take the multi-line prompt as an argument. Point Settings at the agent's executable, or use a custom command without {prompt} so Orca sends the prompt on stdin." - }) - expect(spawnMock).not.toHaveBeenCalled() - }) - }) }) diff --git a/src/main/text-generation/execution/commit-message-text-generation-remote-execution.test.ts b/src/main/text-generation/execution/commit-message-text-generation-remote-execution.test.ts index bb0ec740533..1b7445c090c 100644 --- a/src/main/text-generation/execution/commit-message-text-generation-remote-execution.test.ts +++ b/src/main/text-generation/execution/commit-message-text-generation-remote-execution.test.ts @@ -3,46 +3,6 @@ import { describe, expect, it } from 'vitest' import { generateCommitMessageFromContext } from '../commit-message-text-generation' describe('generateCommitMessageFromContext', () => { - it('uses a prepared remote execution plan instead of running git on the remote side', async () => { - const result = await generateCommitMessageFromContext( - { - branch: 'main', - stagedSummary: 'M\tREADME.md', - stagedPatch: '+hello' - }, - { - agentId: 'custom', - model: '', - customAgentCommand: 'agent --message {prompt}' - }, - { - kind: 'remote', - cwd: '/repo', - missingBinaryLocation: 'remote PATH', - execute: async (plan, cwd, timeoutMs) => { - expect(cwd).toBe('/repo') - expect(timeoutMs).toBe(60_000) - expect(plan.binary).toBe('agent') - expect(plan.args).toHaveLength(2) - expect(plan.args[0]).toBe('--message') - expect(plan.args[1]).toContain('Staged files:\nM\tREADME.md') - return { - stdout: 'Add README note.\n', - stderr: '', - exitCode: 0, - timedOut: false - } - } - } - ) - - expect(result).toEqual({ - success: true, - message: 'Add README note', - agentLabel: 'agent' - }) - }) - it('sanitizes remote execution transport failures', async () => { const result = await generateCommitMessageFromContext( { diff --git a/src/main/text-generation/execution/source-control-agent-cli-path.test.ts b/src/main/text-generation/execution/source-control-agent-cli-path.test.ts index 027c206b3eb..7735fd6c874 100644 --- a/src/main/text-generation/execution/source-control-agent-cli-path.test.ts +++ b/src/main/text-generation/execution/source-control-agent-cli-path.test.ts @@ -47,8 +47,7 @@ describe.runIf(process.platform !== 'win32')('chat naming CLI discovery', () => it.each([ ['./node_modules/.bin:EMPTY', 'node_modules/.bin'], - [':EMPTY', '.'], - ['EMPTY:', '.'] + [':EMPTY', '.'] ])('resolves recipe PATH %s from the workspace before global installs', async (path, folder) => { const home = mkdtempSync(join(tmpdir(), 'orca-naming-recipe-path-')) homes.push(home) diff --git a/src/main/text-generation/execution/source-control-agent-launch.test.ts b/src/main/text-generation/execution/source-control-agent-launch.test.ts deleted file mode 100644 index cda7896d791..00000000000 --- a/src/main/text-generation/execution/source-control-agent-launch.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import 'node:events' -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' -import type * as RunProcess from '@orca/process-host' -import type { ProcessSpec } from '@orca/process-host/process-spec' -import '../../provider-process/provider-process-supervisor' -import { generateCommitMessageFromContext } from '../commit-message-text-generation' -import './commit-message-text-generation-test-harness' -import './source-control-agent-launch' - -const { spawnProcessMock } = vi.hoisted(() => ({ spawnProcessMock: vi.fn() })) - -vi.mock('@orca/process-host', async (importOriginal) => { - const actual = await importOriginal() - spawnProcessMock.mockImplementation(actual.spawnProcess) - return { ...actual, spawnProcess: spawnProcessMock } -}) - -function lastSpawnSpec(): ProcessSpec { - return spawnProcessMock.mock.calls.at(-1)![0] -} - -function decodedSupervisorSpec(spec: ProcessSpec): Record { - return JSON.parse(Buffer.from(spec.env!.ORCA_PROVIDER_SUPERVISOR_SPEC!, 'base64').toString()) -} - -const folder = mkdtempSync(join(tmpdir(), 'orca-agent-launch-')) -afterAll(() => rmSync(folder, { recursive: true, force: true })) - -beforeEach(() => { - spawnProcessMock.mockClear() -}) - -describe.runIf(process.platform !== 'win32')('supervised agent processes', () => { - it('passes the stdin end through so the agent can answer its request', async () => { - const agent = join(folder, 'echo-agent.cjs') - writeFileSync( - agent, - `let prompt = '' -process.stdin.on('data', (chunk) => (prompt += chunk)) -process.stdin.on('end', () => setTimeout(() => console.log(prompt.includes('README') ? 'Update README' : 'no prompt'), 1200)) -` - ) - - await expect( - generateCommitMessageFromContext( - { - branch: 'main', - stagedSummary: 'M README.md', - stagedPatch: '+test' - }, - { - agentId: 'custom', - model: '', - customAgentCommand: `"${process.execPath}" "${agent}"` - }, - { kind: 'local', cwd: folder } - ) - ).resolves.toMatchObject({ success: true, message: 'Update README' }) - expect(lastSpawnSpec().program).toBe(process.execPath) - expect(decodedSupervisorSpec(lastSpawnSpec())).toMatchObject({ - lifetime: 'one-shot' - }) - }) - - it('drops the agent session that launched Orca but keeps assignments in the command', async () => { - const agent = join(folder, 'session-agent.cjs') - writeFileSync( - agent, - `process.stdin.resume() -process.stdin.on('end', () => console.log('Thread ' + (process.env.CODEX_THREAD_ID ?? 'unset') + ', effort ' + process.env.CLAUDE_EFFORT)) -` - ) - vi.stubEnv('CODEX_THREAD_ID', 'parent-thread') - try { - await expect( - generateCommitMessageFromContext( - { branch: 'main', stagedSummary: 'M README.md', stagedPatch: '+test' }, - { - agentId: 'custom', - model: '', - customAgentCommand: `CLAUDE_EFFORT=low "${process.execPath}" "${agent}"` - }, - { kind: 'local', cwd: folder } - ) - ).resolves.toMatchObject({ success: true, message: 'Thread unset, effort low' }) - } finally { - vi.unstubAllEnvs() - } - }) -}) From fde1ae70dac41473c97044b0ea81fb222fc9447d Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Fri, 13 Nov 2026 04:53:48 -0500 Subject: [PATCH 064/178] fix(mac): give Orca's main executable its own UUID so Local Network access works (#41676) * fix(mac): give Orca's main executable its own Mach-O UUID macOS Local Network privacy identifies an app by its main executable's UUID. The packaged executable is stock Electron's, so it shares that UUID with every other app on the same Electron version and macOS can apply another app's Local Network decision to Orca. Rewrite LC_UUID in every slice to a value derived from the app id, executable name and CPU type, before signing. * fix(mac): re-sign rewritten executables ad hoc, cover the terminal host, assert UUIDs at release Rewriting LC_UUID breaks Electron's linker ad hoc signature, and arm64 kills unsigned code, so identity-less local builds (where app signing is skipped) produced an Orca.app that died at launch. Re-sign the rewritten executable ad hoc, outside the bundle so codesign does not treat it as the bundle; real signing replaces it later. The terminal host's executable is a stock Node binary whose UUID is shared by every copy of that Node release, so give it its own stable UUID the same way. Packaging and release verification now fail when a shipped executable does not carry its expected UUID. * fix(mac): scope the executable UUID rewrite to the main app The terminal host gets a separate design; leave it unchanged here. --- config/electron-builder.config.cjs | 14 +++ .../macos/build-mac-notarization-overlap.mjs | 7 +- .../macos/macos-executable-uuid.cjs | 115 ++++++++++++++++++ .../macos/macos-executable-uuid.test.mjs | 99 +++++++++++++++ 4 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 config/scripts/desktop-distribution/macos/macos-executable-uuid.cjs create mode 100644 config/scripts/desktop-distribution/macos/macos-executable-uuid.test.mjs diff --git a/config/electron-builder.config.cjs b/config/electron-builder.config.cjs index affa225978c..c6531b87a38 100644 --- a/config/electron-builder.config.cjs +++ b/config/electron-builder.config.cjs @@ -5,6 +5,9 @@ const { MACOS_FOLDER_USAGE_DESCRIPTIONS, applyMacHelperFolderUsageDescriptions } = require('./scripts/desktop-distribution/macos/macos-folder-usage-descriptions.cjs') +const { + giveMacExecutableUniqueUuid +} = require('./scripts/desktop-distribution/macos/macos-executable-uuid.cjs') const electronBuilderNativeRebuild = require('./scripts/desktop-distribution/runtime-verification/electron-builder-native-rebuild.cjs') const { assertPackagedDaemonEntryExists, @@ -426,6 +429,17 @@ module.exports = { join(context.appOutDir, `${context.packager.appInfo.productFilename}.app`), context.packager.appInfo.productFilename ) + // Before signing, so the signature seals the rewritten executable. + giveMacExecutableUniqueUuid( + join( + context.appOutDir, + `${context.packager.appInfo.productFilename}.app`, + 'Contents', + 'MacOS', + context.packager.appInfo.productFilename + ), + context.packager.appInfo.id + ) const architecture = macSliceArch(context.arch, 'local-build compatibility') const schedulingAddon = buildMacShipItScheduling(resourcesDir, architecture) await signMacStandaloneHelper(schedulingAddon, 'ShipIt scheduling', context.packager) diff --git a/config/scripts/desktop-distribution/macos/build-mac-notarization-overlap.mjs b/config/scripts/desktop-distribution/macos/build-mac-notarization-overlap.mjs index 81c092a1282..0f064c2db1f 100644 --- a/config/scripts/desktop-distribution/macos/build-mac-notarization-overlap.mjs +++ b/config/scripts/desktop-distribution/macos/build-mac-notarization-overlap.mjs @@ -1,6 +1,7 @@ import { execFileSync } from 'node:child_process' import { createRequire } from 'node:module' -import { join, resolve } from 'node:path' +import { basename, join, resolve } from 'node:path' +import { assertMacExecutableUuid } from './macos-executable-uuid.cjs' const ARCHITECTURES = [1, 3] // electron-builder: x64, arm64. @@ -138,6 +139,10 @@ export async function main(args = process.argv.slice(2)) { verify: (appPath) => { execFileSync('codesign', ['--verify', '--deep', '--strict', appPath], { stdio: 'inherit' }) execFileSync('xcrun', ['stapler', 'validate', appPath], { stdio: 'inherit' }) + assertMacExecutableUuid( + join(appPath, 'Contents', 'MacOS', basename(appPath, '.app')), + config.appId + ) }, targets: new Map([ [ diff --git a/config/scripts/desktop-distribution/macos/macos-executable-uuid.cjs b/config/scripts/desktop-distribution/macos/macos-executable-uuid.cjs new file mode 100644 index 00000000000..f07939c09b4 --- /dev/null +++ b/config/scripts/desktop-distribution/macos/macos-executable-uuid.cjs @@ -0,0 +1,115 @@ +// macOS Local Network privacy maps a process to its app by executable UUID; stock Electron's is shared by every app shipping that build (TN3179). +const { execFileSync } = require('node:child_process') +const { createHash } = require('node:crypto') +const { copyFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } = require('node:fs') +const { tmpdir } = require('node:os') +const { basename, join } = require('node:path') + +const MH_MAGIC = 0xfeedface +const MH_MAGIC_64 = 0xfeedfacf +const FAT_MAGIC = 0xcafebabe +const FAT_MAGIC_64 = 0xcafebabf +const LC_UUID = 0x1b + +function formatUuid(bytes) { + const hex = Buffer.from(bytes).toString('hex').toUpperCase() + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}` +} + +// Stable per app, executable and CPU so releases keep one identity, as the stock UUID did within an Electron version. +function deriveExecutableUuid(appId, executableName, cpuType) { + const uuid = createHash('sha256') + .update(`${appId}\0${executableName}\0${cpuType}`) + .digest() + .subarray(0, 16) + uuid[6] = (uuid[6] & 0x0f) | 0x80 + uuid[8] = (uuid[8] & 0x3f) | 0x80 + return uuid +} + +function sliceOffsets(buffer) { + const magic = buffer.readUInt32BE(0) + if (magic !== FAT_MAGIC && magic !== FAT_MAGIC_64) { + return [0] + } + const is64 = magic === FAT_MAGIC_64 + const count = buffer.readUInt32BE(4) + const offsets = [] + for (let i = 0; i < count; i++) { + const entry = 8 + i * (is64 ? 32 : 20) + offsets.push(is64 ? Number(buffer.readBigUInt64BE(entry + 8)) : buffer.readUInt32BE(entry + 8)) + } + return offsets +} + +/** Where each slice's LC_UUID payload sits. */ +function machOUuidSlots(buffer) { + return sliceOffsets(buffer).map((offset) => { + const magic = buffer.readUInt32LE(offset) + if (magic !== MH_MAGIC && magic !== MH_MAGIC_64) { + throw new Error( + `Unsupported Mach-O slice at offset ${offset} (magic 0x${magic.toString(16)})` + ) + } + const cpuType = buffer.readUInt32LE(offset + 4) + const commandCount = buffer.readUInt32LE(offset + 16) + let command = offset + (magic === MH_MAGIC_64 ? 32 : 28) + for (let i = 0; i < commandCount; i++) { + if (buffer.readUInt32LE(command) === LC_UUID) { + return { cpuType, uuidOffset: command + 8 } + } + command += buffer.readUInt32LE(command + 4) + } + throw new Error(`Mach-O slice for CPU type ${cpuType} has no LC_UUID`) + }) +} + +/** Rewrites LC_UUID in every slice of `buffer` in place. */ +function rewriteMachOUuids(buffer, appId, executableName) { + return machOUuidSlots(buffer).map(({ cpuType, uuidOffset }) => { + const before = formatUuid(buffer.subarray(uuidOffset, uuidOffset + 16)) + const next = deriveExecutableUuid(appId, executableName, cpuType) + next.copy(buffer, uuidOffset) + return { cpuType, before, after: formatUuid(next) } + }) +} + +/** Gives the executable its own UUID, then re-signs it ad hoc; real signing replaces that later. */ +function giveMacExecutableUniqueUuid(executable, appId) { + const name = basename(executable) + const buffer = readFileSync(executable) + const changes = rewriteMachOUuids(buffer, appId, name) + // The edit breaks Electron's linker ad hoc signature and arm64 kills unsigned code, but identity-less builds skip app signing. + // Signed outside the bundle: in place, codesign would treat it as the bundle and require its unsigned siblings be signed. + const staging = mkdtempSync(join(tmpdir(), 'orca-executable-uuid-')) + try { + const staged = join(staging, name) + writeFileSync(staged, buffer, { mode: 0o755 }) + execFileSync('codesign', ['--force', '--sign', '-', '--identifier', appId, staged], { + stdio: 'inherit' + }) + copyFileSync(staged, executable) + } finally { + rmSync(staging, { recursive: true, force: true }) + } + for (const { cpuType, before, after } of changes) { + console.log(`[macos-executable-uuid] ${name} cpu ${cpuType}: ${before} -> ${after}`) + } +} + +/** Fails when a shipped executable lost its own UUID, e.g. the hook was skipped or signing rewrote it. */ +function assertMacExecutableUuid(executable, appId) { + const name = basename(executable) + const buffer = readFileSync(executable) + for (const { cpuType, uuidOffset } of machOUuidSlots(buffer)) { + const actual = formatUuid(buffer.subarray(uuidOffset, uuidOffset + 16)) + const expected = formatUuid(deriveExecutableUuid(appId, name, cpuType)) + if (actual !== expected) { + throw new Error( + `[macos-executable-uuid] ${executable} cpu ${cpuType} has UUID ${actual}, expected ${expected}` + ) + } + } +} + +module.exports = { assertMacExecutableUuid, giveMacExecutableUniqueUuid, rewriteMachOUuids } diff --git a/config/scripts/desktop-distribution/macos/macos-executable-uuid.test.mjs b/config/scripts/desktop-distribution/macos/macos-executable-uuid.test.mjs new file mode 100644 index 00000000000..0f5b041221c --- /dev/null +++ b/config/scripts/desktop-distribution/macos/macos-executable-uuid.test.mjs @@ -0,0 +1,99 @@ +import { execFileSync } from 'node:child_process' +import { copyFileSync, mkdtempSync, rmSync } from 'node:fs' +import { createRequire } from 'node:module' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' + +const require = createRequire(import.meta.url) +const { + assertMacExecutableUuid, + giveMacExecutableUniqueUuid, + rewriteMachOUuids +} = require('./macos-executable-uuid.cjs') + +const ARM64 = 0x0100000c +const X86_64 = 0x01000007 +const STOCK_UUID = Buffer.from('4C4C44C055553144A1E0C291B44FDBE3', 'hex') + +function thinSlice(cpuType) { + const header = Buffer.alloc(32) + header.writeUInt32LE(0xfeedfacf, 0) + header.writeUInt32LE(cpuType, 4) + header.writeUInt32LE(2, 12) + header.writeUInt32LE(2, 16) + header.writeUInt32LE(16 + 24, 20) + const other = Buffer.alloc(16, 0xaa) + other.writeUInt32LE(0x19, 0) + other.writeUInt32LE(16, 4) + const uuid = Buffer.alloc(24) + uuid.writeUInt32LE(0x1b, 0) + uuid.writeUInt32LE(24, 4) + STOCK_UUID.copy(uuid, 8) + return Buffer.concat([header, other, uuid, Buffer.alloc(8, 0xbb)]) +} + +function fatBinary(slices) { + const table = Buffer.alloc(8 + slices.length * 20) + table.writeUInt32BE(0xcafebabe, 0) + table.writeUInt32BE(slices.length, 4) + const parts = [table] + let offset = 4096 + slices.forEach((slice, i) => { + table.writeUInt32BE(slice.readUInt32LE(4), 8 + i * 20) + table.writeUInt32BE(offset, 8 + i * 20 + 8) + table.writeUInt32BE(slice.length, 8 + i * 20 + 12) + const end = parts.reduce((n, p) => n + p.length, 0) + parts.push(Buffer.alloc(offset - end), slice) + offset += 4096 + }) + return Buffer.concat(parts) +} + +const uuidAt = (buffer, offset) => buffer.subarray(offset, offset + 16).toString('hex') + +describe('rewriteMachOUuids', () => { + it('replaces the stock UUID of a thin executable and nothing else', () => { + const original = thinSlice(ARM64) + const rewritten = Buffer.from(original) + rewriteMachOUuids(rewritten, 'com.stablyai.orca', 'Orca') + expect(uuidAt(rewritten, 56)).toBe('e64304103b228bd69677b18884034a30') + expect(Buffer.concat([rewritten.subarray(0, 56), rewritten.subarray(72)])).toEqual( + Buffer.concat([original.subarray(0, 56), original.subarray(72)]) + ) + }) + + it('gives every slice of a universal executable its own stable UUID', () => { + const binary = fatBinary([thinSlice(X86_64), thinSlice(ARM64)]) + rewriteMachOUuids(binary, 'com.stablyai.orca', 'Orca') + rewriteMachOUuids(binary, 'com.stablyai.orca', 'Orca') + expect(uuidAt(binary, 4096 + 56)).toBe('016ee6813dc381c7a6da12dc23b89dd9') + expect(uuidAt(binary, 8192 + 56)).toBe('e64304103b228bd69677b18884034a30') + }) + + it('refuses an executable without LC_UUID', () => { + const slice = thinSlice(ARM64) + slice.writeUInt32LE(0x1a, 48) + expect(() => rewriteMachOUuids(slice, 'com.stablyai.orca', 'Orca')).toThrow('has no LC_UUID') + }) +}) + +describe.runIf(process.platform === 'darwin')('giveMacExecutableUniqueUuid', () => { + it('leaves a signed, runnable universal executable that passes the release check', () => { + const dir = mkdtempSync(join(tmpdir(), 'orca-exe-uuid-')) + try { + const executable = join(dir, 'Orca') + // A real platform-signed fat binary: rewriting it without re-signing gets it killed at exec. + copyFileSync('/usr/bin/true', executable) + expect(() => assertMacExecutableUuid(executable, 'com.stablyai.orca')).toThrow( + 'expected 016EE681-3DC3-81C7-A6DA-12DC23B89DD9' + ) + giveMacExecutableUniqueUuid(executable, 'com.stablyai.orca') + execFileSync(executable) + execFileSync('codesign', ['--verify', '--strict', executable]) + assertMacExecutableUuid(executable, 'com.stablyai.orca') + } finally { + rmSync(dir, { recursive: true, force: true }) + } + }) +}) From 7f43d8aab63681c2ae6dccf9e5707e79e5a62971 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 02:01:50 -0800 Subject: [PATCH 065/178] test: remove prompt transport replays and secret bookkeeping cases (#41947) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- ...ted-secret-persistence-concurrency.test.ts | 12 - .../protected-secret-persistence.test.ts | 30 --- .../saved-credential-readability.test.ts | 3 +- ...xt-generation-failure-sanitization.test.ts | 31 --- .../failures/kimi-generation-failure.test.ts | 37 --- .../source-control-omp-prompt-file.test.ts | 230 +----------------- .../prompts/source-control-wsl-prompt.test.ts | 69 ------ .../issue-command-wsl-stopped-distro.test.ts | 28 --- 8 files changed, 2 insertions(+), 438 deletions(-) diff --git a/src/main/credentials/protected-secret-persistence-concurrency.test.ts b/src/main/credentials/protected-secret-persistence-concurrency.test.ts index 5696287957f..c905b84c8a5 100644 --- a/src/main/credentials/protected-secret-persistence-concurrency.test.ts +++ b/src/main/credentials/protected-secret-persistence-concurrency.test.ts @@ -153,18 +153,6 @@ describe('protected secret acknowledgements', () => { expect(secrets.encrypt('remove', 'replacement').blob).toBe('') }) - it('does not accept a prepared update from a replaced persistence instance', () => { - const previous = new ProtectedSecretPersistence() - const current = new ProtectedSecretPersistence() - const older = prepare(previous, 'slot', 'older') - const newer = prepare(current, 'slot', 'newer') - - current.commitRetentionUpdates([older, newer]) - - cipherState.available = false - expect(current.encrypt('slot', 'replacement').blob).toBe(ciphertext('newer')) - }) - it('keeps the previous ciphertext and pending retry when a prepared save has no confirmed ack', () => { const secrets = new ProtectedSecretPersistence() const original = ciphertext('original') diff --git a/src/main/credentials/protected-secret-persistence.test.ts b/src/main/credentials/protected-secret-persistence.test.ts index a0fa87fccc7..81fea025bb6 100644 --- a/src/main/credentials/protected-secret-persistence.test.ts +++ b/src/main/credentials/protected-secret-persistence.test.ts @@ -44,34 +44,4 @@ describe('ProtectedSecretPersistence', () => { secrets.removeRetainedBlob(slot) expect(secrets.encrypt(slot, '')).toEqual({ blob: '', degraded: false }) }) - - it('keeps deferred encryption pending until its retention update commits', async () => { - const { ProtectedSecretPersistence } = await import('./protected-secret-persistence') - const secrets = new ProtectedSecretPersistence() - cipherState.available = false - secrets.encrypt('slot', 'pending') - expect(secrets.hasPendingEncryption()).toBe(true) - cipherState.available = true - const encrypted = secrets.encrypt('slot', 'pending') - expect(secrets.hasPendingEncryption()).toBe(true) - if (!encrypted.retentionUpdate) { - throw new Error('Expected a retention update') - } - secrets.commitRetentionUpdates([encrypted.retentionUpdate]) - expect(secrets.hasPendingEncryption()).toBe(false) - }) - - it('retires a deferred empty secret without retaining a phantom retry', async () => { - const { ProtectedSecretPersistence } = await import('./protected-secret-persistence') - const secrets = new ProtectedSecretPersistence() - cipherState.available = false - secrets.encrypt('slot', 'pending') - const cleared = secrets.encrypt('slot', '') - expect(secrets.hasPendingEncryption()).toBe(true) - if (!cleared.retentionUpdate) { - throw new Error('Expected a retention update') - } - secrets.commitRetentionUpdates([cleared.retentionUpdate]) - expect(secrets.hasPendingEncryption()).toBe(false) - }) }) diff --git a/src/main/credentials/saved-credential-readability.test.ts b/src/main/credentials/saved-credential-readability.test.ts index 6f5d23b6426..c4607cf8279 100644 --- a/src/main/credentials/saved-credential-readability.test.ts +++ b/src/main/credentials/saved-credential-readability.test.ts @@ -49,8 +49,7 @@ describe('savedCredentialUnreadable', () => { it.each([ ['an empty', ''], - ['a non-base64', 'not*base64!'], - ['a truncated base64', 'c2VhbGVkLWhlcmU6a2V5x'] + ['a non-base64', 'not*base64!'] ])('reports %s payload as unreadable, not sealed elsewhere', (_label, payload) => { expect(judge(`${PREFIX}encrypted:${payload}`)).toBe('unreadable') }) diff --git a/src/main/text-generation/failures/commit-message-text-generation-failure-sanitization.test.ts b/src/main/text-generation/failures/commit-message-text-generation-failure-sanitization.test.ts index 2a1977e2825..df1c71b8d18 100644 --- a/src/main/text-generation/failures/commit-message-text-generation-failure-sanitization.test.ts +++ b/src/main/text-generation/failures/commit-message-text-generation-failure-sanitization.test.ts @@ -2,37 +2,6 @@ import { describe, expect, it } from 'vitest' import { generateCommitMessageFromContext } from '../commit-message-text-generation' describe('generateCommitMessageFromContext', () => { - it('exposes raw CLI failure output only after path sanitization', async () => { - const result = await generateCommitMessageFromContext( - { - branch: 'main', - stagedSummary: 'M\tREADME.md', - stagedPatch: '+hello' - }, - { - agentId: 'custom', - model: '', - customAgentCommand: 'agent' - }, - { - kind: 'remote', - cwd: '/repo', - missingBinaryLocation: 'remote PATH', - execute: async () => ({ - stdout: 'You are generating a single git commit message for /secret/repo', - stderr: 'raw failure output with /Users/thebr/My Repo/secret/file.ts', - exitCode: 1, - timedOut: false - }) - } - ) - - expect(result).toMatchObject({ - success: false, - error: 'agent CLI command failed with code 1: raw failure output with [path]' - }) - }) - it('formats nonzero exit failures with extracted sanitized CLI details', async () => { const result = await generateCommitMessageFromContext( { diff --git a/src/main/text-generation/failures/kimi-generation-failure.test.ts b/src/main/text-generation/failures/kimi-generation-failure.test.ts index ab37b8665da..46edc2093c9 100644 --- a/src/main/text-generation/failures/kimi-generation-failure.test.ts +++ b/src/main/text-generation/failures/kimi-generation-failure.test.ts @@ -27,7 +27,6 @@ const run = (message: string) => `error: failed to run prompt: ${message}\nSee l const network = run('provider.connection_error: getaddrinfo ENOTFOUND private-host') const agentOptions = /^Kimi received conflicting or repeated agent options\. Keep a single --agent or --agent-file option in the generation recipe or CLI arguments, then retry\.$/ -const credentials = /credentials.*execution host.*configure.*sign in/ const parser = /^Kimi rejected an option or argument\. Correct the generation recipe or CLI arguments, then retry\.$/ const delimiters = "private-x' argument '".repeat(12) @@ -36,36 +35,11 @@ const delimiters = "private-x' argument '".repeat(12) it.each([ ['', occupied, realKimiOccupiedOutput, 4], [run('unrelated private-prose'), occupied, `${earlier}\n${realKimiOccupiedOutput}`, 4], - [run('Session "private-session" not found.'), /find.*session.*existing session.*new one/], - [ - run('Session "private-session" was created under a different directory.'), - /another directory.*Resume it there.*workspace/ - ], - [ - run('no provider configured; complete onboarding via /login or the providers endpoint'), - credentials - ], - [ - run( - 'Provider "private-provider" declares api_key_env = "PRIVATE_ENV" in config.toml, but the environment variable is not set or is empty.' - ), - /credential environment variable.*Set it.*execution host/ - ], - [ - run('model private-model does not resolve to a configured provider'), - /usable model.*configure or select/ - ], - [run('Prompt hook blocked the request.'), /hook blocked.*Review.*hooks.*retry/], - [run('Prompt turn ended with reason: cancelled'), /cancelled.*Try again/], [ run('provider.auth_error: private-provider prose'), /authenticate.*Sign in.*credentials.*retry/, '{broken' ], - [run('provider.rate_limit: private-provider prose'), /limit.*Wait.*reduce/], - [run('Provider safety policy blocked the response.'), /blocked.*Revise.*prompt.*configuration/], - [run('context.overflow: private-provider prose'), /context.*Shorten.*new session/], - [run('config.invalid: private-provider prose'), /invalid configuration.*Check.*model.*provider/], [network, /connect.*network.*retry/], [network, /connect.*network.*MagicDNS/, '', 1, true], ...[ @@ -84,17 +58,6 @@ it.each([ [run("unknown option '--private-flag'"), unknown, '{"type":"error","code":"unknown.code"}'], [`error: option '${delimiters}tail`, unknown], [`error: option '${delimiters}value' is invalid. provider.auth_error: private-prose`, parser], - ['error: Login required\n\nmessage:\nprivate-prose\nSee log: private-path', credentials], - ['error: Prompt cannot be empty.', /empty prompt.*recipe.*CLI arguments/], - [ - 'error: Cannot combine --prompt with --auto.', - /conflicting.*--prompt.*--auto.*recipe.*arguments/ - ], - [ - 'error: Cannot combine --agent/--agent-file with --session/--continue: the agent is bound at session creation and the bound agent is restored automatically on resume.', - /cannot change the agent.*Remove.*override.*new session/ - ], - ['error: Cannot use --session without an id in prompt mode.', /session id.*recipe.*arguments/], ["hook narration error: unknown option '--private-flag'", unknown], ['', /Kimi.*interrupted.*Try again/, '', null], ['', invalid, realKimiOccupiedOutput, 0], diff --git a/src/main/text-generation/prompts/source-control-omp-prompt-file.test.ts b/src/main/text-generation/prompts/source-control-omp-prompt-file.test.ts index 0b5dfe6f972..ce2baf2088d 100644 --- a/src/main/text-generation/prompts/source-control-omp-prompt-file.test.ts +++ b/src/main/text-generation/prompts/source-control-omp-prompt-file.test.ts @@ -1,18 +1,9 @@ import { spawnProcess } from '@orca/process-host' -import * as agentSpecs from '../../../shared/source-control/ai/generation/commit-message-agent-spec' -import type { ResolvedSourceControlAiGenerationParams } from '../../../shared/source-control/ai/source-control-ai-operation-resolution' import type { SpawnSourceControlAgent } from '../source-control-text-generation-types' -import { - generateCommitMessage, - generatePullRequestFields, - generateBranchName -} from '../requests/source-control-text-generation-requests' -import { generateConversationName } from '../requests/conversation-name-generation-request' -import { generateJiraIssueSummaryFromContext } from '../requests/jira-issue-summary-text-generation' +import { generateCommitMessage } from '../requests/source-control-text-generation-requests' import { existsSync, mkdtempSync, readFileSync } from 'node:fs' import { rm } from 'node:fs/promises' -import type * as FsPromises from 'node:fs/promises' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { afterEach, expect, it, vi } from 'vitest' @@ -29,13 +20,6 @@ vi.mock('../../pty/process-retirement/windows-process-tree-kill', () => ({ terminateWindowsProcessTree: vi.fn(async () => true) })) -vi.mock('node:fs/promises', async (importOriginal) => { - const actual = await importOriginal() - return { ...actual, rm: vi.fn(actual.rm) } -}) - -const actualFs = await vi.importActual('node:fs/promises') - afterEach(() => vi.useRealTimers()) // OMP 17.0.5 recorded stdin-only execution: exit 0, empty stdout/stderr, no submitted prompt. @@ -137,96 +121,6 @@ const commitContext = { stagedPatch: '+ Trace lighthouse signal' } -type Consumer = { - label: string - run( - params: ResolvedSourceControlAiGenerationParams, - spawnAgent: SpawnSourceControlAgent - ): Promise - output: string - expected: object -} -const consumers: Consumer[] = [ - { - label: 'commit', - run: (params, spawnAgent) => - generateCommitMessage({ context: commitContext, params, target: contractTarget, spawnAgent }), - output: 'Trace lighthouse signal.\n\nPreserve request bytes\n', - expected: { message: 'Trace lighthouse signal\n\nPreserve request bytes' } - }, - { - label: 'PR fields', - run: (params, spawnAgent) => - generatePullRequestFields({ - context: { - branch: 'feature', - base: 'main', - branchChangedByPreparation: true, - currentTitle: '', - currentBody: '', - currentDraft: false, - commitSummary: 'Trace lighthouse signal', - changeSummary: 'signal.ts', - patch: '+ Trace lighthouse signal' - }, - params, - target: contractTarget, - spawnAgent - }), - output: JSON.stringify({ - base: 'main', - title: 'Trace lighthouse signal.', - body: 'Preserve request bytes\n', - draft: true - }), - expected: { - fields: { - base: 'main', - title: 'Trace lighthouse signal', - body: 'Preserve request bytes', - draft: true - }, - branchChangedByPreparation: true - } - }, - { - label: 'branch slug / folder title', - run: (params, spawnAgent) => - generateBranchName({ - context: { firstPrompt: 'Trace lighthouse signal' }, - params, - target: contractTarget, - spawnAgent - }), - output: 'Trace Lighthouse Signal!', - expected: { slug: 'trace-lighthouse-signal' } - }, - { - label: 'conversation name', - run: (params, spawnAgent) => - generateConversationName({ - context: { firstPrompt: 'Trace lighthouse signal' }, - params, - target: contractTarget, - spawnAgent - }), - output: '# Title: "Trace lighthouse signal"\nextra commentary', - expected: { name: 'Trace lighthouse signal' } - }, - { - label: 'Jira summary', - run: (params, spawnAgent) => - generateJiraIssueSummaryFromContext( - { description: 'Trace lighthouse signal' }, - params, - contractTarget, - spawnAgent - ), - output: '"Trace lighthouse signal"\nextra commentary', - expected: { summary: 'Trace lighthouse signal' } - } -] - type Receipt = { paths: string[]; sizes: number[]; content: string; stdin: string; message: string } async function withReader( output: string, @@ -271,55 +165,6 @@ async function withReader( } } -it.each(consumers)( - 'preserves $label output and failure classification across stdin/file transport', - async (consumer) => { - const spec = agentSpecs.getCommitMessageAgentSpec('omp') - if (!spec) { - throw new Error('Missing OMP spec') - } - for (const fail of [false, true]) { - const delivered = await withReader(consumer.output, fail, (spawn) => - consumer.run(contractParams, spawn) - ) - expect(delivered.receipt.paths).toHaveLength(1) - expect(delivered.receipt.stdin).toBe('') - expect(delivered.receipt.message.endsWith('recipe-message')).toBe(true) - expect(delivered.receipt.content).toContain('Trace lighthouse signal') - expect(delivered.receipt.content.includes('Saved recipe instruction')).toBe( - consumer.label !== 'Jira summary' - ) - if (fail) { - expect(delivered.result).toMatchObject({ - success: false, - error: expect.stringContaining('OMP CLI command failed with code 7') - }) - } else { - expect(delivered.result).toMatchObject({ - success: true, - agentLabel: 'OMP', - ...consumer.expected - }) - } - const legacy = vi.spyOn(agentSpecs, 'getCommitMessageAgentSpec').mockReturnValue({ - ...spec, - promptDelivery: 'stdin', - buildArgs: (params) => spec.buildArgs(params).slice(0, -1) - }) - try { - const previous = await withReader(consumer.output, fail, (spawn) => - consumer.run(contractParams, spawn) - ) - expect(previous.receipt.paths).toHaveLength(0) - expect(previous.receipt.content).toBe(delivered.receipt.content) - expect(previous.result).toEqual(delivered.result) - } finally { - legacy.mockRestore() - } - } - } -) - it.each(['below', 'at', 'above', 'multibyte', 'BOM run', 'GIF', 'RIFF', 'internal magic'] as const)( 'delivers complete text recipe content: %s', async (boundary) => { @@ -363,76 +208,3 @@ it.each(['below', 'at', 'above', 'multibyte', 'BOM run', 'GIF', 'RIFF', 'interna } } ) - -it.each(['success', 'spawn failure', 'permanent failure'] as const)( - 'settles independently of bounded async cleanup after %s', - async (outcome) => { - const originalPlatform = process.platform - const rmMock = vi.mocked(rm) - const errors = vi.spyOn(console, 'error').mockImplementation(() => {}) - const locked = Object.assign(new Error('EPERM: prompt handle held'), { code: 'EPERM' }) - let directory = '' - Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' }) - rmMock.mockClear() - rmMock.mockImplementationOnce(async () => { - throw locked - }) - if (outcome === 'permanent failure') { - rmMock.mockRejectedValue(locked) - } - try { - const planned = planCommitMessageGeneration( - { agentId: 'omp', model: 'default' }, - recordedPrompt - ) - if (!planned.ok) { - throw new Error(planned.error) - } - const child = createMockDiscoveryChild() - const execution = runLocalSourceControlPlan({ - plan: planned.plan, - cwd: '/omp-cleanup', - env: undefined, - operation: 'conversation-name', - spawnAgent: ({ args }) => { - directory = dirname(args[planned.plan.promptFile?.argIndex ?? -1].slice(1)) - if (outcome === 'spawn failure') { - throw new Error('Fixture spawn failure') - } - return child - } - }) - if (outcome !== 'spawn failure') { - child.stdout.emit('data', Buffer.from('Lighthouse Signal Trace')) - child.emit('close', 0) - } - expect((await execution.result).success).toBe(outcome !== 'spawn failure') - await execution.processClosed - expect(rmMock).toHaveBeenCalledTimes(1) - expect(existsSync(directory)).toBe(true) - if (outcome === 'permanent failure') { - await vi.waitFor( - () => - expect(errors).toHaveBeenCalledWith( - '[commit-message] Failed to remove generation prompt:', - locked - ), - { timeout: 5000 } - ) - expect(rmMock).toHaveBeenCalledTimes(5) - expect(errors).toHaveBeenCalledTimes(1) - expect(existsSync(directory)).toBe(true) - } else { - await vi.waitFor(() => expect(existsSync(directory)).toBe(false), { timeout: 2000 }) - expect(rmMock).toHaveBeenCalledTimes(2) - } - } finally { - Object.defineProperty(process, 'platform', { configurable: true, value: originalPlatform }) - rmMock.mockImplementation(actualFs.rm) - if (directory) { - await actualFs.rm(directory, { recursive: true, force: true }) - } - errors.mockRestore() - } - } -) diff --git a/src/main/text-generation/prompts/source-control-wsl-prompt.test.ts b/src/main/text-generation/prompts/source-control-wsl-prompt.test.ts index 45134cdd5e5..67538be68c0 100644 --- a/src/main/text-generation/prompts/source-control-wsl-prompt.test.ts +++ b/src/main/text-generation/prompts/source-control-wsl-prompt.test.ts @@ -334,75 +334,6 @@ it.each([ } ) -it.each(['success', 'nonzero', 'empty', 'storage', 'pipe-close'] as const)( - 'runs the production attempt through real private shell preparation and %s', - async (outcome) => { - let directory = '' - let actualPipeError = false - const execution = withPlatform('win32', () => - runLocalSourceControlPlan({ - plan: plan(tricky.repeat(40)), - cwd, - env: undefined, - operation: 'conversation-name', - spawnAgent: (input) => { - const preparation = input.preparation - if (!preparation) { - throw new Error('Missing preparation') - } - directory = `/tmp/orca-generation-${preparation.args[0]}` - const argv = - outcome === 'pipe-close' - ? [ - '-c', - `exec 0<&-; printf 'ORCA_GENERATION_READY:%s\\n' "$1" >&2; sleep 0.1`, - 'fixture', - preparation.args[0] - ] - : [ - '-c', - preparation.script, - 'fixture', - ...preparation.args, - '/bin/sh', - '-c', - outcome === 'empty' - ? ':' - : `printf '%s' 'Useful name'; exit ${outcome === 'nonzero' ? 7 : 0}` - ] - const child = withPlatform('darwin', () => - spawnProcess({ program: '/bin/sh', args: argv, stdio: 'pipe' }) - ) - child.stdin.on('error', (error) => { - actualPipeError ||= 'code' in error && error.code === 'EPIPE' - }) - if (outcome === 'storage') { - child.stderr.once('data', () => mkdirSync(join(directory, 'prompt-1.txt'))) - } - return child - } - }) - ) - try { - const result = await execution.result - await execution.processClosed - expect(result.success).toBe(outcome === 'success') - expect(removeTreeMock).toHaveBeenCalledTimes(1) - if (outcome === 'pipe-close') { - expect(actualPipeError).toBe(true) - } - if (outcome === 'success') { - const file = readFileSync(join(directory, 'prompt-1.txt')) - expect(new TextDecoder().decode(file)).toBe(Buffer.from(tricky.repeat(40)).toString()) - } - } finally { - if (directory) { - await rm(directory, { recursive: true, force: true }) - } - } - } -) - it('delivers a plain UTF-8 policy module and preserves the stdin task after guest materialization', async () => { const guest = prepareWslPrompt( { diff --git a/src/main/workspace-hooks/issue-command-wsl-stopped-distro.test.ts b/src/main/workspace-hooks/issue-command-wsl-stopped-distro.test.ts index 25732ca452c..855ae0924ae 100644 --- a/src/main/workspace-hooks/issue-command-wsl-stopped-distro.test.ts +++ b/src/main/workspace-hooks/issue-command-wsl-stopped-distro.test.ts @@ -32,7 +32,6 @@ import { readIssueCommandUnlessWslStopped, writeIssueCommandUnlessWslStopped } from './issue-command-file' -import { RuntimeRepositoryIssueCommand } from '../runtime/repositories/runtime-repository-issue-command' const WSL_REPO = '\\\\wsl.localhost\\Ubuntu\\home\\me\\repo' const HOST_REPO = 'C:\\src\\repo' @@ -67,24 +66,6 @@ describe('issue command reads for a stopped WSL distro (E9)', () => { expect(mocks.loadHooks).not.toHaveBeenCalled() }) - it("gates the runtime read a paired client's Settings pane makes, and passes a launch's wake", async () => { - const reader = new RuntimeRepositoryIssueCommand({ - resolveRepo: async () => ({ - id: 'repo-1', - path: WSL_REPO, - displayName: 'repo', - badgeColor: '#000', - addedAt: 1, - kind: 'git' - }), - getLocalGitArgs: () => [] - }) - await expect(reader.read('id:repo-1')).resolves.toMatchObject({ status: 'error' }) - expect(mocks.loadHooks).not.toHaveBeenCalled() - await reader.read('id:repo-1', { wakeWslDistro: true }) - expect(mocks.loadHooks).toHaveBeenCalledWith(WSL_REPO) - }) - it("refuses a write the user didn't make, leaving the hidden file and the distro alone", async () => { await expect(writeIssueCommandUnlessWslStopped(WSL_REPO, 'x', {})).rejects.toThrow( "WSL distro 'Ubuntu' is not running" @@ -94,15 +75,6 @@ describe('issue command reads for a stopped WSL distro (E9)', () => { expect(mocks.rmSync).not.toHaveBeenCalled() }) - it("saves the user's own edit, a user act that may start the distro", async () => { - await writeIssueCommandUnlessWslStopped(WSL_REPO, 'mine', {}, { wakeWslDistro: true }) - expect(mocks.writeFileSync).toHaveBeenCalledWith( - expect.stringContaining('issue-command'), - 'mine\n', - 'utf-8' - ) - }) - it('refuses a host-path repo whose Git runs in the stopped distro', async () => { await expect( writeIssueCommandUnlessWslStopped(HOST_REPO, 'x', () => ({ wslDistro: 'Ubuntu' })) From 7e10540b93585b413e263a27cccfbaa6f069c823 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 13 Nov 2026 02:06:33 -0800 Subject: [PATCH 066/178] refactor: group preload modules by domain (#41940) * refactor: group preload modules by domain * fix: include preload entry dependencies in web typecheck --- config/main-blocking-ipc-baseline.txt | 2 +- config/reliability-gates.jsonc | 22 +- .../scripts/ci/sharding/ci-shard-timings.json | 45 ++-- config/tsconfig.tc.web.json | 26 +- ...t-page-renderer-lifecycle.electron.test.ts | 7 +- .../browser-extension-api-host.test.ts | 78 +----- ...tension-content-script-persistence.test.ts | 194 -------------- .../browser-extension-api-namespaces.test.ts | 137 +--------- .../api/declarative-net-request.test.ts | 139 +--------- .../browser/extensions/api/storage-relay.ts | 2 +- .../browser/extensions/api/storage-sync.ts | 2 +- .../extensions/api/web-navigation.test.ts | 2 +- .../register-browser-extension-namespaces.ts | 2 +- .../browser-extension-discarded-tabs.test.ts | 27 +- src/preload/api-types.ts | 62 ----- src/preload/api/app-bridge.ts | 7 +- src/preload/api/automations-bridge.ts | 3 +- ...bridge-guest-registration-and-downloads.ts | 2 +- src/preload/api/claude-usage-bridge.ts | 4 +- src/preload/api/codex-usage-bridge.ts | 4 +- src/preload/api/crash-reports-bridge.ts | 4 +- src/preload/api/e2e-bridge.ts | 2 +- src/preload/{ => api}/gitlab.ts | 4 +- src/preload/api/gl-bridge.ts | 4 - src/preload/api/muse-usage-bridge.ts | 4 +- src/preload/api/native-chat-bridge.ts | 6 +- src/preload/api/open-code-usage-bridge.ts | 4 +- src/preload/api/orca-profiles-bridge.ts | 4 +- src/preload/api/platform-bridge.test.ts | 48 ---- src/preload/api/platform-bridge.ts | 2 +- src/preload/api/plugins-bridge.ts | 8 +- src/preload/api/preflight-bridge.ts | 3 +- src/preload/api/runtime-bridge.ts | 2 +- .../api/runtime-environments-bridge.ts | 2 +- .../ssh-authority-forwarding.test.ts | 38 +-- .../api/ui-bridge-tab-and-browser-commands.ts | 4 +- src/preload/api/updater-bridge.ts | 4 +- .../app-restart-checkpoint-routing.test.ts | 239 ------------------ .../browser-extension-content-script-api.ts | 6 +- ...browser-extension-native-messaging.test.ts | 159 ------------ src/preload/browser-extension-preload.ts | 22 +- .../browser-extension-storage-sync.test.ts | 209 --------------- ...rowser-extension-web-store-wording.test.ts | 48 ---- src/preload/browser-extension-worker-shim.ts | 18 +- src/preload/browser-guest.ts | 8 +- src/preload/browser-passkey-request.ts | 2 +- .../browser-window-close-installation.test.ts | 62 ----- .../api}/browser-extension-api-surface.ts | 0 .../api}/browser-extension-event-filters.ts | 0 .../api}/browser-extension-frame-lifetime.ts | 0 .../api}/browser-extension-inert-apis.test.ts | 6 - .../api}/browser-extension-inert-apis.ts | 0 .../api}/browser-extension-main-world.test.ts | 0 .../api}/browser-extension-main-world.ts | 0 ...browser-extension-document-targets.test.ts | 102 -------- .../browser-extension-document-targets.ts | 2 +- ...ser-extension-content-script-relay.test.ts | 2 +- .../browser-extension-content-script-relay.ts | 4 +- .../browser-extension-native-messaging.ts | 2 +- .../browser-extension-storage-relay.test.ts | 65 +---- .../browser-extension-storage-relay.ts | 2 +- .../browser-extension-storage-sync.test.ts | 100 ++++++++ .../browser-extension-storage-sync.ts | 2 +- .../browser-extension-web-store-bridge.ts | 0 ...ser-extension-web-store-main-world.test.ts | 6 - .../browser-extension-web-store-main-world.ts | 0 .../browser-extension-web-store-wording.ts | 0 .../browser-passkey-request-watch.test.ts | 0 .../browser-passkey-request-watch.ts | 0 .../browser-password-detector.test.ts | 62 +---- .../passwords}/browser-password-detector.ts | 2 +- .../browser-password-element-tokens.ts | 0 .../passwords}/browser-password-field-fill.ts | 2 +- .../browser-password-field-rules.ts | 0 .../browser-password-focus-tracker.ts | 2 +- .../browser-password-form-parser.test.ts | 53 ---- .../browser-password-form-parser.ts | 2 +- .../passwords}/browser-password-form-scan.ts | 2 +- .../browser-password-guest-channels.ts | 2 +- .../passwords}/browser-password-guest-port.ts | 0 .../browser-password-submit-tracker.ts | 2 +- .../browser-window-close-installation.test.ts | 22 ++ .../browser-window-close-installation.ts | 0 .../browser-window-prompt-installation.ts | 0 src/preload/doc-preview-link.ts | 4 +- .../doc-preview-link-interception.ts | 2 +- src/preload/gitlab-work-item-details.test.ts | 18 -- src/preload/index.ts | 8 +- .../native-chat-visual-guest-theme.ts | 2 +- src/preload/opencode-go-credentials.test.ts | 23 -- src/preload/plugin-frame-guest.ts | 4 +- .../plugin-frame-guest-render-lane.test.ts | 0 .../frames}/plugin-frame-guest-send-budget.ts | 0 .../frames}/plugin-frame-guest-transport.ts | 0 .../frames}/plugin-frame-guest.test.ts | 64 +---- src/preload/renderer-restart-wiring.test.ts | 163 ------------ ...wser-client-page-renderer-requests.test.ts | 49 +--- .../browser-client-page-renderer-requests.ts | 2 +- .../browser-find-subscriptions.test.ts | 30 --- .../browser-find-subscriptions.ts | 4 +- .../renderer-heap-statistics-reader.test.ts | 0 .../renderer-heap-statistics-reader.ts | 2 +- .../renderer-process-memory-reader.test.ts | 0 .../renderer-process-memory-reader.ts | 2 +- src/preload/{ => renderer}/e2e-config.ts | 2 +- .../hidden-window-frame-pump.ts | 2 +- .../renderer-runtime.ts} | 10 +- .../app-restart-checkpoint-routing.test.ts | 96 +++++++ .../restart/renderer-restart-wiring.test.ts | 69 +++++ .../restart}/renderer-restart-wiring.ts | 8 +- ...close-active-tab-payload-admission.test.ts | 0 .../close-active-tab-payload-admission.ts | 2 +- .../runtime-environment-subscriptions.ts | 4 +- src/preload/updater-package-recovery.test.ts | 83 ------ src/preload/usage-provider-api.test.ts | 33 --- .../{ => usage}/backend-port-client.ts | 4 +- src/preload/{ => usage}/usage-provider-api.ts | 4 +- .../plugins/catalog/PluginCatalogGroups.tsx | 2 +- .../PluginCatalogHighlights.fixture.tsx | 2 +- .../catalog/plugin-catalog-highlights.test.ts | 2 +- .../plugins/dialogs/PluginInstallDialog.tsx | 2 +- .../dialogs/PluginMarketplaceSourceDialog.tsx | 2 +- .../plugins/dialogs/PluginRemoveDialog.tsx | 2 +- .../plugins/dialogs/PluginRollbackDialog.tsx | 2 +- .../dialogs/PluginUpdateAllDialog.test.tsx | 194 +------------- .../plugins/dialogs/PluginUpdateAllDialog.tsx | 2 +- .../plugins/list/PluginFilterPopover.tsx | 2 +- .../plugins/list/PluginListToolbar.tsx | 2 +- .../plugins/list/PluginManager.test.tsx | 217 ---------------- .../components/plugins/list/PluginManager.tsx | 2 +- .../plugins/list/PluginManagerRows.tsx | 2 +- .../plugins/list/PluginSettingsOverview.tsx | 2 +- .../plugins/list/plugin-list-entries.test.ts | 4 +- .../plugins/list/plugin-list-entries.ts | 4 +- .../plugins/list/plugin-public-catalog.ts | 2 +- .../plugins/page/PluginAddsSection.test.tsx | 2 +- .../plugins/page/PluginAddsSection.tsx | 6 +- .../page/PluginContributionActionEvents.tsx | 2 +- .../page/PluginContributionActionPanel.tsx | 2 +- .../plugins/page/PluginDetailPage.fixture.tsx | 2 +- .../plugins/page/PluginDetailPage.test.tsx | 2 +- ...plugin-contribution-action-availability.ts | 2 +- .../presentation/plugin-access-profile.ts | 6 +- .../presentation/plugin-install-trust.ts | 2 +- .../plugin-status-presentation.ts | 2 +- .../plugins/review/PluginInstallConsent.tsx | 2 +- .../plugins/review/PluginUpdateCandidate.tsx | 2 +- .../plugins/review/PluginUpdateReview.tsx | 2 +- .../review/PluginVmRecipeConsentPreview.tsx | 2 +- .../plugins/state/plugin-preview-cache.ts | 2 +- .../plugins/state/use-plugin-art-palettes.ts | 2 +- .../plugins/state/use-plugin-catalog.ts | 2 +- .../state/use-plugin-contribution-source.ts | 2 +- .../state/use-plugin-hover-prefetch.ts | 2 +- .../state/use-plugin-install-action.test.tsx | 148 ----------- .../state/use-plugin-install-action.ts | 2 +- .../state/use-plugin-install-link.test.tsx | 2 +- .../plugins/state/use-plugin-install-link.ts | 2 +- .../plugins/state/use-plugin-logs.ts | 3 +- .../state/use-plugin-marketplace-lifecycle.ts | 2 +- .../plugins/state/use-plugin-page-model.ts | 2 +- .../state/use-plugin-page-navigation.test.tsx | 146 ----------- .../state/use-plugin-review-queue.test.tsx | 2 +- .../plugins/state/use-plugin-review-queue.ts | 2 +- .../shared/MacFolderAccessFixDialog.tsx | 2 +- .../shared/mac-folder-access-folder-name.ts | 4 +- .../sidebar/SeveredTerminalDaemonBanner.tsx | 2 +- .../task-source-provider-availability.ts | 2 +- .../linear-linked-issue-refresh.test.ts | 58 +---- ...untime-client-ipc-bridge-ownership.test.ts | 56 +--- .../useMacTccAttributionSeveredNotice.ts | 2 +- .../slices/usage/usage-backend-port.test.ts | 10 +- .../preload-api/web-host-capability-api.ts | 7 +- .../web/preload-api/web-native-chat-api.ts | 5 +- .../src/web/web-runtime-crew-follow.test.tsx | 185 -------------- .../src/web/web-runtime-crew.test-fixture.ts | 137 ---------- 176 files changed, 538 insertions(+), 3557 deletions(-) delete mode 100644 src/main/browser/extensions/api-runtime/browser-extension-content-script-persistence.test.ts rename src/preload/{ => api}/gitlab.ts (96%) delete mode 100644 src/preload/api/gl-bridge.ts delete mode 100644 src/preload/api/platform-bridge.test.ts rename src/preload/{ => api}/ssh-authority-forwarding.test.ts (79%) delete mode 100644 src/preload/app-restart-checkpoint-routing.test.ts delete mode 100644 src/preload/browser-extension-native-messaging.test.ts delete mode 100644 src/preload/browser-extension-storage-sync.test.ts delete mode 100644 src/preload/browser-extension-web-store-wording.test.ts delete mode 100644 src/preload/browser-window-close-installation.test.ts rename src/preload/{ => browser/extensions/api}/browser-extension-api-surface.ts (100%) rename src/preload/{ => browser/extensions/api}/browser-extension-event-filters.ts (100%) rename src/preload/{ => browser/extensions/api}/browser-extension-frame-lifetime.ts (100%) rename src/preload/{ => browser/extensions/api}/browser-extension-inert-apis.test.ts (90%) rename src/preload/{ => browser/extensions/api}/browser-extension-inert-apis.ts (100%) rename src/preload/{ => browser/extensions/api}/browser-extension-main-world.test.ts (100%) rename src/preload/{ => browser/extensions/api}/browser-extension-main-world.ts (100%) rename src/preload/{ => browser/extensions/documents}/browser-extension-document-targets.test.ts (67%) rename src/preload/{ => browser/extensions/documents}/browser-extension-document-targets.ts (99%) rename src/preload/{ => browser/extensions/messaging}/browser-extension-content-script-relay.test.ts (98%) rename src/preload/{ => browser/extensions/messaging}/browser-extension-content-script-relay.ts (98%) rename src/preload/{ => browser/extensions/messaging}/browser-extension-native-messaging.ts (98%) rename src/preload/{ => browser/extensions/storage}/browser-extension-storage-relay.test.ts (75%) rename src/preload/{ => browser/extensions/storage}/browser-extension-storage-relay.ts (98%) create mode 100644 src/preload/browser/extensions/storage/browser-extension-storage-sync.test.ts rename src/preload/{ => browser/extensions/storage}/browser-extension-storage-sync.ts (99%) rename src/preload/{ => browser/extensions/web-store}/browser-extension-web-store-bridge.ts (100%) rename src/preload/{ => browser/extensions/web-store}/browser-extension-web-store-main-world.test.ts (94%) rename src/preload/{ => browser/extensions/web-store}/browser-extension-web-store-main-world.ts (100%) rename src/preload/{ => browser/extensions/web-store}/browser-extension-web-store-wording.ts (100%) rename src/preload/{ => browser/passkeys}/browser-passkey-request-watch.test.ts (100%) rename src/preload/{ => browser/passkeys}/browser-passkey-request-watch.ts (100%) rename src/preload/{ => browser/passwords}/browser-password-detector.test.ts (90%) rename src/preload/{ => browser/passwords}/browser-password-detector.ts (99%) rename src/preload/{ => browser/passwords}/browser-password-element-tokens.ts (100%) rename src/preload/{ => browser/passwords}/browser-password-field-fill.ts (99%) rename src/preload/{ => browser/passwords}/browser-password-field-rules.ts (100%) rename src/preload/{ => browser/passwords}/browser-password-focus-tracker.ts (98%) rename src/preload/{ => browser/passwords}/browser-password-form-parser.test.ts (80%) rename src/preload/{ => browser/passwords}/browser-password-form-parser.ts (99%) rename src/preload/{ => browser/passwords}/browser-password-form-scan.ts (99%) rename src/preload/{ => browser/passwords}/browser-password-guest-channels.ts (95%) rename src/preload/{ => browser/passwords}/browser-password-guest-port.ts (100%) rename src/preload/{ => browser/passwords}/browser-password-submit-tracker.ts (99%) create mode 100644 src/preload/browser/window-controls/browser-window-close-installation.test.ts rename src/preload/{ => browser/window-controls}/browser-window-close-installation.ts (100%) rename src/preload/{ => browser/window-controls}/browser-window-prompt-installation.ts (100%) rename src/preload/{ => document-preview}/doc-preview-link-interception.ts (97%) delete mode 100644 src/preload/gitlab-work-item-details.test.ts rename src/preload/{ => native-chat}/native-chat-visual-guest-theme.ts (95%) delete mode 100644 src/preload/opencode-go-credentials.test.ts rename src/preload/{ => plugins/frames}/plugin-frame-guest-render-lane.test.ts (100%) rename src/preload/{ => plugins/frames}/plugin-frame-guest-send-budget.ts (100%) rename src/preload/{ => plugins/frames}/plugin-frame-guest-transport.ts (100%) rename src/preload/{ => plugins/frames}/plugin-frame-guest.test.ts (60%) delete mode 100644 src/preload/renderer-restart-wiring.test.ts rename src/preload/{ => renderer/browser-pages}/browser-client-page-renderer-requests.test.ts (81%) rename src/preload/{ => renderer/browser-pages}/browser-client-page-renderer-requests.ts (99%) rename src/preload/{ => renderer/browser-pages}/browser-find-subscriptions.test.ts (70%) rename src/preload/{ => renderer/browser-pages}/browser-find-subscriptions.ts (94%) rename src/preload/{ => renderer/diagnostics}/renderer-heap-statistics-reader.test.ts (100%) rename src/preload/{ => renderer/diagnostics}/renderer-heap-statistics-reader.ts (91%) rename src/preload/{ => renderer/diagnostics}/renderer-process-memory-reader.test.ts (100%) rename src/preload/{ => renderer/diagnostics}/renderer-process-memory-reader.ts (89%) rename src/preload/{ => renderer}/e2e-config.ts (93%) rename src/preload/{ => renderer}/hidden-window-frame-pump.ts (97%) rename src/preload/{preload-runtime-support.ts => renderer/renderer-runtime.ts} (82%) create mode 100644 src/preload/renderer/restart/app-restart-checkpoint-routing.test.ts create mode 100644 src/preload/renderer/restart/renderer-restart-wiring.test.ts rename src/preload/{ => renderer/restart}/renderer-restart-wiring.ts (92%) rename src/preload/{ => renderer/window-commands}/close-active-tab-payload-admission.test.ts (100%) rename src/preload/{ => renderer/window-commands}/close-active-tab-payload-admission.ts (90%) rename src/preload/{ => runtime}/runtime-environment-subscriptions.ts (98%) delete mode 100644 src/preload/updater-package-recovery.test.ts delete mode 100644 src/preload/usage-provider-api.test.ts rename src/preload/{ => usage}/backend-port-client.ts (97%) rename src/preload/{ => usage}/usage-provider-api.ts (95%) delete mode 100644 src/renderer/src/components/plugins/list/PluginManager.test.tsx delete mode 100644 src/renderer/src/components/plugins/state/use-plugin-install-action.test.tsx delete mode 100644 src/renderer/src/components/plugins/state/use-plugin-page-navigation.test.tsx delete mode 100644 src/renderer/src/web/web-runtime-crew-follow.test.tsx delete mode 100644 src/renderer/src/web/web-runtime-crew.test-fixture.ts diff --git a/config/main-blocking-ipc-baseline.txt b/config/main-blocking-ipc-baseline.txt index 5821c30c503..dac3b7e5930 100644 --- a/config/main-blocking-ipc-baseline.txt +++ b/config/main-blocking-ipc-baseline.txt @@ -6,4 +6,4 @@ 1 src/preload/api/app-bridge.ts 1 src/preload/api/native-chat-bridge.ts 1 src/preload/api/settings-bridge.ts -1 src/preload/native-chat-visual-guest-theme.ts +1 src/preload/native-chat/native-chat-visual-guest-theme.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index d7e19757b11..c361edcc424 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -9350,7 +9350,7 @@ "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-transport/ipc/pty-transport-detach-attach-handoff.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/remote-runtime-pty-transport-stream-reconnect.test.ts src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/remote-runtime-pty-transport-expired-pane-recovery.test.ts src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/recovery/remote-runtime-pty-recovery-state.test.ts src/shared/remote-runtime/remote-runtime-socket-liveness.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-socket-generation.test.ts src/shared/remote-runtime/remote-runtime-client-error-classification.test.ts src/main/runtime/terminal/launch/orca-runtime-terminal-create-idempotency.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/store/slices/ui/workspace-navigation-history-hydration.test.ts src/renderer/src/store/repos/repos-ssh-host-reconciliation.test.ts --reporter=dot", - "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/ssh/ssh.test.ts src/main/ssh/connection/ssh-connection-manager.test.ts src/main/ssh/connection/ssh-connection.test.ts src/preload/ssh-authority-forwarding.test.ts src/renderer/src/runtime/rpc/subscriptions/runtime-client-events.test.ts src/renderer/src/runtime/hosts/ssh/runtime-environment-ssh-state.test.ts src/shared/ssh-retained-payload-admission.test.ts --reporter=dot", + "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/ssh/ssh.test.ts src/main/ssh/connection/ssh-connection-manager.test.ts src/main/ssh/connection/ssh-connection.test.ts src/preload/api/ssh-authority-forwarding.test.ts src/renderer/src/runtime/rpc/subscriptions/runtime-client-events.test.ts src/renderer/src/runtime/hosts/ssh/runtime-environment-ssh-state.test.ts src/shared/ssh-retained-payload-admission.test.ts --reporter=dot", "pnpm exec electron-vite build --mode e2e", "pnpm run build:web-from-renderer", "SKIP_BUILD=1 pnpm exec playwright test tests/e2e/paired-remote-terminal-materialization-reconnect.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1" @@ -9376,7 +9376,7 @@ "src/main/ipc/ssh/ssh.test.ts", "src/main/ssh/connection/ssh-connection-manager.test.ts", "src/main/ssh/connection/ssh-connection.test.ts", - "src/preload/ssh-authority-forwarding.test.ts", + "src/preload/api/ssh-authority-forwarding.test.ts", "src/renderer/src/runtime/rpc/subscriptions/runtime-client-events.test.ts", "src/renderer/src/runtime/hosts/ssh/runtime-environment-ssh-state.test.ts", "src/shared/ssh-retained-payload-admission.test.ts" @@ -9428,7 +9428,7 @@ ] }, { - "file": "src/preload/ssh-authority-forwarding.test.ts", + "file": "src/preload/api/ssh-authority-forwarding.test.ts", "assertions": [ "full authority crosses Electron IPC without loss", "partial authority becomes unknown for bounded reconciliation and malformed full authority is dropped", @@ -9779,7 +9779,7 @@ "providers": ["local", "daemon", "ssh", "remote-runtime"], "coveredPlatforms": ["macos", "linux"], "coveredProviders": [], - "coverageNotes": "Audit3699 removes the complete restored app-owned-copy xterm bypass predicate suite (3 authored/3 finite). Intentionally lose Linux/Windows Ctrl+Shift+C keydown/keyup and logical-over-physical-key pass-through, selection/Kitty-disabled/defaultPrevented refusal, and macOS Cmd+C pass-through/selection/Kitty-disabled policy alarms. No actual browser dispatch, clipboard operation, negotiated application bytes, native key delivery or PTY result was established by this driver. No equivalent replacement or fresh execution claimed. Preserve all prior dated receipts and historical accounts. Previous account (preserved verbatim): Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. The complete mocked AgentTerminalPreview component suite is removed, including the snapshot-proven kitty mirror forwarding alarm. Remove its active file, command token and assertion reference; the other IME drivers are unchanged and do not replace the removed component checks. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit1709 removes low-value test definitions and retires affected assertion or execution-receipt claims without equivalent replacement coverage. Per-source reviews bound retained behavior and injected/native/remote limits. Unchanged assertions and receipts keep their original scope; gates lacking current active evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1619 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally reduces src/renderer/src/components/terminal-pane/terminal-ime-candidate-key-release-guard.test.ts: Loses direct timestamp-formula assertion; no useful independent alarm in removed registration. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1361 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audit1342 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audit1320 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audit1301 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): This audit removes the browser native-keyboard fallback inventory case. Its empty-chord, absent-layout and no-op-subscription alarms are lost; retained native input evidence is not an equivalent replacement. Historical commands and results remain historical. Local macOS and containerized Linux evidence, deterministic renderer-unit coverage for the Linux/Sogou candidate-key policy including the legacy orphaned-keyup fallback, xterm preedit visibility and row-occlusion behavior, Electron/CDP preedit geometry, Electron/CDP live-PTY Sogou-style repros, and macOS renderer/main slices for Preview kitty commits and layout-native Option input. The Option slice covers complete CSI-u flag fields including lock state, repeat/release event types, authoritative committed associated text, native dead-key release suppression, functional-versus-printable numpad codes, press-pinned release identity, live keyup modifiers, pending-layout text fallback, atomic layout refresh, rapid in-focus input-source changes, stable native layout characters, and both native/web preload fallbacks. The standalone remote SnapshotStart metadata suite is removed. Its proven-zero, nonzero, absent-field and malformed-value propagation alarms are intentionally lost; retained daemon and preview consumer contracts do not establish equivalent remote projection coverage. Historical commands and results remain historical. Real Linux/Sogou OS IME automation, native Windows candidate-window placement, Windows ConPTY post-agent reset, and the CJK/Vietnamese/Arabic matrix remain registered gaps. The audit removes isolated candidate-anchor width, last-column, timer-ordering and detached-textarea examples. The retained deterministic fixtures do not observe a native candidate window or establish equivalent geometry coverage; separate Electron geometry evidence remains historical. The test-value audit removes the empty end-of-row/no-tail composition baseline from terminal-korean-midline-preedit-occlusion.spec.ts. The placeholder masking and measured row-tail occlusion registrations remain; they are not equivalent replacement coverage for the removed empty-tail alarm. No new execution or native IME evidence is claimed. Audit1301: src/renderer/src/components/terminal-pane/terminal-shortcut-option-compose.test.ts reduced. Dead-key/IME variants, Unicode/language inventories, numpad/CapsLock cases and other protocol samples lose their individual alarms. No equivalent replacement coverage is claimed. Audit1320: src/renderer/src/lib/keyboard-layout/layout-base-character.test.ts reduced. Last-complete snapshot while layout-map/native-snapshot partial refresh settles loses its individual implementation-cache alarm. No equivalent replacement coverage is claimed. Audit1342: src/renderer/src/components/terminal-pane/terminal-ime-macos-keybinding-dict-trace.test.ts reduced. Second Korean-layout replay, ordinary Korean/ASCII no-remap baselines and weak Option nonempty-byte alarm are dropped. No equivalent replacement coverage is claimed. Audit1361: src/renderer/src/components/dashboard-popout/preview-terminal-snapshot-replay.test.ts removed. Snapshot/live-proven kitty flags, replacement choice and replay idempotence in the assembled mirror. No equivalent replacement coverage is claimed. Audit1365: focused Linux unit selection passed on source head 8880357cb420c45f1b808ae545a5bc609324624d. This new result has its own exact command; historical results keep their prior scope. Intentional test losses remain recorded in knownGaps. No physical-provider, native input, rendered E2E, p95, soak or replacement-coverage claim. Audit1391: src/renderer/src/lib/pane-manager/terminal-ime-candidate-anchor.test.ts reduced. Lose explicit one-read/unchanged-style work budget across 30 syllables and single-timer burst alarm. Synthetic screen bounds/terminal cursor bound retained proof to DOM anchor updates, not actual OS IME placement. src/renderer/src/components/terminal-pane/terminal-kitty-csi-u-encoding.test.ts reduced. Lose explicit PC-101 alternate omission, multi-codepoint/control associated text filtering, lock modifier integer, shifted punctuation, standalone separator and printable nonfunctional keypad fallbacks. Retained pure encoding is a wire-contract check without host input delivery. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1396: src/renderer/src/components/terminal-pane/xterm-bypass-policy.test.ts reduced. Lose standalone handled Ctrl, Shift+Space, shifted non-ASCII keydown and active-composition backspace/arrow/text/Process suppression alarms. No actual IME/PTY encoding occurs. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1402: src/renderer/src/lib/keyboard-layout/option-as-alt-probe.test.ts removed. Intentionally loses invalidation, focus/manual refresh gating and stale/newest generation settlement outcomes, including direct stale category suppression. No equivalent replacement or default preload input-source delivery claim. src/renderer/src/components/terminal-pane/terminal-ime-substituted-text-commit.test.ts removed. Intentionally loses substituted sentence punctuation, shifted/backslash/multi-unit/full-width/letter input and Kitty flag/release/report encoding alarms across14 authored registrations. No equivalent replacement or real pane/browser input delivery claim. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Preload layout cleanup retires the complete mocked macOS keyboard bridge registration. Its API-read forwarding, two-phase payload forwarding and exact listener-disposer alarms are intentionally lost without equivalent replacement. The remaining restart/checkpoint registrations do not cover this IME contract. Unaffected dated receipts retain their original commands and scope; this metadata change claims no new execution. Previous account (preserved verbatim): Audit3699 removes the complete restored app-owned-copy xterm bypass predicate suite (3 authored/3 finite). Intentionally lose Linux/Windows Ctrl+Shift+C keydown/keyup and logical-over-physical-key pass-through, selection/Kitty-disabled/defaultPrevented refusal, and macOS Cmd+C pass-through/selection/Kitty-disabled policy alarms. No actual browser dispatch, clipboard operation, negotiated application bytes, native key delivery or PTY result was established by this driver. No equivalent replacement or fresh execution claimed. Preserve all prior dated receipts and historical accounts. Previous account (preserved verbatim): Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. The complete mocked AgentTerminalPreview component suite is removed, including the snapshot-proven kitty mirror forwarding alarm. Remove its active file, command token and assertion reference; the other IME drivers are unchanged and do not replace the removed component checks. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit1709 removes low-value test definitions and retires affected assertion or execution-receipt claims without equivalent replacement coverage. Per-source reviews bound retained behavior and injected/native/remote limits. Unchanged assertions and receipts keep their original scope; gates lacking current active evidence are downgraded. Historical scope before this audit: Audit1640 removes low-value IPC, SSH, workspace and transcript-watcher tests without equivalent replacement coverage. Per-source reviews record retained public refusal, cancellation, execution-host and real-filesystem outcomes with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1623 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Audit1619 removes low-value mocked daemon projections, scalar tables and duplicate terminal examples without equivalent replacement coverage. Per-source reviews record retained terminal content preservation, process ownership refusal and recovery boundaries together with injected/native/remote limits. Affected historical receipts are retired; gates without active current evidence are downgraded. Historical scope before this audit: Retained exact unchanged-source receipts remain the only current execution evidence. Reduced selectors have no fresh execution result. Audit1417 intentionally reduces src/renderer/src/components/terminal-pane/terminal-ime-candidate-key-release-guard.test.ts: Loses direct timestamp-formula assertion; no useful independent alarm in removed registration. No equivalent replacement coverage or fresh result is claimed. Historical coverage account: Audit1361 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audit1342 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audit1320 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audit1301 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): This audit removes the browser native-keyboard fallback inventory case. Its empty-chord, absent-layout and no-op-subscription alarms are lost; retained native input evidence is not an equivalent replacement. Historical commands and results remain historical. Local macOS and containerized Linux evidence, deterministic renderer-unit coverage for the Linux/Sogou candidate-key policy including the legacy orphaned-keyup fallback, xterm preedit visibility and row-occlusion behavior, Electron/CDP preedit geometry, Electron/CDP live-PTY Sogou-style repros, and macOS renderer/main slices for Preview kitty commits and layout-native Option input. The Option slice covers complete CSI-u flag fields including lock state, repeat/release event types, authoritative committed associated text, native dead-key release suppression, functional-versus-printable numpad codes, press-pinned release identity, live keyup modifiers, pending-layout text fallback, atomic layout refresh, rapid in-focus input-source changes, stable native layout characters, and both native/web preload fallbacks. The standalone remote SnapshotStart metadata suite is removed. Its proven-zero, nonzero, absent-field and malformed-value propagation alarms are intentionally lost; retained daemon and preview consumer contracts do not establish equivalent remote projection coverage. Historical commands and results remain historical. Real Linux/Sogou OS IME automation, native Windows candidate-window placement, Windows ConPTY post-agent reset, and the CJK/Vietnamese/Arabic matrix remain registered gaps. The audit removes isolated candidate-anchor width, last-column, timer-ordering and detached-textarea examples. The retained deterministic fixtures do not observe a native candidate window or establish equivalent geometry coverage; separate Electron geometry evidence remains historical. The test-value audit removes the empty end-of-row/no-tail composition baseline from terminal-korean-midline-preedit-occlusion.spec.ts. The placeholder masking and measured row-tail occlusion registrations remain; they are not equivalent replacement coverage for the removed empty-tail alarm. No new execution or native IME evidence is claimed. Audit1301: src/renderer/src/components/terminal-pane/terminal-shortcut-option-compose.test.ts reduced. Dead-key/IME variants, Unicode/language inventories, numpad/CapsLock cases and other protocol samples lose their individual alarms. No equivalent replacement coverage is claimed. Audit1320: src/renderer/src/lib/keyboard-layout/layout-base-character.test.ts reduced. Last-complete snapshot while layout-map/native-snapshot partial refresh settles loses its individual implementation-cache alarm. No equivalent replacement coverage is claimed. Audit1342: src/renderer/src/components/terminal-pane/terminal-ime-macos-keybinding-dict-trace.test.ts reduced. Second Korean-layout replay, ordinary Korean/ASCII no-remap baselines and weak Option nonempty-byte alarm are dropped. No equivalent replacement coverage is claimed. Audit1361: src/renderer/src/components/dashboard-popout/preview-terminal-snapshot-replay.test.ts removed. Snapshot/live-proven kitty flags, replacement choice and replay idempotence in the assembled mirror. No equivalent replacement coverage is claimed. Audit1365: focused Linux unit selection passed on source head 8880357cb420c45f1b808ae545a5bc609324624d. This new result has its own exact command; historical results keep their prior scope. Intentional test losses remain recorded in knownGaps. No physical-provider, native input, rendered E2E, p95, soak or replacement-coverage claim. Audit1391: src/renderer/src/lib/pane-manager/terminal-ime-candidate-anchor.test.ts reduced. Lose explicit one-read/unchanged-style work budget across 30 syllables and single-timer burst alarm. Synthetic screen bounds/terminal cursor bound retained proof to DOM anchor updates, not actual OS IME placement. src/renderer/src/components/terminal-pane/terminal-kitty-csi-u-encoding.test.ts reduced. Lose explicit PC-101 alternate omission, multi-codepoint/control associated text filtering, lock modifier integer, shifted punctuation, standalone separator and printable nonfunctional keypad fallbacks. Retained pure encoding is a wire-contract check without host input delivery. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1396: src/renderer/src/components/terminal-pane/xterm-bypass-policy.test.ts reduced. Lose standalone handled Ctrl, Shift+Space, shifted non-ASCII keydown and active-composition backspace/arrow/text/Process suppression alarms. No actual IME/PTY encoding occurs. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result. Audit1402: src/renderer/src/lib/keyboard-layout/option-as-alt-probe.test.ts removed. Intentionally loses invalidation, focus/manual refresh gating and stale/newest generation settlement outcomes, including direct stale category suppression. No equivalent replacement or default preload input-source delivery claim. src/renderer/src/components/terminal-pane/terminal-ime-substituted-text-commit.test.ts removed. Intentionally loses substituted sentence punctuation, shifted/backslash/multi-unit/full-width/letter input and Kitty flag/release/report encoding alarms across14 authored registrations. No equivalent replacement or real pane/browser input delivery claim. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://github.com/stablyai/orca/pull/6699", "https://github.com/stablyai/orca/pull/6682", @@ -9796,7 +9796,7 @@ "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-linux-fcitx4-trace-replay.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-xterm-resumed-preedit-visibility.test.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-composer-placeholder-mask.test.ts src/shared/terminal/input/terminal-composer-draft.test.ts src/main/daemon/emulation/testing/terminal-cursor-line-context.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/dashboard-popout/preview-terminal-ime-bridge-kitty-bytes.test.ts src/shared/terminal/protocol/input-modes/terminal-kitty-keyboard-mode-tracker.test.ts src/main/ipc/terminal-preview-output-stream.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts", - "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts src/main/ipc/macos-keyboard-layout-change-notifications.test.ts src/preload/app-restart-checkpoint-routing.test.ts src/renderer/src/web/web-preload-api-ui.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts src/main/ipc/macos-keyboard-layout-change-notifications.test.ts src/renderer/src/web/web-preload-api-ui.test.ts", "pnpm run test:e2e -- tests/e2e/chinese-ime-chat-input-repro.spec.ts", "pnpm run test:e2e -- tests/e2e/terminal-korean-endofrow-preedit-cell-span.spec.ts tests/e2e/terminal-korean-midline-preedit-occlusion.spec.ts", "pnpm run test:e2e -- tests/e2e/terminal-option-composed-ascii.spec.ts" @@ -9812,7 +9812,6 @@ "src/main/daemon/emulation/testing/terminal-cursor-line-context.test.ts", "src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts", "src/main/ipc/macos-keyboard-layout-change-notifications.test.ts", - "src/preload/app-restart-checkpoint-routing.test.ts", "src/renderer/src/web/web-preload-api-ui.test.ts", "src/renderer/src/components/dashboard-popout/preview-terminal-ime-bridge-kitty-bytes.test.ts", "src/shared/terminal/protocol/input-modes/terminal-kitty-keyboard-mode-tracker.test.ts", @@ -9856,14 +9855,6 @@ "rapid input-source changes coalesce to one latest-generation refresh signal" ] }, - { - "file": "src/preload/app-restart-checkpoint-routing.test.ts", - "assertions": [ - "macOS keyboard reads and the change subscription remain wired after main integration", - "the two-phase change payload reaches the renderer unchanged", - "the subscription disposer removes the exact IPC listener" - ] - }, { "file": "src/renderer/src/components/dashboard-popout/preview-terminal-ime-bridge-kitty-bytes.test.ts", "assertions": [ @@ -9992,7 +9983,8 @@ "Audit1896 historical assertion inventory for three retired and three narrowed refs; not current validated assertions. Original raw assertion objects follow:\n{\n \"file\": \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-xterm-trailing-preedit-occlusion.test.ts\",\n \"assertions\": [\n \"rolling Korean input keeps only the uncommitted trailing syllable in the themed overlay\",\n \"the overlay remains anchored to the live cursor cell and clears after commit\"\n ]\n }\n{\n \"file\": \"src/renderer/src/components/terminal-pane/input/keyboard/xterm-bypass-policy-non-mac.test.ts\",\n \"assertions\": [\n \"standalone Linux 229 keydowns reach xterm while Windows 229 keydowns stay suppressed\",\n \"candidate Space/digit selectors are suppressed only while candidate guards are active\"\n ]\n }\n{\n \"file\": \"src/renderer/src/components/terminal-pane/input/paste/terminal-paste-runtime.test.ts\",\n \"assertions\": [\"paste/runtime forwarding avoids duplicate terminal payloads\"]\n }\n{\n \"file\": \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-xterm-midline-preedit-tail.test.ts\",\n \"assertions\": [\n \"a mid-line preedit reproduces committed cells hidden by its opaque overlay and refreshes that tail after terminal renders\",\n \"arbitrary fully dimmed output remains visible even at column zero\",\n \"the final-column caret stays visible and the textarea candidate anchor shifts left with an over-wide preedit\",\n \"cancel, empty resumed updates, and disposal remove transient overlay state\"\n ]\n }\n{\n \"file\": \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-xterm-resumed-preedit-visibility.test.ts\",\n \"assertions\": [\n \"recorded Windows Korean compositionupdate-without-compositionstart traces reactivate a visible preedit transaction\",\n \"the resumed transaction commits exactly once and cleans up normally\",\n \"the ordinary Linux compositionstart sequence remains unchanged\"\n ]\n }\n{\n \"file\": \"src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts\",\n \"assertions\": [\n \"owned releases use live Shift and Option state and current alternate-key flags\",\n \"owned releases retain the delivered press identity across layout, Shift, and NumLock changes\",\n \"a native dead-key keyup is consumed without inventing an unmatched protocol release\",\n \"event-type pop consumes without bytes and repeat keydowns cannot replace the original release owner\"\n ]\n }", "Renderer direct-owner cleanup retires the complete layout-base-character suite (one registration). Lost alarm: layout invalidation blocks focus refresh, fences an older snapshot, and accepts the new-generation character. The removed case consumed private snapshot/reset state and internal refresh-count accounting. No equivalent replacement or new execution is claimed. Remaining IME/keyboard/provider sources and the unchanged fcitx4 receipt retain their existing bounded qualifications; this removal does not validate their selectors. Historical removed active mappings (verbatim semantic values; no longer active): [{\"field\":\"testFiles\",\"previous\":[\"src/renderer/src/lib/keyboard-layout/layout-base-character.test.ts\"]},{\"field\":\"commands\",\"previous\":[\"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts src/renderer/src/lib/keyboard-layout/layout-base-character.test.ts src/main/ipc/macos-keyboard-layout-change-notifications.test.ts src/preload/app-restart-checkpoint-routing.test.ts src/renderer/src/web/web-preload-api-ui.test.ts\"]}]", "Audit3460 final-v2 composes accepted AUTHOR3454/PEER3455 on actual post263 main: intentionally removes99 authored/106 potential expanded cases across16 selected drivers, including7 whole suites, retaining24 authored/26 potential cases. No equivalent replacement or fresh runtime is claimed. The complete mocked AgentTerminalPreview component suite is removed, including the snapshot-proven kitty mirror forwarding alarm. Remove its active file, command token and assertion reference; the other IME drivers are unchanged and do not replace the removed component checks. Earlier notes, complete dated receipts and archived gaps retain their original scopes.", - "Audit3460 historical original active selectors (verbatim JSON values; not current coverage):\ncommands: [\n \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-native-text-forwarder.test.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-macos-keybinding-dict-trace.test.ts\",\n \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-linux-fcitx4-trace-replay.test.ts\",\n \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-xterm-resumed-preedit-visibility.test.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-composer-placeholder-mask.test.ts src/shared/terminal/input/terminal-composer-draft.test.ts src/main/daemon/emulation/testing/terminal-cursor-line-context.test.ts\",\n \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/dashboard-popout/preview-terminal-ime-bridge-kitty-bytes.test.ts src/renderer/src/components/dashboard-popout/AgentTerminalPreview.test.tsx src/shared/terminal/protocol/input-modes/terminal-kitty-keyboard-mode-tracker.test.ts src/main/ipc/terminal-preview-output-stream.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts\",\n \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts src/main/ipc/macos-keyboard-layout-change-notifications.test.ts src/preload/app-restart-checkpoint-routing.test.ts src/renderer/src/web/web-preload-api-ui.test.ts\",\n \"pnpm run test:e2e -- tests/e2e/chinese-ime-chat-input-repro.spec.ts\",\n \"pnpm run test:e2e -- tests/e2e/terminal-korean-endofrow-preedit-cell-span.spec.ts tests/e2e/terminal-korean-midline-preedit-occlusion.spec.ts\",\n \"pnpm run test:e2e -- tests/e2e/terminal-option-composed-ascii.spec.ts\"\n ]\ntestFiles: [\n \"tests/e2e/terminal-option-composed-ascii.spec.ts\",\n \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-native-text-forwarder.test.ts\",\n \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-macos-keybinding-dict-trace.test.ts\",\n \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-linux-fcitx4-trace-replay.test.ts\",\n \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-xterm-resumed-preedit-visibility.test.ts\",\n \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-composer-placeholder-mask.test.ts\",\n \"src/shared/terminal/input/terminal-composer-draft.test.ts\",\n \"src/main/daemon/emulation/testing/terminal-cursor-line-context.test.ts\",\n \"src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts\",\n \"src/main/ipc/macos-keyboard-layout-change-notifications.test.ts\",\n \"src/preload/app-restart-checkpoint-routing.test.ts\",\n \"src/renderer/src/web/web-preload-api-ui.test.ts\",\n \"src/renderer/src/components/dashboard-popout/preview-terminal-ime-bridge-kitty-bytes.test.ts\",\n \"src/renderer/src/components/dashboard-popout/AgentTerminalPreview.test.tsx\",\n \"src/shared/terminal/protocol/input-modes/terminal-kitty-keyboard-mode-tracker.test.ts\",\n \"src/main/ipc/terminal-preview-output-stream.test.ts\",\n \"src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts\",\n \"tests/e2e/chinese-ime-chat-input-repro.spec.ts\",\n \"tests/e2e/terminal-korean-endofrow-preedit-cell-span.spec.ts\",\n \"tests/e2e/terminal-korean-midline-preedit-occlusion.spec.ts\"\n ]\nassertionRefs: [\n {\n \"file\": \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-linux-fcitx4-trace-replay.test.ts\",\n \"assertions\": [\n \"real fcitx4 Sogou captures (GTK module and XIM) send only the committed candidate for Space and digit picks, under legacy keys and kitty flags 1 and 5\",\n \"a real fcitx4 Google Pinyin in-client preedit still commits through the composition path\",\n \"an XIM pick's code-less keydown pairs with its keyup so kitty event reporting sends one release per pick\"\n ]\n },\n {\n \"file\": \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-xterm-resumed-preedit-visibility.test.ts\",\n \"assertions\": [\"A transaction inferred from update alone commits and cleans up.\"]\n },\n {\n \"file\": \"tests/e2e/terminal-option-composed-ascii.spec.ts\",\n \"assertions\": [\n \"a live kitty-negotiated pane receives the Option-composed ASCII character, not the physical alt chord\",\n \"an Option layer that also needs Shift still reaches the pty as text\",\n \"configured Option-as-Alt still reports the layout-resolved physical chord\",\n \"a non-ASCII Option glyph still reaches the application as its alt hotkey\"\n ]\n },\n {\n \"file\": \"src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts\",\n \"assertions\": [\n \"Autorepeat cannot replace the first release sender; a fresh press can replace an orphaned sender for the same key.\"\n ]\n },\n {\n \"file\": \"src/main/ipc/macos-keyboard-layout-change-notifications.test.ts\",\n \"assertions\": [\n \"rapid input-source changes coalesce to one latest-generation refresh signal\"\n ]\n },\n {\n \"file\": \"src/preload/app-restart-checkpoint-routing.test.ts\",\n \"assertions\": [\n \"macOS keyboard reads and the change subscription remain wired after main integration\",\n \"the two-phase change payload reaches the renderer unchanged\",\n \"the subscription disposer removes the exact IPC listener\"\n ]\n },\n {\n \"file\": \"src/renderer/src/components/dashboard-popout/preview-terminal-ime-bridge-kitty-bytes.test.ts\",\n \"assertions\": [\n \"the real forwarder behind the Preview bridge encodes the commit from the snapshot-proven flags before any live output\",\n \"flags 0/1 commit raw text, flags 2 add one CSI-u release, flags 8 emit one CSI-u press, and flags 10 emit a CSI-u press followed by exactly one release\",\n \"both keydown->insertText->keyup and keydown->keyup->insertText orderings produce the same bytes\",\n \"the release is emitted by the forwarder while xterm's internal kitty flags stay 0\",\n \"the mutable flags getter is read exactly once, at commit time, never on keydown or keyup\",\n \"auto-repeat, two-key rollover, a swallowed commit, a retired claim, blur, and disposal each emit at most one release and never synthesize one\"\n ]\n },\n {\n \"file\": \"src/renderer/src/components/dashboard-popout/AgentTerminalPreview.test.tsx\",\n \"assertions\": [\n \"the Preview hands the forwarder the live kitty mirror seeded from the snapshot's proven flags\"\n ]\n },\n {\n \"file\": \"src/shared/terminal/protocol/input-modes/terminal-kitty-keyboard-mode-tracker.test.ts\",\n \"assertions\": [\n \"a fresh PTY's known 0 is distinguishable from a snapshot's unproven 0 fallback\",\n \"restoreSnapshotFlags proves only the active screen\",\n \"provenance survives absolute sets, screen switches, RIS and DECSTR, but a pop past omitted push history returns to unknown rather than a manufactured 0\"\n ]\n },\n {\n \"file\": \"src/main/ipc/terminal-preview-output-stream.test.ts\",\n \"assertions\": [\n \"a partially covered sequenced chunk retains exactly its unseen suffix in live mode\",\n \"a transformed overlap remains unsliced in replay mode\"\n ]\n },\n {\n \"file\": \"tests/e2e/chinese-ime-chat-input-repro.spec.ts\",\n \"assertions\": [\n \"Sogou-style Space candidate selection submits only the committed Chinese character\",\n \"Sogou-style digit candidate selection submits only the committed Chinese phrase\",\n \"Post-composition Sogou-style digit selection stays out of the PTY after compositionend\",\n \"Legacy orphaned-letter-keyup digit selection submits no selector byte to the PTY; real legacy candidate commit preservation remains a platform gap\"\n ]\n },\n {\n \"file\": \"tests/e2e/terminal-korean-endofrow-preedit-cell-span.spec.ts\",\n \"assertions\": [\n \"the rendered final-column preedit caret remains inside the terminal screen\",\n \"a rendered full-width Korean preedit spans materially more than the one terminal cell reported in #12729\"\n ]\n },\n {\n \"file\": \"tests/e2e/terminal-korean-midline-preedit-occlusion.spec.ts\",\n \"assertions\": [\n \"the rendered opaque overlay reproduces every committed row-tail cell its measured rectangle hides\",\n \"a real Codex placeholder with bold prompt, blank footer gap, model footer, and cursor at column 2 stays masked\"\n ]\n }\n ]" + "Audit3460 historical original active selectors (verbatim JSON values; not current coverage):\ncommands: [\n \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-native-text-forwarder.test.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-macos-keybinding-dict-trace.test.ts\",\n \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-linux-fcitx4-trace-replay.test.ts\",\n \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-xterm-resumed-preedit-visibility.test.ts src/renderer/src/components/terminal-pane/input/ime/terminal-ime-composer-placeholder-mask.test.ts src/shared/terminal/input/terminal-composer-draft.test.ts src/main/daemon/emulation/testing/terminal-cursor-line-context.test.ts\",\n \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/dashboard-popout/preview-terminal-ime-bridge-kitty-bytes.test.ts src/renderer/src/components/dashboard-popout/AgentTerminalPreview.test.tsx src/shared/terminal/protocol/input-modes/terminal-kitty-keyboard-mode-tracker.test.ts src/main/ipc/terminal-preview-output-stream.test.ts src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts\",\n \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts src/main/ipc/macos-keyboard-layout-change-notifications.test.ts src/preload/app-restart-checkpoint-routing.test.ts src/renderer/src/web/web-preload-api-ui.test.ts\",\n \"pnpm run test:e2e -- tests/e2e/chinese-ime-chat-input-repro.spec.ts\",\n \"pnpm run test:e2e -- tests/e2e/terminal-korean-endofrow-preedit-cell-span.spec.ts tests/e2e/terminal-korean-midline-preedit-occlusion.spec.ts\",\n \"pnpm run test:e2e -- tests/e2e/terminal-option-composed-ascii.spec.ts\"\n ]\ntestFiles: [\n \"tests/e2e/terminal-option-composed-ascii.spec.ts\",\n \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-native-text-forwarder.test.ts\",\n \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-macos-keybinding-dict-trace.test.ts\",\n \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-linux-fcitx4-trace-replay.test.ts\",\n \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-xterm-resumed-preedit-visibility.test.ts\",\n \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-composer-placeholder-mask.test.ts\",\n \"src/shared/terminal/input/terminal-composer-draft.test.ts\",\n \"src/main/daemon/emulation/testing/terminal-cursor-line-context.test.ts\",\n \"src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts\",\n \"src/main/ipc/macos-keyboard-layout-change-notifications.test.ts\",\n \"src/preload/app-restart-checkpoint-routing.test.ts\",\n \"src/renderer/src/web/web-preload-api-ui.test.ts\",\n \"src/renderer/src/components/dashboard-popout/preview-terminal-ime-bridge-kitty-bytes.test.ts\",\n \"src/renderer/src/components/dashboard-popout/AgentTerminalPreview.test.tsx\",\n \"src/shared/terminal/protocol/input-modes/terminal-kitty-keyboard-mode-tracker.test.ts\",\n \"src/main/ipc/terminal-preview-output-stream.test.ts\",\n \"src/main/daemon/providers/adapter/testing/daemon-pty-adapter.test.ts\",\n \"tests/e2e/chinese-ime-chat-input-repro.spec.ts\",\n \"tests/e2e/terminal-korean-endofrow-preedit-cell-span.spec.ts\",\n \"tests/e2e/terminal-korean-midline-preedit-occlusion.spec.ts\"\n ]\nassertionRefs: [\n {\n \"file\": \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-linux-fcitx4-trace-replay.test.ts\",\n \"assertions\": [\n \"real fcitx4 Sogou captures (GTK module and XIM) send only the committed candidate for Space and digit picks, under legacy keys and kitty flags 1 and 5\",\n \"a real fcitx4 Google Pinyin in-client preedit still commits through the composition path\",\n \"an XIM pick's code-less keydown pairs with its keyup so kitty event reporting sends one release per pick\"\n ]\n },\n {\n \"file\": \"src/renderer/src/components/terminal-pane/input/ime/terminal-ime-xterm-resumed-preedit-visibility.test.ts\",\n \"assertions\": [\"A transaction inferred from update alone commits and cleans up.\"]\n },\n {\n \"file\": \"tests/e2e/terminal-option-composed-ascii.spec.ts\",\n \"assertions\": [\n \"a live kitty-negotiated pane receives the Option-composed ASCII character, not the physical alt chord\",\n \"an Option layer that also needs Shift still reaches the pty as text\",\n \"configured Option-as-Alt still reports the layout-resolved physical chord\",\n \"a non-ASCII Option glyph still reaches the application as its alt hotkey\"\n ]\n },\n {\n \"file\": \"src/renderer/src/components/terminal-pane/input/keyboard/terminal-option-kitty-release.test.ts\",\n \"assertions\": [\n \"Autorepeat cannot replace the first release sender; a fresh press can replace an orphaned sender for the same key.\"\n ]\n },\n {\n \"file\": \"src/main/ipc/macos-keyboard-layout-change-notifications.test.ts\",\n \"assertions\": [\n \"rapid input-source changes coalesce to one latest-generation refresh signal\"\n ]\n },\n {\n \"file\": \"src/preload/app-restart-checkpoint-routing.test.ts\",\n \"assertions\": [\n \"macOS keyboard reads and the change subscription remain wired after main integration\",\n \"the two-phase change payload reaches the renderer unchanged\",\n \"the subscription disposer removes the exact IPC listener\"\n ]\n },\n {\n \"file\": \"src/renderer/src/components/dashboard-popout/preview-terminal-ime-bridge-kitty-bytes.test.ts\",\n \"assertions\": [\n \"the real forwarder behind the Preview bridge encodes the commit from the snapshot-proven flags before any live output\",\n \"flags 0/1 commit raw text, flags 2 add one CSI-u release, flags 8 emit one CSI-u press, and flags 10 emit a CSI-u press followed by exactly one release\",\n \"both keydown->insertText->keyup and keydown->keyup->insertText orderings produce the same bytes\",\n \"the release is emitted by the forwarder while xterm's internal kitty flags stay 0\",\n \"the mutable flags getter is read exactly once, at commit time, never on keydown or keyup\",\n \"auto-repeat, two-key rollover, a swallowed commit, a retired claim, blur, and disposal each emit at most one release and never synthesize one\"\n ]\n },\n {\n \"file\": \"src/renderer/src/components/dashboard-popout/AgentTerminalPreview.test.tsx\",\n \"assertions\": [\n \"the Preview hands the forwarder the live kitty mirror seeded from the snapshot's proven flags\"\n ]\n },\n {\n \"file\": \"src/shared/terminal/protocol/input-modes/terminal-kitty-keyboard-mode-tracker.test.ts\",\n \"assertions\": [\n \"a fresh PTY's known 0 is distinguishable from a snapshot's unproven 0 fallback\",\n \"restoreSnapshotFlags proves only the active screen\",\n \"provenance survives absolute sets, screen switches, RIS and DECSTR, but a pop past omitted push history returns to unknown rather than a manufactured 0\"\n ]\n },\n {\n \"file\": \"src/main/ipc/terminal-preview-output-stream.test.ts\",\n \"assertions\": [\n \"a partially covered sequenced chunk retains exactly its unseen suffix in live mode\",\n \"a transformed overlap remains unsliced in replay mode\"\n ]\n },\n {\n \"file\": \"tests/e2e/chinese-ime-chat-input-repro.spec.ts\",\n \"assertions\": [\n \"Sogou-style Space candidate selection submits only the committed Chinese character\",\n \"Sogou-style digit candidate selection submits only the committed Chinese phrase\",\n \"Post-composition Sogou-style digit selection stays out of the PTY after compositionend\",\n \"Legacy orphaned-letter-keyup digit selection submits no selector byte to the PTY; real legacy candidate commit preservation remains a platform gap\"\n ]\n },\n {\n \"file\": \"tests/e2e/terminal-korean-endofrow-preedit-cell-span.spec.ts\",\n \"assertions\": [\n \"the rendered final-column preedit caret remains inside the terminal screen\",\n \"a rendered full-width Korean preedit spans materially more than the one terminal cell reported in #12729\"\n ]\n },\n {\n \"file\": \"tests/e2e/terminal-korean-midline-preedit-occlusion.spec.ts\",\n \"assertions\": [\n \"the rendered opaque overlay reproduces every committed row-tail cell its measured rectangle hides\",\n \"a real Codex placeholder with bold prompt, blank footer gap, model footer, and cursor at column 2 stays masked\"\n ]\n }\n ]", + "Preload layout cleanup retires the complete mocked macOS keyboard bridge registration. Its API-read forwarding, two-phase payload forwarding and exact listener-disposer alarms are intentionally lost without equivalent replacement. The remaining restart/checkpoint registrations do not cover this IME contract. Unaffected dated receipts retain their original commands and scope; this metadata change claims no new execution. Retired assertion reference: {\"file\":\"src/preload/app-restart-checkpoint-routing.test.ts\",\"assertions\":[\"macOS keyboard reads and the change subscription remain wired after main integration\",\"the two-phase change payload reaches the renderer unchanged\",\"the subscription disposer removes the exact IPC listener\"]}" ], "demotionRule": "Cannot promote if success is based only on DOM text without PTY byte/cell evidence." }, diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 38ac05a3f8c..b19b9bd1a84 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -770,7 +770,6 @@ "src/main/automations/automation-dispatch-host-fence.test.ts": 2592, "src/main/automations/automation-owner-fencing.test.ts": 2028, "src/main/automations/automation-run-terminal-surface.test.ts": 4180, - "src/main/automations/automation-skip-coalescing.test.ts": 2577, "src/main/automations/ownership/automation-ssh-readoption-migration.test.ts": 59, "src/main/automations/automation-update-host-retarget.test.ts": 1842, @@ -945,7 +944,6 @@ "src/main/browser/extensions/api/browser-extension-api-namespaces.test.ts": 832, "src/main/browser/extensions/installation/browser-extension-archive.test.ts": 72, "src/main/browser/extensions/actions/browser-extension-commands.test.ts": 52, - "src/main/browser/extensions/api-runtime/browser-extension-content-script-persistence.test.ts": 313, "src/main/browser/extensions/context-menus/browser-extension-context-menu-host.test.ts": 68, "src/main/browser/extensions/context-menus/browser-extension-context-menu-model.test.ts": 57, "src/main/browser/extensions/installation/browser-extension-crx.test.ts": 214, @@ -4125,32 +4123,25 @@ "src/packages/process-host/src/windows-tree-kill.test.ts": 81, "src/packages/process-host/src/wsl-distro-allowlist.test.ts": 55, "src/preload/api/notification-sound-playback.test.ts": 64, - "src/preload/api/platform-bridge.test.ts": 48, - "src/preload/app-restart-checkpoint-routing.test.ts": 2028, - "src/preload/browser-client-page-renderer-requests.test.ts": 154, - "src/preload/browser-extension-content-script-relay.test.ts": 65, - "src/preload/browser-extension-document-targets.test.ts": 75, - "src/preload/browser-extension-inert-apis.test.ts": 50, - "src/preload/browser-extension-main-world.test.ts": 81, - "src/preload/browser-extension-native-messaging.test.ts": 70, - "src/preload/browser-extension-storage-relay.test.ts": 104, - "src/preload/browser-extension-storage-sync.test.ts": 108, - "src/preload/browser-extension-web-store-main-world.test.ts": 166, - "src/preload/browser-extension-web-store-wording.test.ts": 194, - "src/preload/browser-find-subscriptions.test.ts": 66, + "src/preload/renderer/restart/app-restart-checkpoint-routing.test.ts": 2028, + "src/preload/renderer/browser-pages/browser-client-page-renderer-requests.test.ts": 154, + "src/preload/browser/extensions/messaging/browser-extension-content-script-relay.test.ts": 65, + "src/preload/browser/extensions/documents/browser-extension-document-targets.test.ts": 75, + "src/preload/browser/extensions/api/browser-extension-inert-apis.test.ts": 50, + "src/preload/browser/extensions/api/browser-extension-main-world.test.ts": 81, + "src/preload/browser/extensions/storage/browser-extension-storage-relay.test.ts": 104, + "src/preload/browser/extensions/storage/browser-extension-storage-sync.test.ts": 108, + "src/preload/browser/extensions/web-store/browser-extension-web-store-main-world.test.ts": 166, + "src/preload/renderer/browser-pages/browser-find-subscriptions.test.ts": 66, "src/preload/browser-window-close.test.ts": 98, - "src/preload/close-active-tab-payload-admission.test.ts": 48, + "src/preload/renderer/window-commands/close-active-tab-payload-admission.test.ts": 48, "src/preload/doc-preview-link-interception.test.ts": 234, - "src/preload/gitlab-work-item-details.test.ts": 49, - "src/preload/opencode-go-credentials.test.ts": 45, - "src/preload/plugin-frame-guest-render-lane.test.ts": 47, - "src/preload/plugin-frame-guest.test.ts": 287, - "src/preload/renderer-heap-statistics-reader.test.ts": 50, - "src/preload/renderer-process-memory-reader.test.ts": 66, - "src/preload/renderer-restart-wiring.test.ts": 96, - "src/preload/ssh-authority-forwarding.test.ts": 1111, - "src/preload/updater-package-recovery.test.ts": 1102, - "src/preload/usage-provider-api.test.ts": 46, + "src/preload/plugins/frames/plugin-frame-guest-render-lane.test.ts": 47, + "src/preload/plugins/frames/plugin-frame-guest.test.ts": 287, + "src/preload/renderer/diagnostics/renderer-heap-statistics-reader.test.ts": 50, + "src/preload/renderer/diagnostics/renderer-process-memory-reader.test.ts": 66, + "src/preload/renderer/restart/renderer-restart-wiring.test.ts": 96, + "src/preload/api/ssh-authority-forwarding.test.ts": 1111, "src/renderer/src/app-shell/shutdown-checkpoint-persist.test.ts": 65, "src/renderer/src/app-shell/use-profile-state-save-delay-notice.sonner.test.tsx": 355, "src/renderer/src/app-shell/use-profile-state-save-delay-notice.test.tsx": 293, @@ -4771,7 +4762,6 @@ "src/renderer/src/components/plain-ssh-terminal/PlainSshTerminalPanels.test.tsx": 2510, "src/renderer/src/components/plugins/declarative/PluginDeclarativeView.test.tsx": 2804, "src/renderer/src/components/plugins/dialogs/PluginUpdateAllDialog.test.tsx": 2313, - "src/renderer/src/components/plugins/list/PluginManager.test.tsx": 3449, "src/renderer/src/components/plugins/list/plugin-list-entries.test.ts": 231, "src/renderer/src/components/plugins/page/PluginAddsSection.test.tsx": 2043, "src/renderer/src/components/plugins/page/PluginCommandReplacementRow.test.tsx": 1926, @@ -4779,7 +4769,6 @@ "src/renderer/src/components/plugins/page/PluginSettingsSection.test.tsx": 572, "src/renderer/src/components/plugins/state/use-plugin-contribution-source.test.tsx": 1935, "src/renderer/src/components/plugins/state/use-plugin-install-link.test.tsx": 518, - "src/renderer/src/components/plugins/state/use-plugin-page-navigation.test.tsx": 480, "src/renderer/src/components/plugins/state/use-plugin-review-queue.test.tsx": 389, "src/renderer/src/components/plugins/view/plugin-view-acts.test.tsx": 3241, "src/renderer/src/components/plugins/view/plugin-view-data-acts.test.tsx": 3002, diff --git a/config/tsconfig.tc.web.json b/config/tsconfig.tc.web.json index b0f23d98c71..95529130196 100644 --- a/config/tsconfig.tc.web.json +++ b/config/tsconfig.tc.web.json @@ -14,18 +14,20 @@ "../src/renderer/src/**/*.tsx", "../src/preload/api-types.ts", "../src/preload/api/**/*", - "../src/preload/backend-port-client.ts", - "../src/preload/browser-client-page-renderer-requests.ts", - "../src/preload/browser-find-subscriptions.ts", - "../src/preload/close-active-tab-payload-admission.ts", - "../src/preload/e2e-config.ts", - "../src/preload/gitlab.ts", - "../src/preload/preload-runtime-support.ts", - "../src/preload/renderer-heap-statistics-reader.ts", - "../src/preload/renderer-process-memory-reader.ts", - "../src/preload/renderer-restart-wiring.ts", - "../src/preload/runtime-environment-subscriptions.ts", - "../src/preload/usage-provider-api.ts", + "../src/preload/index.ts", + "../src/preload/usage/backend-port-client.ts", + "../src/preload/renderer/browser-pages/browser-client-page-renderer-requests.ts", + "../src/preload/renderer/browser-pages/browser-find-subscriptions.ts", + "../src/preload/renderer/window-commands/close-active-tab-payload-admission.ts", + "../src/preload/renderer/e2e-config.ts", + "../src/preload/renderer/hidden-window-frame-pump.ts", + "../src/preload/api/gitlab.ts", + "../src/preload/renderer/renderer-runtime.ts", + "../src/preload/renderer/diagnostics/renderer-heap-statistics-reader.ts", + "../src/preload/renderer/diagnostics/renderer-process-memory-reader.ts", + "../src/preload/renderer/restart/renderer-restart-wiring.ts", + "../src/preload/runtime/runtime-environment-subscriptions.ts", + "../src/preload/usage/usage-provider-api.ts", "../src/main/pi/rpc/options/rpc-model-schema.ts", "../src/main/pi/rpc/options/rpc-model-options.ts", "../src/shared/**/*", diff --git a/src/main/browser/client-pages/renderer/browser-client-page-renderer-lifecycle.electron.test.ts b/src/main/browser/client-pages/renderer/browser-client-page-renderer-lifecycle.electron.test.ts index eaa68d7e2ce..137b4712532 100644 --- a/src/main/browser/client-pages/renderer/browser-client-page-renderer-lifecycle.electron.test.ts +++ b/src/main/browser/client-pages/renderer/browser-client-page-renderer-lifecycle.electron.test.ts @@ -228,7 +228,12 @@ async function runFixture(): Promise { const resultPath = join(root, 'result.json') writeFileSync( preloadSource, - preloadEntry(join(process.cwd(), 'src/preload/browser-client-page-renderer-requests.ts')) + preloadEntry( + join( + process.cwd(), + 'src/preload/renderer/browser-pages/browser-client-page-renderer-requests.ts' + ) + ) ) writeFileSync( rendererSource, diff --git a/src/main/browser/extensions/api-runtime/browser-extension-api-host.test.ts b/src/main/browser/extensions/api-runtime/browser-extension-api-host.test.ts index c1a882d765d..20cdd567c36 100644 --- a/src/main/browser/extensions/api-runtime/browser-extension-api-host.test.ts +++ b/src/main/browser/extensions/api-runtime/browser-extension-api-host.test.ts @@ -70,7 +70,7 @@ mocks.userData = mkdtempSync(join(tmpdir(), 'orca-extension-api-host-')) const { BROWSER_EXTENSION_INVOKE_CHANNEL } = await import('../../../../shared/browser/extensions/browser-extension-channels') -await import('../../../../preload/browser-extension-api-surface') +await import('../../../../preload/browser/extensions/api/browser-extension-api-surface') const registry = await import('./browser-extension-namespace-registry') const host = await import('./browser-extension-api-host') const contexts = await import('./browser-extension-contexts') @@ -180,22 +180,6 @@ describe('extension API host', () => { }) }) -it('retires the current host after extension support restarts in the same document', async () => { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the session boundary copies the extensions, serviceWorkers and protocol members consumed by the actual host. - const isolated = { ...extensionSession } as unknown as Electron.Session - host.attachBrowserExtensionApiHostToSession(isolated, 'restarted') - const current = frameEvent(`chrome-extension://${EXTENSION_ID}/page.html`, isolated, 10, 1) - const listen = { type: 'listen', namespace: 'probe', event: 'onPing', active: true } - await invoke(current.event, listen) - host.forgetBrowserExtensionContexts(isolated) - host.attachBrowserExtensionApiHostToSession(isolated, 'restarted') - await invoke(current.event, listen) - current.event.sender.emit('did-navigate') - expect(contexts.getHostSessionState(isolated)?.frames.size).toBe(0) - expect(contexts.browserExtensionEventHasListener(isolated, 'probe', 'onPing')).toBe(false) - host.forgetBrowserExtensionContexts(isolated) -}) - describe('extension subframe listener retirement', () => { const listen = { type: 'listen', namespace: 'probe', event: 'onPing', active: true } @@ -218,66 +202,6 @@ describe('extension subframe listener retirement', () => { return current } - it('shares one port across listeners and replaces it after final removal', async () => { - const { sess, state } = isolatedSession() - const page = frameEvent(`chrome-extension://${EXTENSION_ID}/frame.html`, sess, 21, 1) - await invoke(page.event, listen) - const first = channel() - const lateClose = first.port1.listeners('close')[0] - await invoke(page.event, listen) - await invoke(page.event, { ...listen, namespace: 'tabs', event: 'onCreated' }) - expect(page.frame.postMessage).toHaveBeenCalledTimes(1) - await invoke(page.event, { ...listen, active: false }) - expect(state.frames.size).toBe(1) - expect(first.port1.close).not.toHaveBeenCalled() - await invoke(page.event, { ...listen, namespace: 'tabs', event: 'onCreated', active: false }) - expect(state.frames.size).toBe(0) - expect(first.port1.close).toHaveBeenCalledTimes(1) - expect(first.port1.listenerCount('close')).toBe(0) - await invoke(page.event, listen) - expect(channel()).not.toBe(first) - lateClose?.() - host.emitBrowserExtensionEvent({ session: sess }, 'probe', 'onPing', ['successor']) - expect(page.frame.send).toHaveBeenCalledTimes(1) - expect(state.frames.size).toBe(1) - host.forgetBrowserExtensionContexts(sess) - }) - - it('preserves sibling frames, windows, sessions and worker listeners', async () => { - const { sess, state } = isolatedSession() - const elsewhere = isolatedSession() - const removed = frameEvent(`chrome-extension://${EXTENSION_ID}/frame.html`, sess, 22, 1) - const sibling = frameEvent(`chrome-extension://${EXTENSION_ID}/frame.html`, sess, 22, 2) - sibling.event.sender = removed.event.sender - const window = frameEvent(`chrome-extension://${EXTENSION_ID}/page.html`, sess, 22, 3) - window.event.sender.id = 78 - const other = frameEvent(`chrome-extension://${EXTENSION_ID}/page.html`, elsewhere.sess, 23, 1) - await invoke(removed.event, listen) - const owner = channel() - await invoke(sibling.event, listen) - await invoke(window.event, listen) - await invoke(other.event, listen) - const worker = contexts.ensureWorkerContext( - state, - EXTENSION_ID, - `chrome-extension://${EXTENSION_ID}/` - ) - worker.events.add('probe.onPing') - worker.stream = { send: vi.fn(), close: vi.fn() } - owner.port1.emit('close') - host.emitBrowserExtensionEvent({}, 'probe', 'onPing', ['survivors']) - expect(removed.frame.send).not.toHaveBeenCalled() - for (const frame of [sibling.frame, window.frame, other.frame]) { - expect(frame.send).toHaveBeenCalledTimes(1) - } - expect(worker.stream.send).toHaveBeenCalledTimes(1) - expect(worker.stream.close).not.toHaveBeenCalled() - expect(state.frames.size).toBe(2) - expect(elsewhere.state.frames.size).toBe(1) - host.forgetBrowserExtensionContexts(sess) - host.forgetBrowserExtensionContexts(elsewhere.sess) - }) - it('keeps extension membership snapshotted while resolving current frames after callbacks', async () => { const { sess } = isolatedSession() const first = frameEvent(`chrome-extension://${EXTENSION_ID}/first.html`, sess, 41, 1) diff --git a/src/main/browser/extensions/api-runtime/browser-extension-content-script-persistence.test.ts b/src/main/browser/extensions/api-runtime/browser-extension-content-script-persistence.test.ts deleted file mode 100644 index af0d8e07d20..00000000000 --- a/src/main/browser/extensions/api-runtime/browser-extension-content-script-persistence.test.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { mkdtempSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { runInNewContext } from 'node:vm' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { ContentScriptMethod } from './browser-extension-content-scripts' - -type Script = Record & { id: string } -const mocks = vi.hoisted(() => ({ - directory: '', - scripts: new Map(), - beforeExecute: async (): Promise => {}, - beforeCallback: async (): Promise => {}, - nativeError: '', - windows: new Array<{ options: unknown; destroyed: boolean }>() -})) -vi.mock('electron', () => ({ - app: { on() {} }, - webFrameMain: { fromId() {} }, - BrowserWindow: class { - record: { options: unknown; destroyed: boolean } - constructor(options: unknown) { - this.record = { options, destroyed: false } - mocks.windows.push(this.record) - } - async loadURL() {} - webContents = { - executeJavaScript: async (code: string) => { - await mocks.beforeExecute() - const runtime: { lastError?: { message: string } } = {} - const complete = async (callback: (value?: unknown) => void, value?: unknown) => { - await mocks.beforeCallback() - if (mocks.nativeError) { - runtime.lastError = { message: mocks.nativeError } - } - callback(value) - delete runtime.lastError - } - return runInNewContext(code, { - chrome: { - runtime, - scripting: { - registerContentScripts(scripts: Script[], callback: () => void) { - if (!mocks.nativeError) { - for (const script of scripts) { - if (mocks.scripts.has(script.id)) { - runtime.lastError = { message: 'Duplicate script ID' } - callback() - delete runtime.lastError - return - } - mocks.scripts.set(script.id, { ...script }) - } - } - complete(callback) - }, - updateContentScripts(updates: Script[], callback: () => void) { - if (!mocks.nativeError) { - for (const update of updates) { - mocks.scripts.set(update.id, { ...mocks.scripts.get(update.id), ...update }) - } - } - complete(callback) - }, - unregisterContentScripts(filter: { ids?: string[] }, callback: () => void) { - for (const id of filter.ids ?? [...mocks.scripts.keys()]) { - mocks.scripts.delete(id) - } - complete(callback) - }, - getRegisteredContentScripts( - filterOrCallback: { ids?: string[] } | ((value: Script[]) => void), - callback?: (value: Script[]) => void - ) { - const filter = typeof filterOrCallback === 'function' ? {} : filterOrCallback - const done = typeof filterOrCallback === 'function' ? filterOrCallback : callback - done?.( - [...mocks.scripts.values()] - .filter((script) => !filter.ids || filter.ids.includes(script.id)) - .map((script) => ({ ...script })) - ) - } - } - } - }) - } - } - destroy() { - this.record.destroyed = true - } - } -})) -vi.mock('../profile-storage/browser-extension-profile-data-directory', () => ({ - getBrowserExtensionProfileDataDirectory: () => mocks.directory -})) -mocks.directory = mkdtempSync(join(tmpdir(), 'orca-content-script-persistence-')) - -const registry = await import('./browser-extension-namespace-registry') -const { registerScriptingNamespaces } = await import('../api/scripting') -const { forgetBrowserExtensionContentScriptRestore, restoreBrowserExtensionContentScripts } = - await import('./browser-extension-content-scripts') -const { getBrowserExtensionProfileStateStore } = - await import('../profile-storage/browser-extension-profile-state-store') -const { installBrowserExtensionApis } = - await import('../../../../preload/browser-extension-main-world') -const { BROWSER_EXTENSION_API_SURFACE } = - await import('../../../../preload/browser-extension-api-surface') -const extension: Electron.Extension = { - id: 'a'.repeat(32), - name: 'Popup only', - version: '1', - path: '/example', - url: '', - manifest: { - manifest_version: 3, - permissions: ['scripting'], - action: { default_popup: 'popup.html' } - } -} -// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: native page creation is mocked; the owner only uses session identity. -const session = {} as Electron.Session -const store = getBrowserExtensionProfileStateStore() -const stored = () => store.read('default', extension.id).contentScripts -function context(fromWorker: boolean) { - const scripting: Partial> = {} - const chrome = { runtime: { getManifest: () => extension.manifest }, scripting } - const bridge = { - call: async (namespace: string, method: string, args: unknown[]) => ({ - ok: true, - value: await registry - .getBrowserExtensionNamespace(namespace) - ?.methods[method].invoke( - { extension, session, browserProfileId: 'default', fromWorker }, - args - ) - }), - listen() {}, - onEvent() {}, - passesFilter: () => true - } - runInNewContext(`(${installBrowserExtensionApis.toString()})(bridge, surface)`, { - chrome, - bridge, - console, - surface: { scripting: BROWSER_EXTENSION_API_SURFACE.scripting } - }) - const call = async (method: ContentScriptMethod, ...args: unknown[]) => { - const fn = chrome.scripting[method] - if (typeof fn !== 'function') { - throw new Error('Missing scripting method') - } - return fn(...args) - } - return call -} -registerScriptingNamespaces() -beforeEach(() => { - forgetBrowserExtensionContentScriptRestore(session, extension.id) - mocks.scripts.clear() - mocks.windows.length = 0 - mocks.beforeExecute = async () => {} - mocks.beforeCallback = async () => {} - mocks.nativeError = '' - store.update('default', extension.id, (state) => ({ ...state, contentScripts: [] })) -}) - -describe('native content script ownership', () => { - it('retires timed-out native work instead of allowing a successor to overlap it', async () => { - const script = { id: 'saved', js: ['saved.js'] } - store.update('default', extension.id, (state) => ({ ...state, contentScripts: [script] })) - let release = () => {} - let completed = () => {} - const nativeCompleted = new Promise((resolve) => { - completed = resolve - }) - mocks.beforeCallback = () => - new Promise((resolve) => { - release = () => { - resolve() - completed() - } - }) - await expect( - restoreBrowserExtensionContentScripts(session, 'default', extension, 1) - ).rejects.toThrow('timed out') - await expect(context(false)('unregisterContentScripts', {})).rejects.toThrow('timed out') - expect(mocks.windows).toHaveLength(1) - expect(mocks.windows[0].destroyed).toBe(true) - expect([...mocks.scripts.values()]).toEqual([script]) - release() - await nativeCompleted - expect(stored()).toEqual([script]) - }) -}) diff --git a/src/main/browser/extensions/api/browser-extension-api-namespaces.test.ts b/src/main/browser/extensions/api/browser-extension-api-namespaces.test.ts index 0b5419b68ba..4cad31a7369 100644 --- a/src/main/browser/extensions/api/browser-extension-api-namespaces.test.ts +++ b/src/main/browser/extensions/api/browser-extension-api-namespaces.test.ts @@ -72,15 +72,10 @@ const { getBrowserExtensionProfileStateStore } = const menuHost = await import('../context-menus/browser-extension-context-menu-host') const webRequest = await import('../requests/browser-extension-web-request-observer') const { setBrowserExtensionSupportEnabled } = await import('../browser-extension-support') -const contexts = await import('../api-runtime/browser-extension-contexts') -const eventDispatch = await import('../api-runtime/browser-extension-event-dispatch') -const { attachWorkerEventStream } = - await import('../api-runtime/browser-extension-worker-event-stream') -const { handleBrowserExtensionApiRequest } = - await import('../api-runtime/browser-extension-api-request') -await import('../../../../preload/browser-extension-storage-relay') + +await import('../../../../preload/browser/extensions/storage/browser-extension-storage-relay') const { PRELOAD_STORAGE_SYNC_NAMESPACE } = - await import('../../../../preload/browser-extension-storage-sync') + await import('../../../../preload/browser/extensions/storage/browser-extension-storage-sync') const VAULT = 'a'.repeat(32) const OTHER = 'b'.repeat(32) @@ -205,79 +200,6 @@ beforeEach(() => { mocks.prompt.mockReset() }) -describe('permissions.request', () => { - it.each(['extension unload', 'profile deletion'] as const)( - 'does not recreate grants when accepted after %s', - async (reason) => { - const extensionId = (reason === 'extension unload' ? 'd' : 'e').repeat(32) - extensions.set(extensionId, makeExtension(extensionId, { optional_permissions: ['privacy'] })) - let answer: (accepted: boolean) => void = () => {} - mocks.prompt.mockReturnValueOnce( - new Promise((resolve) => { - answer = resolve - }) - ) - const pending = call( - extensionId, - 'permissions', - 'request', - [{ permissions: ['privacy'] }], - 'page' - ) - const store = getBrowserExtensionProfileStateStore() - if (reason === 'extension unload') { - extensionSession.extensions.emit('extension-unloaded', {}, extensions.get(extensionId)) - store.forgetExtension(extensionId) - } else { - host.forgetBrowserExtensionContexts(session) - store.forgetProfile('default') - } - answer(true) - await expect(pending).resolves.toBe(false) - expect(store.readProfile('default')).not.toHaveProperty(extensionId) - host.attachBrowserExtensionApiHostToSession(session, 'default') - } - ) - - it('delivers a previously registered optional event only while permission is granted', async () => { - const extensionId = 'f'.repeat(32) - extensions.set(extensionId, makeExtension(extensionId, { optional_permissions: ['privacy'] })) - const event = 'services.passwordSavingEnabled.onChange' - const frame = await listenFrame(extensionId, 'privacy', event) - const state = contexts.getHostSessionState(session) - if (!state) { - throw new Error('Session not attached') - } - const worker = contexts.ensureWorkerContext( - state, - extensionId, - `chrome-extension://${extensionId}/` - ) - const listen = () => - handleBrowserExtensionApiRequest( - caller(extensionId), - state, - { type: 'listen', namespace: 'privacy', event, active: true }, - { kind: 'worker', context: worker } - ) - await expect(listen()).resolves.toEqual({ ok: true, value: undefined }) - host.emitBrowserExtensionEvent({ session, extensionId }, 'privacy', event, [{ value: false }]) - expect(frame.payloads()).toEqual([]) - mocks.prompt.mockResolvedValueOnce(true) - await call(extensionId, 'permissions', 'request', [{ permissions: ['privacy'] }], 'page') - host.emitBrowserExtensionEvent({ session, extensionId }, 'privacy', event, [{ value: false }]) - expect(frame.payloads()).toEqual([[{ value: false }]]) - expect(worker.queue).toHaveLength(1) - await call(extensionId, 'permissions', 'remove', [{ permissions: ['privacy'] }]) - const send = vi.fn() - attachWorkerEventStream(worker, { send, close() {} }) - await expect(listen()).resolves.toEqual({ ok: true, value: undefined }) - expect(send).not.toHaveBeenCalled() - host.emitBrowserExtensionEvent({ session, extensionId }, 'privacy', event, [{ value: true }]) - expect(frame.payloads()).toEqual([[{ value: false }]]) - }) -}) - describe('context menus in the page menu', () => { const guest = { id: 900, @@ -379,63 +301,10 @@ describe('storage.sync', () => { ) } - function listeningWorker(sess: Electron.Session, extensionId: string) { - const state = contexts.getHostSessionState(sess) - if (!state) { - throw new Error('Session is not attached') - } - const context = contexts.ensureWorkerContext( - state, - extensionId, - `chrome-extension://${extensionId}/` - ) - context.events.add(eventDispatch.eventKey(SYNC, 'onChanged')) - const send = vi.fn() - attachWorkerEventStream(context, { send, close: vi.fn() }) - return { changes: () => send.mock.calls.map(([payload]) => payload.args) } - } - beforeEach(async () => { await sync('clear', []) }) - it('sends exact old and new values to the extension pages and worker in that session only', async () => { - host.attachBrowserExtensionApiHostToSession(workSession, 'work') - const worker = listeningWorker(session, VAULT) - const page = await listenFrame(VAULT, SYNC, 'onChanged') - const otherExtension = listeningWorker(session, OTHER) - const otherProfile = listeningWorker(workSession, VAULT) - const fromPage = { kind: 'page' } as const - - const written = { a: { newValue: 1 }, b: { newValue: { deep: [1] } } } - // The writer gets its changes back and fires its own listeners. - await expect(sync('set', [{ a: 1, b: { deep: [1] } }], fromPage)).resolves.toEqual(written) - await sync('set', [{ a: 1, b: { deep: [2] } }], fromPage) - await expect(sync('set', [{ a: 1 }], fromPage)).resolves.toEqual({}) - await sync('remove', [['a', 'missing']], fromPage) - await sync('clear', [], fromPage) - await sync('clear', [], fromPage) - const expected = [ - [written], - [{ b: { oldValue: { deep: [1] }, newValue: { deep: [2] } } }], - [{ a: { oldValue: 1 } }], - [{ b: { oldValue: { deep: [2] } } }] - ] - expect(worker.changes()).toEqual(expected) - expect(page.payloads()).toEqual(expected) - expect(otherExtension.changes()).toEqual([]) - expect(otherProfile.changes()).toEqual([]) - - await sync('set', [{ fromWorker: 1 }]) - expect(worker.changes()).toHaveLength(4) - expect(page.payloads().at(-1)).toEqual([{ fromWorker: { newValue: 1 } }]) - - await sync('set', [{ mine: true }]) - await expect(sync('get', [null], { extensionId: OTHER })).resolves.toEqual({}) - await expect(sync('get', [null], { profile: 'work' })).resolves.toEqual({}) - await sync('clear', []) - }) - it('validates an atomic batch against its final size regardless of key order', async () => { const initial = Object.fromEntries( Array.from({ length: 12 }, (_, index) => [`key${index}`, 'x'.repeat(8000)]) diff --git a/src/main/browser/extensions/api/declarative-net-request.test.ts b/src/main/browser/extensions/api/declarative-net-request.test.ts index 1c933fdb927..44a63993765 100644 --- a/src/main/browser/extensions/api/declarative-net-request.test.ts +++ b/src/main/browser/extensions/api/declarative-net-request.test.ts @@ -29,14 +29,12 @@ const { attachBrowserExtensionDnr, getDnrSessionRuntime } = const { browserExtensionDnrRequestDecider: decider } = await import('../declarative-net-request/session/browser-extension-dnr-requests') await import('../declarative-net-request/session/browser-extension-dnr-limits') -await import('../../../../preload/browser-extension-api-surface') +await import('../../../../preload/browser/extensions/api/browser-extension-api-surface') const { getBrowserExtensionDnrStore } = await import('../declarative-net-request/session/browser-extension-dnr-store') const { registerPermissionsNamespace } = await import('./permissions') -const { ensureHostSessionState } = await import('../api-runtime/browser-extension-contexts') -const { getBrowserExtensionInstallStore } = - await import('../installation/browser-extension-install-store') + registerPermissionsNamespace() registerDeclarativeNetRequestNamespace() @@ -132,137 +130,4 @@ describe('chrome.declarativeNetRequest', () => { expect(next.blocks('https://z.example/1.js')).toBe(true) await next.call('updateDynamicRules', { removeRuleIds: [1, 3] }) }) - - it('switches static rulesets, and an update brings back the manifest choice', async () => { - const { call, blocks } = await launch() - await expect(call('getEnabledRulesets')).resolves.toEqual(['a']) - await call('updateEnabledRulesets', { enableRulesetIds: ['b'], disableRulesetIds: ['a'] }) - expect(blocks('https://b.example/x.js')).toBe(true) - expect(blocks('https://a.example/x.js')).toBe(false) - await expect(call('updateEnabledRulesets', { enableRulesetIds: ['zzz'] })).rejects.toThrow( - 'Invalid ruleset id: zzz.' - ) - await expect(call('getAvailableStaticRuleCount')).resolves.toBe(330_000 - 2) - await expect((await launch()).call('getEnabledRulesets')).resolves.toEqual(['b']) - await expect((await launch(extension('2'))).call('getEnabledRulesets')).resolves.toEqual(['a']) - }) - - it('drops updates still queued when the extension unloads', async () => { - const { session } = await launch(extension('6')) - const runtime = getDnrSessionRuntime(session) - const rules = runtime?.extensions.get(ID)?.rules - const enabling = rules?.updateEnabledRulesets(['b'], []) - const disabling = rules?.updateStaticRules('a', [1], []) - runtime?.removeExtension(ID) - await enabling?.catch(() => undefined) - await expect(disabling).rejects.toThrow('Internal error.') - const next = await launch(extension('6')) - expect(next.blocks('https://a.example/x.js')).toBe(true) - }) -}) - -describe('live DNR permission changes and precedence', () => { - it('activates optional authority immediately and preserves session rules across feedback changes', async () => { - const ext = extension() - ext.manifest.permissions = [] - ext.manifest.optional_permissions = [ - 'declarativeNetRequestWithHostAccess', - 'declarativeNetRequestFeedback' - ] - const { session, call, blocks } = await launch(ext) - ensureHostSessionState(session, PROFILE) - const caller = { extension: ext, session, browserProfileId: PROFILE, userGesture: true } - const permission = (method: string, permissions: string[]) => - registry - .getBrowserExtensionNamespace('permissions') - ?.methods[method].invoke(caller, [{ permissions }]) - expect(blocks('https://a.example/file')).toBe(false) - await expect(permission('request', ['declarativeNetRequestWithHostAccess'])).resolves.toBe(true) - expect(blocks('https://a.example/file')).toBe(true) - await call('updateSessionRules', { addRules: [block(21, '||session.example^')] }) - await permission('request', ['declarativeNetRequestFeedback']) - expect(blocks('https://session.example/file')).toBe(true) - expect(await call('getSessionRules')).toEqual([block(21, '||session.example^')]) - const runtime = getDnrSessionRuntime(session) - expect(runtime?.matched.matchedRules(ID, {})).not.toEqual([]) - await permission('remove', ['declarativeNetRequestFeedback']) - expect(runtime?.matched.matchedRules(ID, {})).toEqual([]) - expect(blocks('https://session.example/file')).toBe(true) - await permission('remove', ['declarativeNetRequestWithHostAccess']) - expect(blocks('https://a.example/file')).toBe(false) - expect(blocks('https://session.example/file')).toBe(false) - }) - - it('keeps install precedence when an older extension reloads or another profile loads in reverse order', async () => { - const store = getBrowserExtensionInstallStore() - const make = (id: string, target: string): Electron.Extension => { - const path = mkdtempSync(join(tmpdir(), 'orca-dnr-precedence-')) - writeFileSync( - join(path, 'rules.json'), - JSON.stringify([ - { - id: 1, - condition: { urlFilter: '|https://tie.example/' }, - action: { type: 'redirect', redirect: { url: target } } - } - ]) - ) - store.save({ - id, - name: id, - version: '1', - path, - source: 'unpacked', - iconPath: null, - permissions: ['declarativeNetRequest'], - hostPermissions: [''], - optionsPage: null - }) - return { - id, - name: id, - version: '1', - path, - url: '', - manifest: { - permissions: ['declarativeNetRequest'], - host_permissions: [''], - declarative_net_request: { - rule_resources: [{ id: 'rules', enabled: true, path: 'rules.json' }] - } - } - } - } - const older = make('a'.repeat(32), 'https://older.example/') - const newer = make('b'.repeat(32), 'https://newer.example/') - const first = await launch(older) - const runtime = getDnrSessionRuntime(first.session) - if (!runtime) { - throw new Error('Missing DNR runtime') - } - await runtime.addExtension(newer) - const winner = (owner: typeof runtime) => - owner.engine.evaluateBeforeRequest( - { - url: 'https://tie.example/file', - method: 'GET', - type: 'script', - initiator: null, - tabId: TAB - }, - { allowByExtension: new Map(), actions: [] }, - { hostAccess: () => 'allowed' } - ).actions[0]?.redirectUrl - expect(winner(runtime)).toBe('https://newer.example/') - runtime.removeExtension(older.id) - await runtime.addExtension(older) - expect(winner(runtime)).toBe('https://newer.example/') - const second = await launch(newer) - const reversed = getDnrSessionRuntime(second.session) - if (!reversed) { - throw new Error('Missing DNR runtime') - } - await reversed.addExtension(older) - expect(winner(reversed)).toBe('https://newer.example/') - }) }) diff --git a/src/main/browser/extensions/api/storage-relay.ts b/src/main/browser/extensions/api/storage-relay.ts index 7a24c8fc2a2..9b7f2df9832 100644 --- a/src/main/browser/extensions/api/storage-relay.ts +++ b/src/main/browser/extensions/api/storage-relay.ts @@ -9,7 +9,7 @@ import { /** * Electron never fires chrome.storage.onChanged in extension service workers, so extension pages * report their own storage writes here and main forwards them to the same extension's worker in the - * same session (see src/preload/browser-extension-storage-relay.ts). Workers deliver their own. + * same session (see src/preload/browser/extensions/storage/browser-extension-storage-relay.ts). Workers deliver their own. * Sync is not relayed: main owns that area and fires its events (api/storage-sync.ts). */ const STORAGE_RELAY_NAMESPACE = 'orcaStorageRelay' diff --git a/src/main/browser/extensions/api/storage-sync.ts b/src/main/browser/extensions/api/storage-sync.ts index 17862c39604..073e44bdac5 100644 --- a/src/main/browser/extensions/api/storage-sync.ts +++ b/src/main/browser/extensions/api/storage-sync.ts @@ -15,7 +15,7 @@ import type { BrowserExtensionProfileDocumentItems } from '../profile-storage/br * writer, so every change carries exact old values. A write returns its changes to the caller, * which fires its own listeners (a starting worker's write can reach main before its listen * does); onChanged goes to the extension's other pages and its worker in the session. - * The preload installs it as chrome.storage.sync (src/preload/browser-extension-storage-sync.ts). + * The preload installs it as chrome.storage.sync (src/preload/browser/extensions/storage/browser-extension-storage-sync.ts). */ const STORAGE_SYNC_NAMESPACE = 'orcaStorageSync' diff --git a/src/main/browser/extensions/api/web-navigation.test.ts b/src/main/browser/extensions/api/web-navigation.test.ts index ade2520dc15..918aa1bc48c 100644 --- a/src/main/browser/extensions/api/web-navigation.test.ts +++ b/src/main/browser/extensions/api/web-navigation.test.ts @@ -36,7 +36,7 @@ const registry = await import('../api-runtime/browser-extension-namespace-regist const { observeBrowserExtensionWebNavigation, registerWebNavigationNamespace } = await import('./web-navigation') const { PRELOAD_DOCUMENT_TARGETS_NAMESPACE } = - await import('../../../../preload/browser-extension-document-targets') + await import('../../../../preload/browser/extensions/documents/browser-extension-document-targets') function frame(frameTreeNodeId: number, url: string, parent: FakeFrame | null = null): FakeFrame { const created: FakeFrame = { diff --git a/src/main/browser/extensions/register-browser-extension-namespaces.ts b/src/main/browser/extensions/register-browser-extension-namespaces.ts index 38223e59a31..4f6808de1f2 100644 --- a/src/main/browser/extensions/register-browser-extension-namespaces.ts +++ b/src/main/browser/extensions/register-browser-extension-namespaces.ts @@ -19,7 +19,7 @@ import { registerTabsNamespace } from './api/tabs' import { registerWebNavigationNamespace } from './api/web-navigation' import { registerWindowsNamespace } from './api/windows' -/** Every main-side namespace; each must also appear in src/preload/browser-extension-api-surface.ts. */ +/** Every main-side namespace; each must also appear in src/preload/browser/extensions/api/browser-extension-api-surface.ts. */ export function registerBrowserExtensionNamespaces(): void { registerWindowsNamespace() registerTabsNamespace() diff --git a/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.test.ts b/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.test.ts index d8dde11508b..fe53dc4afc5 100644 --- a/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.test.ts +++ b/src/main/browser/extensions/tabs-and-windows/browser-extension-discarded-tabs.test.ts @@ -110,7 +110,7 @@ const { recordActiveBrowserPage } = await import('./browser-extension-tab-model' const { registerTabsNamespace } = await import('../api/tabs') const { announceRegisteredBrowserExtensionTab, observeBrowserExtensionTabLifecycle } = await import('./browser-extension-tab-lifecycle') -await import('../../../../preload/browser-extension-document-targets') +await import('../../../../preload/browser/extensions/documents/browser-extension-document-targets') function page(id: string, workspaceId: string, overrides: Partial = {}): BrowserPage { return { @@ -276,31 +276,6 @@ beforeEach(() => { }) describe('restored browser pages without a guest are discarded tabs', () => { - it('lists every unloaded page of the caller’s profile after the loaded tabs, with a stable id', async () => { - const tabs = await query({}, [{ id: 11, url: 'https://live.test/' }]) - expect(tabs.map((entry) => [entry.url, entry.discarded, entry.status, entry.index])).toEqual([ - ['https://live.test/', false, 'loading', 0], - ['https://cold.test/', true, 'unloaded', 1], - ['https://notes.test/', true, 'unloaded', 3], - ['https://remote.test/', true, 'unloaded', 4] - ]) - const cold = tabs[1] - expect(cold).toMatchObject({ title: 'cold title', windowId: 1, active: false }) - expect(cold.id).toBeGreaterThanOrEqual(discardedTabs.FIRST_DISCARDED_BROWSER_TAB_ID) - expect((await query({ discarded: true })).map((entry) => entry.id)).toEqual( - tabs.slice(1).map((entry) => entry.id) - ) - expect(await call('get', [cold.id, null])).toMatchObject({ id: cold.id, discarded: true }) - // Electron's native query already filtered the loaded tabs by url. - expect((await query({ url: 'https://cold.test/*' })).map((entry) => entry.id)).toEqual([ - cold.id - ]) - // The other profile's extensions see only its own page. - expect((await query({}, [], mocks.sessions.work)).map((entry) => entry.url)).toEqual([ - 'https://job.test/' - ]) - }) - it('activates through the renderer, then is replaced by the loaded guest', async () => { const [cold] = await query({ discarded: true }) const coldId = cold.id! diff --git a/src/preload/api-types.ts b/src/preload/api-types.ts index 06f6052d657..c127099dcc9 100644 --- a/src/preload/api-types.ts +++ b/src/preload/api-types.ts @@ -179,68 +179,6 @@ export type PreloadApi = { speech: SpeechApi } -export type { - ClaudeUsageApi, - CodexUsageApi, - MuseUsageApi, - OpenCodeUsageApi -} from './api/agent-usage-api' -export type { AiVaultApi } from './api/ai-vault-api' -export type { AutomationsApi, ExternalAutomationManagerResult } from './api/automation-api' -export type { AppApi } from './api/app-api' -export type { - BrowserApi, - DetectedBrowserInfo, - DetectedBrowserProfileInfo -} from './api/browser/browser-api' -export type { EmulatorApi } from './api/emulator-api' -export type { ExportApi } from './api/filesystem-api' -export type { - NativeChatApi, - NativeChatAppendedMessages, - NativeChatAppendedPayload, - NativeChatReadSessionResult, - NativeChatSubscribeArgs, - NativeChatSubscriptionFrame -} from './api/native-chat-api' -export type { - PluginAutoUpdateEvent, - PluginHostCustomEditor, - PluginHostNewFile, - PluginHostInstallResult, - PluginHostInstallSource, - PluginHostListEntry, - PluginHostLogLine, - PluginHostPanel, - PluginHostStatus, - PluginMarketplaceHostInstallPreview, - PluginMarketplaceHostListing, - PluginMarketplaceHostSourceState -} from './api/plugin-host-api' -export type { - PreflightApi, - PreflightRuntimeContext, - PreflightStatus, - RefreshAgentsResult -} from './api/preflight-api' -export type { - PtyManagementApi, - PtyManagementDaemonCwdClass, - PtyManagementFolderAccessMismatch, - PtyManagementMacTccAttributionHealth, - PtyManagementSession, - PtyManagementSeveredDaemon, - PtyManagementSeveredDaemonRestartResult -} from './api/pty-management-api' - -export type { - DiagnosticsBundlePayload, - DiagnosticsStatusPayload, - DiagnosticsUploadPayload, - MemoryApi, - StatsApi -} from './api/telemetry-api' - declare global { // oxlint-disable-next-line typescript-eslint/consistent-type-definitions -- declaration merging requires interface interface Window { diff --git a/src/preload/api/app-bridge.ts b/src/preload/api/app-bridge.ts index d9b9bd19edf..b684fb55485 100644 --- a/src/preload/api/app-bridge.ts +++ b/src/preload/api/app-bridge.ts @@ -9,8 +9,11 @@ import { KEYBOARD_LAYOUT_CHANGED_CHANNEL, type KeyboardLayoutChangeEvent } from '../../shared/keyboard-layout-events' -import { prepareAndInvokeAppRestart } from '../renderer-restart-wiring' -import { awaitBeforeUnloadCheckpoint, startupDiagnosticsEnabled } from '../preload-runtime-support' +import { prepareAndInvokeAppRestart } from '../renderer/restart/renderer-restart-wiring' +import { + awaitBeforeUnloadCheckpoint, + startupDiagnosticsEnabled +} from '../renderer/renderer-runtime' import type { PreloadApi } from '../api-types' export const appApi = { diff --git a/src/preload/api/automations-bridge.ts b/src/preload/api/automations-bridge.ts index 1e6135996ce..78779f12381 100644 --- a/src/preload/api/automations-bridge.ts +++ b/src/preload/api/automations-bridge.ts @@ -1,6 +1,7 @@ import type { ExecutionHostId } from '../../shared/execution-host' import { ipcRenderer } from 'electron' -import type { ExternalAutomationManagerResult, PreloadApi } from '../api-types' +import type { ExternalAutomationManagerResult } from './automation-api' +import type { PreloadApi } from '../api-types' import type { AutomationOwnerRef } from '../../shared/automations/ownership/automation-owner-ref' import type { ScopedExternalManagerActionRequest, diff --git a/src/preload/api/browser/browser-bridge-guest-registration-and-downloads.ts b/src/preload/api/browser/browser-bridge-guest-registration-and-downloads.ts index 48d27c8f339..3f5d1245fc8 100644 --- a/src/preload/api/browser/browser-bridge-guest-registration-and-downloads.ts +++ b/src/preload/api/browser/browser-bridge-guest-registration-and-downloads.ts @@ -17,7 +17,7 @@ import type { BrowserPagePromptResult } from '../../../shared/browser/prompts/browser-page-prompt' import { readBrowserClientHostIdArgument } from '../../../shared/browser/client-hosting/browser-client-host-id-argument' -import { browserClientPageRendererRequests } from '../../preload-runtime-support' +import { browserClientPageRendererRequests } from '../../renderer/renderer-runtime' import type { PreloadApi } from '../../api-types' export const browserGuestRegistrationAndDownloadsApi = { diff --git a/src/preload/api/claude-usage-bridge.ts b/src/preload/api/claude-usage-bridge.ts index aee5e62425e..af96d18ef5b 100644 --- a/src/preload/api/claude-usage-bridge.ts +++ b/src/preload/api/claude-usage-bridge.ts @@ -1,5 +1,5 @@ -import { backendUsageTransport } from '../backend-port-client' -import { createUsageProviderApi } from '../usage-provider-api' +import { backendUsageTransport } from '../usage/backend-port-client' +import { createUsageProviderApi } from '../usage/usage-provider-api' import type { PreloadApi } from '../api-types' export const claudeUsageApi = createUsageProviderApi( diff --git a/src/preload/api/codex-usage-bridge.ts b/src/preload/api/codex-usage-bridge.ts index 0705d66ccb5..5caeda7115c 100644 --- a/src/preload/api/codex-usage-bridge.ts +++ b/src/preload/api/codex-usage-bridge.ts @@ -1,5 +1,5 @@ -import { backendUsageTransport } from '../backend-port-client' -import { createUsageProviderApi } from '../usage-provider-api' +import { backendUsageTransport } from '../usage/backend-port-client' +import { createUsageProviderApi } from '../usage/usage-provider-api' import type { PreloadApi } from '../api-types' export const codexUsageApi = createUsageProviderApi( diff --git a/src/preload/api/crash-reports-bridge.ts b/src/preload/api/crash-reports-bridge.ts index a77ad412eb7..9b57675f036 100644 --- a/src/preload/api/crash-reports-bridge.ts +++ b/src/preload/api/crash-reports-bridge.ts @@ -9,8 +9,8 @@ import type { } from '../../shared/crash-reporting' import type { RendererHeapStatistics } from '../../shared/renderer-heap-statistics' import type { RendererProcessMemory } from '../../shared/renderer-process-memory' -import { readRendererHeapStatistics } from '../renderer-heap-statistics-reader' -import { readRendererProcessMemory } from '../renderer-process-memory-reader' +import { readRendererHeapStatistics } from '../renderer/diagnostics/renderer-heap-statistics-reader' +import { readRendererProcessMemory } from '../renderer/diagnostics/renderer-process-memory-reader' import type { PreloadApi } from '../api-types' export const crashReportsApi = { diff --git a/src/preload/api/e2e-bridge.ts b/src/preload/api/e2e-bridge.ts index 900b17a9fcf..95ae25baabb 100644 --- a/src/preload/api/e2e-bridge.ts +++ b/src/preload/api/e2e-bridge.ts @@ -1,4 +1,4 @@ -import { preloadE2EConfig } from '../e2e-config' +import { preloadE2EConfig } from '../renderer/e2e-config' import type { PreloadApi } from '../api-types' export const e2eApi = { diff --git a/src/preload/gitlab.ts b/src/preload/api/gitlab.ts similarity index 96% rename from src/preload/gitlab.ts rename to src/preload/api/gitlab.ts index ed2904de2a0..0b393f98904 100644 --- a/src/preload/gitlab.ts +++ b/src/preload/api/gitlab.ts @@ -1,6 +1,6 @@ import { ipcRenderer } from 'electron' -import type { GitLabMRInvolvement } from '../shared/gitlab-types' -import type { TaskSourceContext } from '../shared/work-items/task-source-context' +import type { GitLabMRInvolvement } from '../../shared/gitlab-types' +import type { TaskSourceContext } from '../../shared/work-items/task-source-context' type GitLabRepoSelectorArgs = { repoPath: string diff --git a/src/preload/api/gl-bridge.ts b/src/preload/api/gl-bridge.ts deleted file mode 100644 index 3977536a838..00000000000 --- a/src/preload/api/gl-bridge.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { glApi } from '../gitlab' -import type { PreloadApi } from '../api-types' - -export const glApiBridge = glApi satisfies PreloadApi['gl'] diff --git a/src/preload/api/muse-usage-bridge.ts b/src/preload/api/muse-usage-bridge.ts index a741ddac1cb..833b0541434 100644 --- a/src/preload/api/muse-usage-bridge.ts +++ b/src/preload/api/muse-usage-bridge.ts @@ -1,5 +1,5 @@ -import { backendUsageTransport } from '../backend-port-client' -import { createUsageProviderApi } from '../usage-provider-api' +import { backendUsageTransport } from '../usage/backend-port-client' +import { createUsageProviderApi } from '../usage/usage-provider-api' import type { PreloadApi } from '../api-types' export const museUsageApi = createUsageProviderApi( diff --git a/src/preload/api/native-chat-bridge.ts b/src/preload/api/native-chat-bridge.ts index b4067ed7d09..fdea9c0b8c9 100644 --- a/src/preload/api/native-chat-bridge.ts +++ b/src/preload/api/native-chat-bridge.ts @@ -2,9 +2,9 @@ import { ipcRenderer } from 'electron' import type { NativeChatAppendedPayload, NativeChatReadSessionResult, - NativeChatSubscriptionFrame, - PreloadApi -} from '../api-types' + NativeChatSubscriptionFrame +} from './native-chat-api' +import type { PreloadApi } from '../api-types' import type { AgentType } from '../../shared/agent-status/contracts/agent-status-types' import { NATIVE_CHAT_VISUAL_GUEST_THEME_PUBLISH, diff --git a/src/preload/api/open-code-usage-bridge.ts b/src/preload/api/open-code-usage-bridge.ts index 8b583be211d..beb6ee468a9 100644 --- a/src/preload/api/open-code-usage-bridge.ts +++ b/src/preload/api/open-code-usage-bridge.ts @@ -1,5 +1,5 @@ -import { backendUsageTransport } from '../backend-port-client' -import { createUsageProviderApi } from '../usage-provider-api' +import { backendUsageTransport } from '../usage/backend-port-client' +import { createUsageProviderApi } from '../usage/usage-provider-api' import type { PreloadApi } from '../api-types' export const openCodeUsageApi = createUsageProviderApi( diff --git a/src/preload/api/orca-profiles-bridge.ts b/src/preload/api/orca-profiles-bridge.ts index f144c9d7697..063de33913f 100644 --- a/src/preload/api/orca-profiles-bridge.ts +++ b/src/preload/api/orca-profiles-bridge.ts @@ -8,8 +8,8 @@ import { type SwitchOrcaProfileResult, type TransferOrcaProfileProjectResult } from '../../shared/orca-profiles' -import { prepareAndInvokeAppRestart } from '../renderer-restart-wiring' -import { awaitBeforeUnloadCheckpoint } from '../preload-runtime-support' +import { prepareAndInvokeAppRestart } from '../renderer/restart/renderer-restart-wiring' +import { awaitBeforeUnloadCheckpoint } from '../renderer/renderer-runtime' export const orcaProfilesApi = { list: () => ipcRenderer.invoke('orcaProfiles:list'), diff --git a/src/preload/api/platform-bridge.test.ts b/src/preload/api/platform-bridge.test.ts deleted file mode 100644 index 1e67947d77c..00000000000 --- a/src/preload/api/platform-bridge.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { platformApi } from './platform-bridge' - -const mocks = vi.hoisted(() => ({ getLinuxDisplayServer: vi.fn(() => null) })) - -vi.mock('../preload-runtime-support', () => ({ - getLinuxDisplayServer: mocks.getLinuxDisplayServer -})) - -// Electron declares getSystemVersion as required on NodeJS.Process; Node does not have it. -const mutableProcess = process as unknown as { getSystemVersion?: () => string } - -async function loadPlatformApi(): Promise { - vi.resetModules() - return (await import('./platform-bridge')).platformApi -} - -describe('platformApi.get', () => { - beforeEach(() => { - mocks.getLinuxDisplayServer.mockClear() - }) - - afterEach(() => { - delete mutableProcess.getSystemVersion - }) - - it('resolves the immutable payload once and returns the identical object', async () => { - const platformApi = await loadPlatformApi() - const getSystemVersion = vi.fn(() => '25.3.0') - mutableProcess.getSystemVersion = getSystemVersion - - const first = platformApi.get() - expect(platformApi.get()).toBe(first) - expect(Object.isFrozen(first)).toBe(true) - - expect(getSystemVersion).toHaveBeenCalledTimes(1) - expect(mocks.getLinuxDisplayServer).toHaveBeenCalledTimes(1) - expect(first.platform).toBe(process.platform) - expect(first.arch).toBe(process.arch) - expect(first.osRelease).toBe('25.3.0') - }) - - it('resolves nothing before the first get, keeping preload startup free', async () => { - await loadPlatformApi() - - expect(mocks.getLinuxDisplayServer).not.toHaveBeenCalled() - }) -}) diff --git a/src/preload/api/platform-bridge.ts b/src/preload/api/platform-bridge.ts index 6ea1102f598..534cd0d80cc 100644 --- a/src/preload/api/platform-bridge.ts +++ b/src/preload/api/platform-bridge.ts @@ -1,4 +1,4 @@ -import { getLinuxDisplayServer } from '../preload-runtime-support' +import { getLinuxDisplayServer } from '../renderer/renderer-runtime' import { parseWindowMaterialArgument } from '../../shared/window-material' import type { PreloadApi } from '../api-types' diff --git a/src/preload/api/plugins-bridge.ts b/src/preload/api/plugins-bridge.ts index 348cbab6e6d..54fc1c5eb61 100644 --- a/src/preload/api/plugins-bridge.ts +++ b/src/preload/api/plugins-bridge.ts @@ -20,10 +20,10 @@ import type { PluginAutoUpdateEvent, PluginHostInstallResult, PluginHostInstallSource, - PluginHostListEntry, - PluginHostLogLine, - PreloadApi -} from '../api-types' + PluginHostLogLine +} from './plugin-host-api' +import type { PluginHostListEntry } from '../../shared/plugins/plugin-list-entry' +import type { PreloadApi } from '../api-types' export const pluginsApi = { list: (): Promise => ipcRenderer.invoke('plugins:list'), diff --git a/src/preload/api/preflight-bridge.ts b/src/preload/api/preflight-bridge.ts index ca0f030c155..152d757247d 100644 --- a/src/preload/api/preflight-bridge.ts +++ b/src/preload/api/preflight-bridge.ts @@ -1,5 +1,6 @@ import { ipcRenderer } from 'electron' -import type { PreflightRuntimeContext, PreloadApi, RefreshAgentsResult } from '../api-types' +import type { PreflightRuntimeContext, RefreshAgentsResult } from './preflight-api' +import type { PreloadApi } from '../api-types' import type { ZCodeInteractiveCapability } from '../../shared/zcode-missing-tui' export const preflightApi = { diff --git a/src/preload/api/runtime-bridge.ts b/src/preload/api/runtime-bridge.ts index eaf6c4e7eff..96515e0fe59 100644 --- a/src/preload/api/runtime-bridge.ts +++ b/src/preload/api/runtime-bridge.ts @@ -8,7 +8,7 @@ import type { } from '../../shared/runtime-types' import type { RuntimeTerminalDriverState } from '../../shared/runtime-session-contracts' import type { RuntimeRpcResponse } from '../../shared/runtime-rpc-envelope' -import type { RuntimeEnvironmentSubscriptionHandle } from '../runtime-environment-subscriptions' +import type { RuntimeEnvironmentSubscriptionHandle } from '../runtime/runtime-environment-subscriptions' import type { PreloadApi } from '../api-types' export const runtimeApi = { diff --git a/src/preload/api/runtime-environments-bridge.ts b/src/preload/api/runtime-environments-bridge.ts index 715605b216f..517b443e774 100644 --- a/src/preload/api/runtime-environments-bridge.ts +++ b/src/preload/api/runtime-environments-bridge.ts @@ -22,7 +22,7 @@ import { subscribeCrewAttemptFromPreload, cancelRuntimeEnvironmentSubscriptionFromPreload, type RuntimeEnvironmentSubscriptionHandle -} from '../runtime-environment-subscriptions' +} from '../runtime/runtime-environment-subscriptions' import type { PreloadApi } from '../api-types' import { managedOrcadApi } from './managed-orcad-api' import { getWslHostsApi } from './wsl-hosts-api' diff --git a/src/preload/ssh-authority-forwarding.test.ts b/src/preload/api/ssh-authority-forwarding.test.ts similarity index 79% rename from src/preload/ssh-authority-forwarding.test.ts rename to src/preload/api/ssh-authority-forwarding.test.ts index 70a90a4a4b2..4594039a0ca 100644 --- a/src/preload/ssh-authority-forwarding.test.ts +++ b/src/preload/api/ssh-authority-forwarding.test.ts @@ -1,11 +1,11 @@ import { afterEach, beforeEach, describe, expect, expectTypeOf, it, vi } from 'vitest' -import type { PreloadApi } from './api-types' -import type { SshConnectionState, SshProviderEpoch } from '../shared/ssh-types' +import type { PreloadApi } from '../api-types' +import type { SshConnectionState, SshProviderEpoch } from '../../shared/ssh-types' import type { HostQualifiedDetectedWorktreeResult, ListDetectedWorktreesArgs -} from '../shared/detected-worktree-provider-contract' -import type { DetectedWorktreeListResult } from '../shared/worktree/types' +} from '../../shared/detected-worktree-provider-contract' +import type { DetectedWorktreeListResult } from '../../shared/worktree/types' function listDetectedVariableTypeProbe(api: PreloadApi, args: ListDetectedWorktreesArgs) { return api.worktrees.listDetected(args) @@ -66,30 +66,6 @@ describe('native preload SSH authority forwarding', () => { >() }) - it('forwards host-qualified metadata-only worktree reads', async () => { - await import('./index') - const api = exposeInMainWorld.mock.calls.find(([name]) => name === 'api')?.[1] as PreloadApi - const args = { repoId: 'repo-1', executionHostId: 'ssh:ssh-1' as const } - - await api.worktrees.listKnownForExecutionHost?.(args) - - expect(invoke).toHaveBeenCalledWith('worktrees:listKnownForExecutionHost', args) - }) - - it('forwards metadata retirement for authoritatively removed worktrees', async () => { - await import('./index') - const api = exposeInMainWorld.mock.calls.find(([name]) => name === 'api')?.[1] as PreloadApi - const args = { - repoId: 'repo-1', - executionHostId: 'ssh:ssh-1' as const, - worktreeIds: ['repo-1::/remote/deleted'] - } - - await api.worktrees.forgetRemovedForExecutionHost?.(args) - - expect(invoke).toHaveBeenCalledWith('worktrees:forgetRemovedForExecutionHost', args) - }) - it('forwards full-pair get and push states without cloning away authority', async () => { const state: SshConnectionState = { targetId: 'ssh-1', @@ -100,7 +76,7 @@ describe('native preload SSH authority forwarding', () => { connectionGeneration: 29 } invoke.mockResolvedValueOnce(state) - await import('./index') + await import('../index') const api = exposeInMainWorld.mock.calls.find(([name]) => name === 'api')?.[1] as PreloadApi await expect(api.ssh.getState({ targetId: 'ssh-1' })).resolves.toEqual(state) @@ -126,7 +102,7 @@ describe('native preload SSH authority forwarding', () => { providerEpoch: 'partial-provider-epoch' } as SshConnectionState invoke.mockResolvedValueOnce(partialState) - await import('./index') + await import('../index') const api = exposeInMainWorld.mock.calls.find(([name]) => name === 'api')?.[1] as PreloadApi const returned = await api.ssh.getState({ targetId: 'ssh-1' }) @@ -156,7 +132,7 @@ describe('native preload SSH authority forwarding', () => { }) it('drops malformed full authority before it reaches the renderer', async () => { - await import('./index') + await import('../index') const api = exposeInMainWorld.mock.calls.find(([name]) => name === 'api')?.[1] as PreloadApi const onStateChanged = vi.fn() api.ssh.onStateChanged(onStateChanged) diff --git a/src/preload/api/ui-bridge-tab-and-browser-commands.ts b/src/preload/api/ui-bridge-tab-and-browser-commands.ts index 3b7a092224a..ea87e6e2b61 100644 --- a/src/preload/api/ui-bridge-tab-and-browser-commands.ts +++ b/src/preload/api/ui-bridge-tab-and-browser-commands.ts @@ -1,5 +1,5 @@ import { ipcRenderer } from 'electron' -import { admitCloseActiveTabPayload } from '../close-active-tab-payload-admission' +import { admitCloseActiveTabPayload } from '../renderer/window-commands/close-active-tab-payload-admission' import type { CloseActiveTabPayload } from '../api/ui-command-event-api' import type { BrowserHistoryNavigateCommand, @@ -11,7 +11,7 @@ import type { WorktreeDefaultTabsLaunch, WorktreeSetupLaunch } from '../../shared/worktree/launch-types' -import { browserFindSubscriptions } from '../preload-runtime-support' +import { browserFindSubscriptions } from '../renderer/renderer-runtime' import type { PreloadApi } from '../api-types' export const uiTabAndBrowserCommandsApi = { diff --git a/src/preload/api/updater-bridge.ts b/src/preload/api/updater-bridge.ts index 325d5ace205..af55a0967d0 100644 --- a/src/preload/api/updater-bridge.ts +++ b/src/preload/api/updater-bridge.ts @@ -1,7 +1,7 @@ import { ipcRenderer } from 'electron' import type { UpdateStatus } from '../../shared/update-status-types' -import { prepareAndInvokeUpdaterInstall } from '../renderer-restart-wiring' -import { awaitBeforeUnloadCheckpoint, updaterQuitAbortRelay } from '../preload-runtime-support' +import { prepareAndInvokeUpdaterInstall } from '../renderer/restart/renderer-restart-wiring' +import { awaitBeforeUnloadCheckpoint, updaterQuitAbortRelay } from '../renderer/renderer-runtime' import type { PreloadApi } from '../api-types' export const updaterApi = { diff --git a/src/preload/app-restart-checkpoint-routing.test.ts b/src/preload/app-restart-checkpoint-routing.test.ts deleted file mode 100644 index a62da40ea76..00000000000 --- a/src/preload/app-restart-checkpoint-routing.test.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { PreloadApi } from './api-types' -import { - ORCA_APP_RESTART_ABORTED_EVENT, - ORCA_APP_RESTART_STARTED_EVENT -} from '../shared/updater-renderer-events' -import { KEYBOARD_LAYOUT_CHANGED_CHANNEL } from '../shared/keyboard-layout-events' - -const { exposeInMainWorld, invoke, on, removeListener, send, sendSync, getPathForFile } = - vi.hoisted(() => ({ - exposeInMainWorld: vi.fn(), - invoke: vi.fn(), - on: vi.fn(), - removeListener: vi.fn(), - send: vi.fn(), - sendSync: vi.fn(), - getPathForFile: vi.fn(() => '/files/notes.txt') - })) - -vi.mock('electron', () => ({ - contextBridge: { exposeInMainWorld }, - ipcRenderer: { invoke, on, removeListener, send, sendSync }, - webFrame: { - getZoomFactor: vi.fn(() => 1), - setZoomFactor: vi.fn(), - setVisualZoomLevelLimits: vi.fn() - }, - webUtils: { getPathForFile } -})) - -describe('native preload destructive app actions', () => { - const originalContextIsolated = Object.getOwnPropertyDescriptor(process, 'contextIsolated') - let eventTarget: EventTarget - - beforeEach(() => { - vi.resetModules() - exposeInMainWorld.mockReset() - invoke.mockReset() - on.mockReset() - removeListener.mockReset() - send.mockReset() - sendSync.mockReset() - Object.defineProperty(process, 'contextIsolated', { configurable: true, value: true }) - eventTarget = new EventTarget() - vi.stubGlobal('window', eventTarget) - vi.stubGlobal('document', { addEventListener: vi.fn() }) - }) - - afterEach(() => { - vi.unstubAllGlobals() - if (originalContextIsolated) { - Object.defineProperty(process, 'contextIsolated', originalContextIsolated) - } else { - Reflect.deleteProperty(process, 'contextIsolated') - } - }) - - const loadApi = async (): Promise => { - await import('./index') - return exposeInMainWorld.mock.calls.find(([name]) => name === 'api')?.[1] as PreloadApi - } - - it('exposes synchronous file path resolution and a window-local preparation invoke', async () => { - const api = await loadApi() - const file = new File(['x'], 'notes.txt') - expect(api).not.toHaveProperty('getPathForFile') - expect(api.fs.getPathForFile?.(file)).toBe('/files/notes.txt') - expect(getPathForFile).toHaveBeenCalledWith(file) - const request = { paths: ['/files/notes.txt'], consumer: 'agent' as const } - invoke.mockResolvedValue({ paths: request.paths, failures: [] }) - await expect(api.fs.prepareDroppedPaths(request)).resolves.toEqual({ - paths: request.paths, - failures: [] - }) - expect(invoke).toHaveBeenCalledExactlyOnceWith('fs:prepareDroppedPaths', request) - }) - - for (const action of ['reload', 'relaunch'] as const) { - it(`prepares and awaits durability before ${action}`, async () => { - const api = await loadApi() - const calls: string[] = [] - eventTarget.addEventListener(ORCA_APP_RESTART_STARTED_EVENT, () => calls.push('prepared')) - invoke.mockImplementation(async (channel: string) => { - calls.push(channel) - return channel === 'app:await-before-unload-checkpoint' ? { ok: true } : undefined - }) - - await api.app[action]() - - expect(calls).toEqual(['prepared', 'app:await-before-unload-checkpoint', `app:${action}`]) - }) - - it(`refuses ${action} when the durable checkpoint fails`, async () => { - const api = await loadApi() - const aborted = vi.fn() - eventTarget.addEventListener(ORCA_APP_RESTART_ABORTED_EVENT, aborted) - invoke.mockImplementation(async (channel: string) => - channel === 'app:await-before-unload-checkpoint' ? { ok: false } : undefined - ) - - await expect(api.app[action]()).rejects.toThrow( - 'Failed to persist renderer state before unload.' - ) - - expect(invoke).not.toHaveBeenCalledWith(`app:${action}`) - expect(aborted).toHaveBeenCalledTimes(1) - }) - } - - it('exposes the durable checkpoint join the lazy-chunk recovery reload depends on', async () => { - const api = await loadApi() - invoke.mockResolvedValue({ ok: true }) - - await expect(api.app.awaitBeforeUnloadCheckpoint()).resolves.toBeUndefined() - expect(invoke).toHaveBeenCalledWith('app:await-before-unload-checkpoint') - - invoke.mockResolvedValue({ ok: false }) - - await expect(api.app.awaitBeforeUnloadCheckpoint()).rejects.toThrow( - 'Failed to persist renderer state before unload.' - ) - }) - - it('preserves both macOS keyboard preload adapters', async () => { - const api = await loadApi() - invoke.mockResolvedValue(undefined) - - await api.app.getMacCapturedDigitRowChords() - await api.app.getKeyboardLayoutSnapshot() - const onKeyboardLayoutChanged = vi.fn() - const unsubscribe = api.app.onKeyboardLayoutChanged(onKeyboardLayoutChanged) - const listener = on.mock.calls.find( - ([channel]) => channel === KEYBOARD_LAYOUT_CHANGED_CHANNEL - )?.[1] as ((event: unknown, payload: unknown) => void) | undefined - const payload = { phase: 'invalidated', generation: 1 } - listener?.({}, payload) - unsubscribe() - - expect(invoke).toHaveBeenCalledWith('app:getMacCapturedDigitRowChords') - expect(invoke).toHaveBeenCalledWith('app:getKeyboardLayoutSnapshot') - expect(onKeyboardLayoutChanged).toHaveBeenCalledExactlyOnceWith(payload) - expect(removeListener).toHaveBeenCalledWith(KEYBOARD_LAYOUT_CHANGED_CHANNEL, listener) - }) - - it('awaits renderer durability before profile maintenance and preserves its result', async () => { - const api = await loadApi() - const started = vi.fn() - eventTarget.addEventListener(ORCA_APP_RESTART_STARTED_EVENT, started) - let finishCheckpoint = (_result: { ok: boolean }): void => {} - const checkpoint = new Promise((resolve) => { - finishCheckpoint = resolve - }) - const result = { status: 'relaunching' } - invoke.mockImplementation((channel: string) => - channel === 'app:await-before-unload-checkpoint' ? checkpoint : Promise.resolve(result) - ) - - const switching = api.orcaProfiles.switchProfile({ profileId: 'target' }) - await vi.waitFor(() => - expect(invoke).toHaveBeenCalledWith('app:await-before-unload-checkpoint') - ) - expect(started).toHaveBeenCalledOnce() - expect(invoke).not.toHaveBeenCalledWith('orcaProfiles:switch', expect.anything()) - finishCheckpoint({ ok: true }) - await expect(switching).resolves.toBe(result) - expect(invoke).toHaveBeenLastCalledWith('orcaProfiles:switch', { profileId: 'target' }) - }) - - it.each(['checkpoint-failed', 'switch-failed', 'already-active'])( - 'resets restart preparation when profile switching returns %s', - async (outcome) => { - const api = await loadApi() - const aborted = vi.fn() - eventTarget.addEventListener(ORCA_APP_RESTART_ABORTED_EVENT, aborted) - invoke.mockImplementation(async (channel: string) => { - if (channel === 'app:await-before-unload-checkpoint') { - return { ok: outcome !== 'checkpoint-failed' } - } - if (outcome === 'switch-failed') { - throw new Error('switch failed') - } - return { status: 'already-active' } - }) - const switching = api.orcaProfiles.switchProfile({ profileId: 'target' }) - await (outcome === 'already-active' - ? expect(switching).resolves.toEqual({ status: 'already-active' }) - : expect(switching).rejects.toThrow()) - expect(aborted).toHaveBeenCalledOnce() - if (outcome === 'checkpoint-failed') { - expect(invoke).not.toHaveBeenCalledWith('orcaProfiles:switch', expect.anything()) - } - } - ) - - it.each(['move', 'copy', 'inactive', 'duplicate', 'recovery'] as const)( - 'prepares only a potentially relaunching project transfer: %s', - async (outcome) => { - const api = await loadApi() - const started = vi.fn() - const aborted = vi.fn() - eventTarget.addEventListener(ORCA_APP_RESTART_STARTED_EVENT, started) - eventTarget.addEventListener(ORCA_APP_RESTART_ABORTED_EVENT, aborted) - const args = { - sourceProfileId: outcome === 'inactive' ? 'inactive' : 'active', - targetProfileId: 'target', - repoId: 'repo', - mode: outcome === 'copy' ? ('copy' as const) : ('move' as const) - } - const result = - outcome === 'duplicate' - ? { status: 'duplicate-target' } - : { status: 'transferred', willRelaunch: outcome === 'move' } - invoke.mockImplementation(async (channel: string) => { - if (channel === 'orcaProfiles:list') { - return { activeProfileId: 'active' } - } - if (channel === 'app:await-before-unload-checkpoint') { - return { ok: true } - } - if (outcome === 'recovery') { - const listener = on.mock.calls.find(([name]) => name === 'app:restart-committed')?.[1] - expect(listener).toBeTypeOf('function') - listener() - throw new Error('move requires recovery') - } - return result - }) - - const transfer = api.orcaProfiles.transferProject(args) - await (outcome === 'recovery' - ? expect(transfer).rejects.toThrow('move requires recovery') - : expect(transfer).resolves.toBe(result)) - const needsPreparation = outcome !== 'copy' && outcome !== 'inactive' - expect(started).toHaveBeenCalledTimes(needsPreparation ? 1 : 0) - expect(aborted).toHaveBeenCalledTimes(outcome === 'duplicate' ? 1 : 0) - expect(invoke).toHaveBeenLastCalledWith('orcaProfiles:transferProject', args) - } - ) -}) diff --git a/src/preload/browser-extension-content-script-api.ts b/src/preload/browser-extension-content-script-api.ts index 70617263a18..3e5c0779803 100644 --- a/src/preload/browser-extension-content-script-api.ts +++ b/src/preload/browser-extension-content-script-api.ts @@ -1,12 +1,12 @@ import { createContentScriptRelayBridge, installContentScriptRelayFilter -} from './browser-extension-content-script-relay' -import { installBrowserExtensionInertApis } from './browser-extension-inert-apis' +} from './browser/extensions/messaging/browser-extension-content-script-relay' +import { installBrowserExtensionInertApis } from './browser/extensions/api/browser-extension-inert-apis' import { installBrowserExtensionStorageSync, PRELOAD_STORAGE_SYNC_CONSTANTS -} from './browser-extension-storage-sync' +} from './browser/extensions/storage/browser-extension-storage-sync' /** * Orca's content script, bundled into one standalone file that Orca's manifest rewrite puts first diff --git a/src/preload/browser-extension-native-messaging.test.ts b/src/preload/browser-extension-native-messaging.test.ts deleted file mode 100644 index fdfc7c30987..00000000000 --- a/src/preload/browser-extension-native-messaging.test.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { runInNewContext } from 'node:vm' -import { describe, expect, it, vi } from 'vitest' -import type { BrowserExtensionInvokeReply } from './browser-extension-main-world' -import { - installBrowserExtensionNativeMessaging, - PRELOAD_NATIVE_MESSAGING_NAMESPACE, - type BrowserExtensionNativeMessagingContext -} from './browser-extension-native-messaging' - -type Reply = BrowserExtensionInvokeReply -type Call = { method: string; args: unknown[] } - -function isRecord(value: unknown): value is Record { - return value !== null && typeof value === 'object' -} - -/** Calls `path` (e.g. 'onMessage.addListener') on `owner` with its parent as `this`. */ -function call(owner: unknown, path: string, ...args: unknown[]): unknown { - const keys = path.split('.') - const leaf = keys.pop() ?? '' - const parent = keys.reduce( - (value, key) => (isRecord(value) ? value[key] : undefined), - owner - ) - const fn = isRecord(parent) ? parent[leaf] : undefined - if (typeof fn !== 'function') { - throw new Error(`${path} is not a function`) - } - return fn.apply(parent, args) -} - -/** Runs the installer from its source text, as contextBridge.executeInMainWorld does. */ -function install( - permissions: Record, - context: BrowserExtensionNativeMessagingContext = 'page' -) { - const runtime: Record = { getManifest: () => permissions } - const browserRuntime: Record = {} - const calls: Call[] = [] - const listens: unknown[][] = [] - const pending = new Map void>() - let dispatch: ((namespace: string, event: string, args: unknown[]) => void) | null = null - const bridge = { - call: (namespace: string, method: string, args: unknown[]) => { - expect(namespace).toBe(PRELOAD_NATIVE_MESSAGING_NAMESPACE) - calls.push({ method, args }) - return new Promise((resolve) => pending.set(`${method}:${String(args[0])}`, resolve)) - }, - listen: (namespace: string, event: string, active: boolean) => - listens.push([namespace, event, active]), - onEvent: (handler: typeof dispatch) => { - dispatch = handler - }, - passesFilter: () => true - } - const error = vi.fn() - const realm = { - chrome: { runtime }, - browser: { runtime: browserRuntime }, - console: { error }, - JSON, - Math, - Date, - Promise - } - runInNewContext(`(${installBrowserExtensionNativeMessaging.toString()})(bridge, context)`, { - ...realm, - globalThis: realm, - bridge, - context - }) - return { - runtime, - browserRuntime, - calls, - listens, - error, - /** The id the installer chose for the n-th call's port. */ - portId: (index = 0) => String(calls[index]?.args[0]), - lastError: () => (isRecord(runtime.lastError) ? runtime.lastError.message : undefined), - reply: (key: string, value: Reply) => pending.get(key)?.(value), - emit: (args: unknown[]) => dispatch?.(PRELOAD_NATIVE_MESSAGING_NAMESPACE, 'onPortEvent', args) - } -} - -const tick = () => new Promise((resolve) => setTimeout(resolve, 0)) - -describe('chrome.runtime native messaging in extension contexts', () => { - it('queues posts until the host runs and routes replies by port', async () => { - const page = install({ permissions: ['nativeMessaging'] }) - const port = call(page.runtime, 'connectNative', 'com.example.vault') - const received: unknown[][] = [] - call(port, 'onMessage.addListener', (message: unknown, from: unknown) => - received.push([message, from === port]) - ) - call(port, 'postMessage', { hello: 'é' }) - expect(isRecord(port) && port.name).toBe('') - const id = page.portId() - expect(page.calls).toEqual([{ method: 'connect', args: [id, 'com.example.vault'] }]) - // A reply can beat the connect answer; it still reaches the port. - page.emit([id, 'message', '{"early":true}']) - page.reply(`connect:${id}`, { ok: true, value: undefined }) - await tick() - expect(page.calls[1]).toEqual({ method: 'postMessage', args: [id, '{"hello":"é"}'] }) - page.emit(['another-port', 'message', '{"stray":1}']) - expect(received).toEqual([[{ early: true }, true]]) - expect(page.listens).toEqual([]) - }) - - it('fires onDisconnect with runtime.lastError set only during the call', async () => { - const page = install({ permissions: ['nativeMessaging'] }) - const port = call(page.runtime, 'connectNative', 'com.example.missing') - const seen: unknown[] = [] - call(port, 'onDisconnect.addListener', () => seen.push(page.lastError())) - page.reply(`connect:${page.portId()}`, { - ok: false, - error: 'Specified native messaging host not found.' - }) - await tick() - expect(seen).toEqual(['Specified native messaging host not found.']) - expect(page.runtime.lastError).toBeUndefined() - expect(() => call(port, 'postMessage', 1)).toThrow( - 'Attempting to use a disconnected port object' - ) - }) - - it('lets main release a port the extension dropped while it was starting', async () => { - const worker = install({ permissions: ['nativeMessaging'] }, 'worker') - const port = call(worker.runtime, 'connectNative', 'com.example.vault') - expect(worker.listens).toEqual([[PRELOAD_NATIVE_MESSAGING_NAMESPACE, 'onPortEvent', true]]) - call(port, 'disconnect') - worker.reply(`connect:${worker.portId()}`, { ok: true, value: undefined }) - await tick() - expect(worker.calls.at(-1)).toEqual({ method: 'disconnect', args: [worker.portId()] }) - }) - - it('answers sendNativeMessage with a promise, or a callback that sees lastError', async () => { - const page = install({ permissions: ['nativeMessaging'] }) - const promised = call(page.runtime, 'sendNativeMessage', 'com.example.vault', { q: 1 }) - expect(page.calls[0]).toEqual({ - method: 'sendNativeMessage', - args: ['com.example.vault', '{"q":1}'] - }) - page.reply('sendNativeMessage:com.example.vault', { ok: true, value: '{"a":2}' }) - await expect(promised).resolves.toEqual({ a: 2 }) - - const results: unknown[] = [] - call(page.runtime, 'sendNativeMessage', 'com.example.gone', 1, (response: unknown) => - results.push([response, page.lastError()]) - ) - await tick() - page.reply('sendNativeMessage:com.example.gone', { - ok: false, - error: 'Native host has exited.' - }) - await tick() - expect(results).toEqual([[undefined, 'Native host has exited.']]) - }) -}) diff --git a/src/preload/browser-extension-preload.ts b/src/preload/browser-extension-preload.ts index 43aa35d0415..82df2ff05b1 100644 --- a/src/preload/browser-extension-preload.ts +++ b/src/preload/browser-extension-preload.ts @@ -3,30 +3,30 @@ import { BROWSER_EXTENSION_API_SURFACE, PRELOAD_BROWSER_EXTENSION_EVENT_CHANNEL, PRELOAD_BROWSER_EXTENSION_INVOKE_CHANNEL -} from './browser-extension-api-surface' +} from './browser/extensions/api/browser-extension-api-surface' import { installBrowserExtensionApis, type BrowserExtensionInvokeReply, type BrowserExtensionMainWorldBridge -} from './browser-extension-main-world' +} from './browser/extensions/api/browser-extension-main-world' import { installBrowserExtensionDocumentTargets, PRELOAD_FIRST_DISCARDED_TAB_ID -} from './browser-extension-document-targets' -import { passesBrowserExtensionEventFilter } from './browser-extension-event-filters' -import { installBrowserExtensionInertApis } from './browser-extension-inert-apis' -import { installContentScriptRelayFilter } from './browser-extension-content-script-relay' -import { installBrowserExtensionNativeMessaging } from './browser-extension-native-messaging' -import { installBrowserExtensionFrameLifetime } from './browser-extension-frame-lifetime' -import { installBrowserExtensionStorageRelay } from './browser-extension-storage-relay' +} from './browser/extensions/documents/browser-extension-document-targets' +import { passesBrowserExtensionEventFilter } from './browser/extensions/api/browser-extension-event-filters' +import { installBrowserExtensionInertApis } from './browser/extensions/api/browser-extension-inert-apis' +import { installContentScriptRelayFilter } from './browser/extensions/messaging/browser-extension-content-script-relay' +import { installBrowserExtensionNativeMessaging } from './browser/extensions/messaging/browser-extension-native-messaging' +import { installBrowserExtensionFrameLifetime } from './browser/extensions/api/browser-extension-frame-lifetime' +import { installBrowserExtensionStorageRelay } from './browser/extensions/storage/browser-extension-storage-relay' import { installBrowserExtensionStorageSync, PRELOAD_STORAGE_SYNC_CONSTANTS -} from './browser-extension-storage-sync' +} from './browser/extensions/storage/browser-extension-storage-sync' import { CHROME_WEB_STORE_ORIGIN, installChromeWebStoreBridge -} from './browser-extension-web-store-bridge' +} from './browser/extensions/web-store/browser-extension-web-store-bridge' const electron: { contextBridge: ContextBridge; ipcRenderer: IpcRenderer } = require('electron') const { contextBridge, ipcRenderer } = electron diff --git a/src/preload/browser-extension-storage-sync.test.ts b/src/preload/browser-extension-storage-sync.test.ts deleted file mode 100644 index 72a2bf1a8a7..00000000000 --- a/src/preload/browser-extension-storage-sync.test.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { runInNewContext } from 'node:vm' -import { describe, expect, it, vi } from 'vitest' -import type { BrowserExtensionInvokeReply } from './browser-extension-main-world' -import { - installBrowserExtensionStorageSync, - PRELOAD_STORAGE_SYNC_CONSTANTS, - PRELOAD_STORAGE_SYNC_NAMESPACE -} from './browser-extension-storage-sync' - -type Runtime = { lastError?: { message: string } } - -function nativeEvent() { - const listeners = new Set() - return { - listeners, - addListener: vi.fn((callback: unknown) => listeners.add(callback)), - removeListener: vi.fn((callback: unknown) => listeners.delete(callback)) - } -} - -/** Runs the installer from its source text, as contextBridge.executeInMainWorld does. */ -function install( - reply: (method: string, args: unknown[]) => BrowserExtensionInvokeReply, - forward?: (changes: unknown, from?: string) => void -) { - const runtime: Runtime = {} - const native = nativeEvent() - const nativeSync = { get: vi.fn(), set: vi.fn() } - const chrome = { runtime, storage: { local: {}, sync: nativeSync, onChanged: native } } - let dispatch: ((namespace: string, event: string, args: unknown[]) => void) | null = null - const calls: unknown[][] = [] - const listens: unknown[][] = [] - const bridge = { - call: async (namespace: string, method: string, args: unknown[]) => { - calls.push([namespace, method, args]) - return reply(method, args) - }, - listen: (namespace: string, event: string, active: boolean) => - listens.push([namespace, event, active]), - onEvent: (handler: typeof dispatch) => { - dispatch = handler - }, - passesFilter: () => true - } - const realm = { chrome, console, JSON, Promise } - const relay: unknown = runInNewContext( - `(${installBrowserExtensionStorageSync.toString()})(bridge, constants, forward)`, - { - ...realm, - globalThis: realm, - bridge, - constants: PRELOAD_STORAGE_SYNC_CONSTANTS, - forward - } - ) - return { - relay: (method: string, args: unknown[], from?: string): unknown => - typeof relay === 'function' ? relay(method, args, from) : undefined, - chrome, - native, - nativeSync, - calls, - listens, - emit: (changes: unknown) => dispatch?.(PRELOAD_STORAGE_SYNC_NAMESPACE, 'onChanged', [changes]) - } -} - -function isRecord(value: unknown): value is Record { - return value !== null && typeof value === 'object' -} - -function call(root: unknown, path: string, ...args: unknown[]): unknown { - const keys = path.split('.') - const name = keys.pop() ?? '' - const owner = keys.reduce( - (value, key) => (isRecord(value) ? value[key] : undefined), - root - ) - const fn = isRecord(owner) ? owner[name] : null - if (typeof fn !== 'function') { - throw new Error(`${path} is not a function`) - } - return fn.call(owner, ...args) -} - -describe('installBrowserExtensionStorageSync', () => { - it('replaces the disabled native area with calls to main, as promises or callbacks', async () => { - const page = install((method) => - method === 'get' ? { ok: true, value: { theme: 'dark' } } : { ok: true, value: undefined } - ) - expect(page.chrome.storage.sync).not.toBe(page.nativeSync) - expect(page.chrome.storage.sync).toMatchObject({ QUOTA_BYTES: 102_400, MAX_ITEMS: 512 }) - - await expect(call(page.chrome, 'storage.sync.get', 'theme')).resolves.toEqual({ - theme: 'dark' - }) - const got = await new Promise((resolve) => call(page.chrome, 'storage.sync.get', resolve)) - expect(got).toEqual({ theme: 'dark' }) - const setDone = vi.fn() - expect( - call(page.chrome, 'storage.sync.set', { at: new Date(0), skip: undefined }, setDone) - ).toBeUndefined() - await vi.waitFor(() => expect(setDone).toHaveBeenCalledWith()) - await expect( - call(page.chrome, 'storage.sync.setAccessLevel', { - accessLevel: 'TRUSTED_AND_UNTRUSTED_CONTEXTS' - }) - ).resolves.toBeUndefined() - await expect( - call(page.chrome, 'storage.sync.setAccessLevel', { accessLevel: 'TRUSTED_CONTEXTS' }) - ).rejects.toThrow('This StorageArea is not available for setting access level') - - expect(page.calls).toEqual([ - [PRELOAD_STORAGE_SYNC_NAMESPACE, 'get', ['theme']], - [PRELOAD_STORAGE_SYNC_NAMESPACE, 'get', []], - [PRELOAD_STORAGE_SYNC_NAMESPACE, 'set', [{ at: '1970-01-01T00:00:00.000Z' }]] - ]) - expect(page.nativeSync.get).not.toHaveBeenCalled() - expect(page.nativeSync.set).not.toHaveBeenCalled() - }) - - it('sets lastError only inside a failed callback and rejects a failed promise', async () => { - const page = install(() => ({ ok: false, error: 'QUOTA_BYTES quota exceeded' })) - const lastError = await new Promise((resolve) => - call(page.chrome, 'storage.sync.set', { big: 1 }, () => - resolve(page.chrome.runtime.lastError?.message) - ) - ) - expect(lastError).toBe('QUOTA_BYTES quota exceeded') - expect(page.chrome.runtime.lastError).toBeUndefined() - await expect(call(page.chrome, 'storage.sync.set', { big: 1 })).rejects.toThrow( - 'QUOTA_BYTES quota exceeded' - ) - }) - - it("delivers main's sync changes to sync.onChanged and storage.onChanged with 'sync'", async () => { - const own = { mine: { newValue: 1 } } - const page = install((method) => ({ ok: true, value: method === 'set' ? own : undefined })) - const all: unknown[][] = [] - const area: unknown[][] = [] - const onAll = (changes: unknown, areaName: unknown) => all.push([areaName, changes]) - call(page.chrome, 'storage.onChanged.addListener', onAll) - call(page.chrome, 'storage.sync.onChanged.addListener', (changes: unknown) => - area.push([changes]) - ) - // The underlying onChanged keeps its own listeners for local and session. - expect(page.native.listeners.has(onAll)).toBe(true) - expect(call(page.chrome, 'storage.onChanged.hasListener', onAll)).toBe(true) - expect(page.listens).toEqual([[PRELOAD_STORAGE_SYNC_NAMESPACE, 'onChanged', true]]) - - const changes = { theme: { oldValue: 'light', newValue: 'dark' } } - page.emit(changes) - expect(all).toEqual([['sync', changes]]) - expect(area).toEqual([[changes]]) - - // Main skips the writer, which fires its own listeners from the changes main returns. - await expect(call(page.chrome, 'storage.sync.set', { mine: 1 })).resolves.toBeUndefined() - expect(all).toEqual([ - ['sync', changes], - ['sync', own] - ]) - expect(area).toEqual([[changes], [own]]) - - call(page.chrome, 'storage.onChanged.removeListener', onAll) - expect(page.native.listeners.has(onAll)).toBe(false) - page.emit(changes) - expect(all).toHaveLength(2) - expect(area).toHaveLength(3) - }) - - it("makes a content script's relayed call and fires this context's listeners for its writes", async () => { - const changes = { theme: { newValue: 'dark' } } - const worker = install((method) => ({ ok: true, value: method === 'set' ? changes : 'value' })) - const heard: unknown[] = [] - call(worker.chrome, 'storage.sync.onChanged.addListener', (change: unknown) => - heard.push(change) - ) - - await expect(worker.relay('get', ['theme'])).resolves.toEqual({ ok: true, value: 'value' }) - await expect(worker.relay('set', [{ theme: 'dark' }])).resolves.toEqual({ - ok: true, - value: changes - }) - expect(heard).toEqual([changes]) - await expect(worker.relay('setAccessLevel', [{}])).resolves.toMatchObject({ ok: false }) - expect(worker.calls).toEqual([ - [PRELOAD_STORAGE_SYNC_NAMESPACE, 'get', ['theme']], - [PRELOAD_STORAGE_SYNC_NAMESPACE, 'set', [{ theme: 'dark' }]] - ]) - }) - - it('passes every sync change on to content scripts, naming the one that wrote it', async () => { - const own = { mine: { newValue: 1 } } - const forward = vi.fn() - const worker = install(() => ({ ok: true, value: own }), forward) - // Listens with no listener of its own: content scripts hear other contexts only this way. - expect(worker.listens).toEqual([[PRELOAD_STORAGE_SYNC_NAMESPACE, 'onChanged', true]]) - - const elsewhere = { theme: { newValue: 'dark' } } - worker.emit(elsewhere) - await call(worker.chrome, 'storage.sync.set', { mine: 1 }) - await worker.relay('set', [{ mine: 1 }], 'cs-1') - expect(forward.mock.calls).toEqual([ - [elsewhere, undefined], - [own, undefined], - [own, 'cs-1'] - ]) - }) -}) diff --git a/src/preload/browser-extension-web-store-wording.test.ts b/src/preload/browser-extension-web-store-wording.test.ts deleted file mode 100644 index 382da7dd12a..00000000000 --- a/src/preload/browser-extension-web-store-wording.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -// @vitest-environment happy-dom -import { afterEach, describe, expect, it } from 'vitest' -import { adaptChromeWebStoreWordingForOrca } from './browser-extension-web-store-wording' - -const flushMutations = (): Promise => new Promise((resolve) => setTimeout(resolve, 0)) - -afterEach(() => { - document.body.innerHTML = '' -}) - -describe('adaptChromeWebStoreWordingForOrca', () => { - it('renames the install wording but keeps the store name', () => { - document.body.innerHTML = - 'Chrome Web Store' - adaptChromeWebStoreWordingForOrca() - expect(document.querySelector('a')?.textContent).toBe('Chrome Web Store') - expect(document.querySelector('button')?.getAttribute('aria-label')).toBe('Add to Orca') - expect(document.querySelector('span')?.textContent).toBe('Add to Orca') - }) - - it('renames wording the store draws later, including the removal toast', async () => { - adaptChromeWebStoreWordingForOrca() - const button = document.createElement('span') - button.textContent = 'Remove from Chrome' - document.body.appendChild(button) - const toast = document.createElement('div') - toast.textContent = 'Bitwarden has been removed from Chrome.' - document.body.appendChild(toast) - await flushMutations() - expect(button.textContent).toBe('Remove from Orca') - expect(toast.textContent).toBe('Bitwarden has been removed from Orca.') - button.firstChild!.nodeValue = 'Add to Chrome' - await flushMutations() - expect(button.textContent).toBe('Add to Orca') - }) - - it('hides the switch-to-Chrome banner and shows the box again when the store reuses it', async () => { - document.body.innerHTML = - '

Vault

' - adaptChromeWebStoreWordingForOrca() - const banner = document.getElementById('banner')! - expect(banner.style.display).toBe('none') - expect(document.querySelector('h1')?.textContent).toBe('Vault') - banner.innerHTML = '
This item is disabled
Enable now' - await flushMutations() - expect(banner.style.display).toBe('') - }) -}) diff --git a/src/preload/browser-extension-worker-shim.ts b/src/preload/browser-extension-worker-shim.ts index a3aa1b19058..bb595fba55c 100644 --- a/src/preload/browser-extension-worker-shim.ts +++ b/src/preload/browser-extension-worker-shim.ts @@ -5,28 +5,28 @@ import { decodeBrowserExtensionWireJson, encodeBrowserExtensionWireJson } from '../shared/browser/extensions/browser-extension-worker-api' -import { BROWSER_EXTENSION_API_SURFACE } from './browser-extension-api-surface' +import { BROWSER_EXTENSION_API_SURFACE } from './browser/extensions/api/browser-extension-api-surface' import { createContentScriptSyncForward, installContentScriptRelayFilter -} from './browser-extension-content-script-relay' +} from './browser/extensions/messaging/browser-extension-content-script-relay' import { installBrowserExtensionDocumentTargets, PRELOAD_FIRST_DISCARDED_TAB_ID -} from './browser-extension-document-targets' -import { passesBrowserExtensionEventFilter } from './browser-extension-event-filters' -import { installBrowserExtensionInertApis } from './browser-extension-inert-apis' +} from './browser/extensions/documents/browser-extension-document-targets' +import { passesBrowserExtensionEventFilter } from './browser/extensions/api/browser-extension-event-filters' +import { installBrowserExtensionInertApis } from './browser/extensions/api/browser-extension-inert-apis' import { installBrowserExtensionApis, type BrowserExtensionInvokeReply, type BrowserExtensionMainWorldBridge -} from './browser-extension-main-world' -import { installBrowserExtensionNativeMessaging } from './browser-extension-native-messaging' -import { installBrowserExtensionStorageRelay } from './browser-extension-storage-relay' +} from './browser/extensions/api/browser-extension-main-world' +import { installBrowserExtensionNativeMessaging } from './browser/extensions/messaging/browser-extension-native-messaging' +import { installBrowserExtensionStorageRelay } from './browser/extensions/storage/browser-extension-storage-relay' import { installBrowserExtensionStorageSync, PRELOAD_STORAGE_SYNC_CONSTANTS -} from './browser-extension-storage-sync' +} from './browser/extensions/storage/browser-extension-storage-sync' /** * Orca's API shim for extension service workers, bundled into one standalone script that Orca's diff --git a/src/preload/browser-guest.ts b/src/preload/browser-guest.ts index 3f42876ed39..1903fc84cdc 100644 --- a/src/preload/browser-guest.ts +++ b/src/preload/browser-guest.ts @@ -1,11 +1,11 @@ import type { ContextBridge, IpcRenderer, WebFrame } from 'electron' -import { installBrowserPasswordDetector } from './browser-password-detector' +import { installBrowserPasswordDetector } from './browser/passwords/browser-password-detector' import { installBrowserWindowCloseGuard, PRELOAD_BROWSER_GUEST_WINDOW_CLOSE_CHANNEL -} from './browser-window-close-installation' -import { installBrowserWindowPrompt } from './browser-window-prompt-installation' -import { installNativeChatVisualGuestTheme } from './native-chat-visual-guest-theme' +} from './browser/window-controls/browser-window-close-installation' +import { installBrowserWindowPrompt } from './browser/window-controls/browser-window-prompt-installation' +import { installNativeChatVisualGuestTheme } from './native-chat/native-chat-visual-guest-theme' // Raw require keeps the sandboxed preload standalone in the CJS build. const electron: { diff --git a/src/preload/browser-passkey-request.ts b/src/preload/browser-passkey-request.ts index 5f666278ea3..4066364b673 100644 --- a/src/preload/browser-passkey-request.ts +++ b/src/preload/browser-passkey-request.ts @@ -3,7 +3,7 @@ import { BROWSER_PASSKEY_REQUEST_CANCEL_CHANNEL, BROWSER_PASSKEY_REQUEST_PENDING_CHANNEL } from '../shared/browser-passkey-request' -import { installBrowserPasskeyRequestWatch } from './browser-passkey-request-watch' +import { installBrowserPasskeyRequestWatch } from './browser/passkeys/browser-passkey-request-watch' // A session preload: it runs in every frame of every browser page, popup and serve window. const electron: { diff --git a/src/preload/browser-window-close-installation.test.ts b/src/preload/browser-window-close-installation.test.ts deleted file mode 100644 index afca315c1a2..00000000000 --- a/src/preload/browser-window-close-installation.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { buildSync } from 'esbuild' -import { fileURLToPath } from 'node:url' -import { runInNewContext } from 'node:vm' - -import { installBrowserWindowCloseGuard } from './browser-window-close-installation' -import { BROWSER_GUEST_WINDOW_CLOSE_CHANNEL } from '../shared/browser-guest-window-close' - -describe('browser window close guard', () => { - afterEach(() => { - vi.unstubAllGlobals() - }) - - it('replaces the native close with a non-replaceable request to Orca', () => { - const nativeClose = vi.fn() - const requestClose = vi.fn() - vi.stubGlobal('window', { close: nativeClose }) - - installBrowserWindowCloseGuard(requestClose) - - expect(window.close()).toBeUndefined() - expect(nativeClose).not.toHaveBeenCalled() - expect(requestClose).toHaveBeenCalledTimes(1) - expect(Reflect.set(window, 'close', nativeClose)).toBe(false) - }) - - it.each([true, false])('routes a close only from the top frame: %s', (isMainFrame) => { - const sendToHost = vi.fn() - const nativeClose = vi.fn() - const pageWindow = { close: nativeClose } - const preload = buildSync({ - entryPoints: [fileURLToPath(new URL('./browser-guest.ts', import.meta.url))], - bundle: true, - platform: 'node', - format: 'cjs', - external: ['electron'], - write: false - }).outputFiles[0].text - - runInNewContext(preload, { - window: pageWindow, - process: { isMainFrame }, - require: (name: string) => { - expect(name).toBe('electron') - return { - ipcRenderer: { sendToHost }, - contextBridge: { - executeInMainWorld: (script: { - func: (requestClose: () => void) => void - args: [() => void] - }) => script.func(...script.args) - } - } - } - }) - - pageWindow.close() - - expect(nativeClose).toHaveBeenCalledTimes(isMainFrame ? 0 : 1) - expect(sendToHost.mock.calls).toEqual(isMainFrame ? [[BROWSER_GUEST_WINDOW_CLOSE_CHANNEL]] : []) - }) -}) diff --git a/src/preload/browser-extension-api-surface.ts b/src/preload/browser/extensions/api/browser-extension-api-surface.ts similarity index 100% rename from src/preload/browser-extension-api-surface.ts rename to src/preload/browser/extensions/api/browser-extension-api-surface.ts diff --git a/src/preload/browser-extension-event-filters.ts b/src/preload/browser/extensions/api/browser-extension-event-filters.ts similarity index 100% rename from src/preload/browser-extension-event-filters.ts rename to src/preload/browser/extensions/api/browser-extension-event-filters.ts diff --git a/src/preload/browser-extension-frame-lifetime.ts b/src/preload/browser/extensions/api/browser-extension-frame-lifetime.ts similarity index 100% rename from src/preload/browser-extension-frame-lifetime.ts rename to src/preload/browser/extensions/api/browser-extension-frame-lifetime.ts diff --git a/src/preload/browser-extension-inert-apis.test.ts b/src/preload/browser/extensions/api/browser-extension-inert-apis.test.ts similarity index 90% rename from src/preload/browser-extension-inert-apis.test.ts rename to src/preload/browser/extensions/api/browser-extension-inert-apis.test.ts index f54dba1de67..f98d3ca313b 100644 --- a/src/preload/browser-extension-inert-apis.test.ts +++ b/src/preload/browser/extensions/api/browser-extension-inert-apis.test.ts @@ -63,10 +63,4 @@ describe('installBrowserExtensionInertApis', () => { expect(member(chrome, 'storage.managed.onChanged')).toEqual({ native: true }) } }) - - it('keeps members Electron already provides', () => { - const native = vi.fn() - const { chrome } = install('page', {}, { extension: { getViews: native } }) - expect(member(chrome, 'extension.getViews')).toBe(native) - }) }) diff --git a/src/preload/browser-extension-inert-apis.ts b/src/preload/browser/extensions/api/browser-extension-inert-apis.ts similarity index 100% rename from src/preload/browser-extension-inert-apis.ts rename to src/preload/browser/extensions/api/browser-extension-inert-apis.ts diff --git a/src/preload/browser-extension-main-world.test.ts b/src/preload/browser/extensions/api/browser-extension-main-world.test.ts similarity index 100% rename from src/preload/browser-extension-main-world.test.ts rename to src/preload/browser/extensions/api/browser-extension-main-world.test.ts diff --git a/src/preload/browser-extension-main-world.ts b/src/preload/browser/extensions/api/browser-extension-main-world.ts similarity index 100% rename from src/preload/browser-extension-main-world.ts rename to src/preload/browser/extensions/api/browser-extension-main-world.ts diff --git a/src/preload/browser-extension-document-targets.test.ts b/src/preload/browser/extensions/documents/browser-extension-document-targets.test.ts similarity index 67% rename from src/preload/browser-extension-document-targets.test.ts rename to src/preload/browser/extensions/documents/browser-extension-document-targets.test.ts index c58e19fee45..cd46a140026 100644 --- a/src/preload/browser-extension-document-targets.test.ts +++ b/src/preload/browser/extensions/documents/browser-extension-document-targets.test.ts @@ -251,108 +251,6 @@ describe('Orca documentIds in native scripting targets', () => { expect(listeners.size).toBe(0) } ) - - it("preserves a native document constraint for Orca's document ids", async () => { - const { chrome, executeScript, calls } = install(['webNavigation', 'scripting']) - const results = await chrome.scripting.executeScript({ - target: { tabId: 3, documentIds: [ORCA_AD] }, - func: () => 1 - }) - expect(executeScript).toHaveBeenCalledTimes(2) - expect(executeScript.mock.calls[1][0].target).toEqual({ tabId: 3, documentIds: [CHROMIUM_AD] }) - expect(results).toEqual([{ frameId: 42, documentId: ORCA_AD, result: 42 }]) - expect(calls).toEqual([[3], [3]]) - }) - - it("passes Chromium's own ids through and keeps the callback form", async () => { - const { chrome, executeScript, insertCSS, sendMessage } = install(['webRequest']) - await chrome.scripting.executeScript({ target: { tabId: 3, documentIds: [CHROMIUM_TOP] } }) - expect(executeScript.mock.calls[0][0].target).toEqual({ tabId: 3, documentIds: [CHROMIUM_TOP] }) - - const done = await new Promise((resolve) => - chrome.scripting.insertCSS( - { target: { tabId: 3, documentIds: [ORCA_TOP] }, css: 'a{}' }, - () => resolve('called back') - ) - ) - expect(done).toBe('called back') - expect(insertCSS.mock.calls[0][0]).toEqual({ - target: { tabId: 3, documentIds: [CHROMIUM_TOP] }, - css: 'a{}' - }) - - await chrome.tabs.sendMessage(3, 'ping', { documentId: ORCA_AD }) - expect(sendMessage).toHaveBeenCalledWith(3, 'ping', { documentId: CHROMIUM_AD }) - }) - - it.each([false, true])( - 'resolves message recipients without requiring scripting even when the namespace exists: %s', - async (nativeScripting) => { - const { chrome, executeScript, sendMessage, nativeListeners } = install( - ['webNavigation'], - nativeScripting - ) - executeScript.mockRejectedValueOnce(new Error('Requires scripting permission')) - - await expect(chrome.tabs.sendMessage(3, 'ping', { documentId: ORCA_AD })).resolves.toBe( - 'pong' - ) - - expect(sendMessage).toHaveBeenLastCalledWith(3, 'ping', { documentId: CHROMIUM_AD }) - expect(executeScript).toHaveBeenCalledTimes(nativeScripting ? 1 : 0) - expect(nativeListeners.size).toBe(0) - } - ) - - it('reaches dynamically injected message listeners that do not handle private probes', async () => { - const { chrome, sendMessage, nativeListeners } = install(['webNavigation', 'scripting']) - sendMessage.mockImplementationOnce(async (_tabId, message) => { - if (typeof message !== 'string') { - throw new Error('Unexpected private packet') - } - return 'dynamic receiver response' - }) - - await expect( - chrome.tabs.sendMessage(3, 'real payload', { documentId: ORCA_TOP }) - ).resolves.toBe('dynamic receiver response') - - expect(sendMessage).toHaveBeenCalledTimes(1) - expect(sendMessage).toHaveBeenCalledWith(3, 'real payload', { documentId: CHROMIUM_TOP }) - expect(nativeListeners.size).toBe(0) - }) - - it('never labels an unexpected native document as the requested Orca document', async () => { - const { chrome, executeScript } = install(['webNavigation', 'scripting']) - executeScript.mockResolvedValueOnce([{ frameId: 0, documentId: CHROMIUM_TOP, result: 0 }]) - executeScript.mockResolvedValueOnce([ - { frameId: 0, documentId: 'replacement-document', result: 1 } - ]) - - await expect( - chrome.scripting.executeScript({ - target: { tabId: 3, documentIds: [ORCA_TOP] } - }) - ).resolves.toEqual([{ frameId: 0, documentId: 'replacement-document', result: 1 }]) - }) - - it('cleans up failed identity probes and reports callback errors without delivering the payload', async () => { - const { chrome, sendMessage, nativeListeners } = install(['webNavigation'], false) - sendMessage.mockRejectedValueOnce(new Error('Receiving end does not exist')) - const callback = vi.fn() - const error = await new Promise((resolve) => { - chrome.tabs.sendMessage(3, 'private payload', { documentId: ORCA_TOP }, () => { - callback() - resolve(chrome.runtime.lastError) - }) - }) - - expect(error).toEqual({ message: 'Receiving end does not exist' }) - expect(callback).toHaveBeenCalledTimes(1) - expect(sendMessage).toHaveBeenCalledTimes(1) - expect(chrome.runtime.lastError).toBeUndefined() - expect(nativeListeners.size).toBe(0) - }) }) describe('calls to a discarded tab', () => { diff --git a/src/preload/browser-extension-document-targets.ts b/src/preload/browser/extensions/documents/browser-extension-document-targets.ts similarity index 99% rename from src/preload/browser-extension-document-targets.ts rename to src/preload/browser/extensions/documents/browser-extension-document-targets.ts index df091692047..eaac39d963a 100644 --- a/src/preload/browser-extension-document-targets.ts +++ b/src/preload/browser/extensions/documents/browser-extension-document-targets.ts @@ -1,4 +1,4 @@ -import type { BrowserExtensionMainWorldBridge } from './browser-extension-main-world' +import type { BrowserExtensionMainWorldBridge } from '../api/browser-extension-main-world' /** Main-only namespace listing a tab's frames with Orca's documentIds; never on `chrome`. */ export const PRELOAD_DOCUMENT_TARGETS_NAMESPACE = 'orcaDocumentTargets' diff --git a/src/preload/browser-extension-content-script-relay.test.ts b/src/preload/browser/extensions/messaging/browser-extension-content-script-relay.test.ts similarity index 98% rename from src/preload/browser-extension-content-script-relay.test.ts rename to src/preload/browser/extensions/messaging/browser-extension-content-script-relay.test.ts index 98d21664c1a..4d98f9d5a60 100644 --- a/src/preload/browser-extension-content-script-relay.test.ts +++ b/src/preload/browser/extensions/messaging/browser-extension-content-script-relay.test.ts @@ -5,7 +5,7 @@ import { createContentScriptRelayBridge, installContentScriptRelayFilter } from './browser-extension-content-script-relay' -import type { BrowserExtensionStorageSyncRelay } from './browser-extension-storage-sync' +import type { BrowserExtensionStorageSyncRelay } from '../storage/browser-extension-storage-sync' type Listener = ( message: unknown, diff --git a/src/preload/browser-extension-content-script-relay.ts b/src/preload/browser/extensions/messaging/browser-extension-content-script-relay.ts similarity index 98% rename from src/preload/browser-extension-content-script-relay.ts rename to src/preload/browser/extensions/messaging/browser-extension-content-script-relay.ts index 0188f6bcb9f..50f2ddd8426 100644 --- a/src/preload/browser-extension-content-script-relay.ts +++ b/src/preload/browser/extensions/messaging/browser-extension-content-script-relay.ts @@ -1,11 +1,11 @@ import type { BrowserExtensionInvokeReply, BrowserExtensionMainWorldBridge -} from './browser-extension-main-world' +} from '../api/browser-extension-main-world' import type { BrowserExtensionStorageSyncForward, BrowserExtensionStorageSyncRelay -} from './browser-extension-storage-sync' +} from '../storage/browser-extension-storage-sync' /** * Content scripts run in Chromium's per-extension isolated world, which no Orca preload reaches, diff --git a/src/preload/browser-extension-native-messaging.ts b/src/preload/browser/extensions/messaging/browser-extension-native-messaging.ts similarity index 98% rename from src/preload/browser-extension-native-messaging.ts rename to src/preload/browser/extensions/messaging/browser-extension-native-messaging.ts index 88d4a32f85e..3c793e8ec18 100644 --- a/src/preload/browser-extension-native-messaging.ts +++ b/src/preload/browser/extensions/messaging/browser-extension-native-messaging.ts @@ -1,4 +1,4 @@ -import type { BrowserExtensionMainWorldBridge } from './browser-extension-main-world' +import type { BrowserExtensionMainWorldBridge } from '../api/browser-extension-main-world' /** Main-only namespace behind connectNative/sendNativeMessage; never installed on `chrome`. */ export const PRELOAD_NATIVE_MESSAGING_NAMESPACE = 'orcaNativeMessaging' diff --git a/src/preload/browser-extension-storage-relay.test.ts b/src/preload/browser/extensions/storage/browser-extension-storage-relay.test.ts similarity index 75% rename from src/preload/browser-extension-storage-relay.test.ts rename to src/preload/browser/extensions/storage/browser-extension-storage-relay.test.ts index 4c611b29325..5ba7e59bc13 100644 --- a/src/preload/browser-extension-storage-relay.test.ts +++ b/src/preload/browser/extensions/storage/browser-extension-storage-relay.test.ts @@ -1,6 +1,6 @@ import { runInNewContext } from 'node:vm' import { describe, expect, it, vi } from 'vitest' -import type { BrowserExtensionInvokeReply } from './browser-extension-main-world' +import type { BrowserExtensionInvokeReply } from '../api/browser-extension-main-world' import { installBrowserExtensionStorageRelay, PRELOAD_STORAGE_RELAY_NAMESPACE, @@ -149,36 +149,7 @@ function call(root: unknown, path: string, ...args: unknown[]): unknown { return fn.call(owner, ...args) } -const settle = () => new Promise((resolve) => setTimeout(resolve, 0)) - describe('installBrowserExtensionStorageRelay in a worker', () => { - it('fires onChanged and the area event once per write with only the keys that changed', async () => { - const worker = install('worker') - worker.listen() - expect(worker.listens).toEqual([[PRELOAD_STORAGE_RELAY_NAMESPACE, 'onChanged', true]]) - - await call(worker.chrome, 'storage.local.set', { kept: 'same', changed: 'new', added: [1] }) - await call(worker.chrome, 'storage.local.set', { kept: 'same' }) - await call(worker.chrome, 'storage.local.remove', ['gone', 'missing']) - await call(worker.chrome, 'storage.session.set', { token: 'abc' }) - await call(worker.chrome, 'storage.session.clear') - - const written = { changed: { oldValue: 'old', newValue: 'new' }, added: { newValue: [1] } } - expect(worker.all).toEqual([ - ['local', written], - ['local', { gone: { oldValue: 1 } }], - ['session', { token: { newValue: 'abc' } }], - ['session', { token: { oldValue: 'abc' } }] - ]) - expect(worker.perArea).toEqual([ - ['local', written], - ['local', { gone: { oldValue: 1 } }], - ['session', { token: { newValue: 'abc' } }], - ['session', { token: { oldValue: 'abc' } }] - ]) - expect(worker.calls).toEqual([]) - }) - it('reports old values in write order when writes are not awaited', async () => { const worker = install('worker') worker.listen() @@ -239,37 +210,3 @@ describe('installBrowserExtensionStorageRelay in a worker', () => { expect(worker.perArea).toEqual([['session', { token: { newValue: 'abc' } }]]) }) }) - -describe('installBrowserExtensionStorageRelay in an extension page', () => { - it('reports its own changed keys to main and leaves the native onChanged alone', async () => { - const page = install('page') - expect(page.chrome.storage.onChanged).toEqual({ native: true }) - expect(page.local.area.onChanged).toEqual({ native: true }) - expect(page.hasEventHandler()).toBe(false) - - await call(page.chrome, 'storage.local.set', { kept: 'same', changed: 'new' }) - await call(page.chrome, 'storage.local.set', { kept: 'same' }) - await call(page.chrome, 'storage.local.clear') - await settle() - expect(page.calls).toEqual([ - [ - PRELOAD_STORAGE_RELAY_NAMESPACE, - 'report', - ['local', { changed: { oldValue: 'old', newValue: 'new' } }] - ], - [ - PRELOAD_STORAGE_RELAY_NAMESPACE, - 'report', - [ - 'local', - { - kept: { oldValue: 'same' }, - changed: { oldValue: 'new' }, - gone: { oldValue: 1 } - } - ] - ] - ]) - expect(page.local.data.size).toBe(0) - }) -}) diff --git a/src/preload/browser-extension-storage-relay.ts b/src/preload/browser/extensions/storage/browser-extension-storage-relay.ts similarity index 98% rename from src/preload/browser-extension-storage-relay.ts rename to src/preload/browser/extensions/storage/browser-extension-storage-relay.ts index 9c43ba3bf11..5f090e6fc99 100644 --- a/src/preload/browser-extension-storage-relay.ts +++ b/src/preload/browser/extensions/storage/browser-extension-storage-relay.ts @@ -1,4 +1,4 @@ -import type { BrowserExtensionMainWorldBridge } from './browser-extension-main-world' +import type { BrowserExtensionMainWorldBridge } from '../api/browser-extension-main-world' /** Main-only namespace carrying page storage writes to the worker; never installed on `chrome`. */ export const PRELOAD_STORAGE_RELAY_NAMESPACE = 'orcaStorageRelay' diff --git a/src/preload/browser/extensions/storage/browser-extension-storage-sync.test.ts b/src/preload/browser/extensions/storage/browser-extension-storage-sync.test.ts new file mode 100644 index 00000000000..40ba69c72d6 --- /dev/null +++ b/src/preload/browser/extensions/storage/browser-extension-storage-sync.test.ts @@ -0,0 +1,100 @@ +import { runInNewContext } from 'node:vm' +import { describe, expect, it, vi } from 'vitest' +import type { BrowserExtensionInvokeReply } from '../api/browser-extension-main-world' +import { + installBrowserExtensionStorageSync, + PRELOAD_STORAGE_SYNC_CONSTANTS, + PRELOAD_STORAGE_SYNC_NAMESPACE +} from './browser-extension-storage-sync' + +type Runtime = { lastError?: { message: string } } + +function nativeEvent() { + const listeners = new Set() + return { + listeners, + addListener: vi.fn((callback: unknown) => listeners.add(callback)), + removeListener: vi.fn((callback: unknown) => listeners.delete(callback)) + } +} + +/** Runs the installer from its source text, as contextBridge.executeInMainWorld does. */ +function install( + reply: (method: string, args: unknown[]) => BrowserExtensionInvokeReply, + forward?: (changes: unknown, from?: string) => void +) { + const runtime: Runtime = {} + const native = nativeEvent() + const nativeSync = { get: vi.fn(), set: vi.fn() } + const chrome = { runtime, storage: { local: {}, sync: nativeSync, onChanged: native } } + let dispatch: ((namespace: string, event: string, args: unknown[]) => void) | null = null + const calls: unknown[][] = [] + const listens: unknown[][] = [] + const bridge = { + call: async (namespace: string, method: string, args: unknown[]) => { + calls.push([namespace, method, args]) + return reply(method, args) + }, + listen: (namespace: string, event: string, active: boolean) => + listens.push([namespace, event, active]), + onEvent: (handler: typeof dispatch) => { + dispatch = handler + }, + passesFilter: () => true + } + const realm = { chrome, console, JSON, Promise } + const relay: unknown = runInNewContext( + `(${installBrowserExtensionStorageSync.toString()})(bridge, constants, forward)`, + { + ...realm, + globalThis: realm, + bridge, + constants: PRELOAD_STORAGE_SYNC_CONSTANTS, + forward + } + ) + return { + relay: (method: string, args: unknown[], from?: string): unknown => + typeof relay === 'function' ? relay(method, args, from) : undefined, + chrome, + native, + nativeSync, + calls, + listens, + emit: (changes: unknown) => dispatch?.(PRELOAD_STORAGE_SYNC_NAMESPACE, 'onChanged', [changes]) + } +} + +function isRecord(value: unknown): value is Record { + return value !== null && typeof value === 'object' +} + +function call(root: unknown, path: string, ...args: unknown[]): unknown { + const keys = path.split('.') + const name = keys.pop() ?? '' + const owner = keys.reduce( + (value, key) => (isRecord(value) ? value[key] : undefined), + root + ) + const fn = isRecord(owner) ? owner[name] : null + if (typeof fn !== 'function') { + throw new Error(`${path} is not a function`) + } + return fn.call(owner, ...args) +} + +describe('installBrowserExtensionStorageSync', () => { + it('sets lastError only inside a failed callback and rejects a failed promise', async () => { + const page = install(() => ({ ok: false, error: 'QUOTA_BYTES quota exceeded' })) + const lastError = await new Promise((resolve) => + call(page.chrome, 'storage.sync.set', { big: 1 }, () => + resolve(page.chrome.runtime.lastError?.message) + ) + ) + expect(lastError).toBe('QUOTA_BYTES quota exceeded') + expect(page.chrome.runtime.lastError).toBeUndefined() + await expect(call(page.chrome, 'storage.sync.set', { big: 1 })).rejects.toThrow( + 'QUOTA_BYTES quota exceeded' + ) + }) +}) diff --git a/src/preload/browser-extension-storage-sync.ts b/src/preload/browser/extensions/storage/browser-extension-storage-sync.ts similarity index 99% rename from src/preload/browser-extension-storage-sync.ts rename to src/preload/browser/extensions/storage/browser-extension-storage-sync.ts index 89c4824a3bb..bd0c5d788df 100644 --- a/src/preload/browser-extension-storage-sync.ts +++ b/src/preload/browser/extensions/storage/browser-extension-storage-sync.ts @@ -1,7 +1,7 @@ import type { BrowserExtensionInvokeReply, BrowserExtensionMainWorldBridge -} from './browser-extension-main-world' +} from '../api/browser-extension-main-world' /** Main-only namespace backing chrome.storage.sync; never installed on `chrome` by that name. */ export const PRELOAD_STORAGE_SYNC_NAMESPACE = 'orcaStorageSync' diff --git a/src/preload/browser-extension-web-store-bridge.ts b/src/preload/browser/extensions/web-store/browser-extension-web-store-bridge.ts similarity index 100% rename from src/preload/browser-extension-web-store-bridge.ts rename to src/preload/browser/extensions/web-store/browser-extension-web-store-bridge.ts diff --git a/src/preload/browser-extension-web-store-main-world.test.ts b/src/preload/browser/extensions/web-store/browser-extension-web-store-main-world.test.ts similarity index 94% rename from src/preload/browser-extension-web-store-main-world.test.ts rename to src/preload/browser/extensions/web-store/browser-extension-web-store-main-world.test.ts index 6923b31d5a4..feb494d4ef7 100644 --- a/src/preload/browser-extension-web-store-main-world.test.ts +++ b/src/preload/browser/extensions/web-store/browser-extension-web-store-main-world.test.ts @@ -63,12 +63,6 @@ function lastErrors(realm: unknown): [unknown, unknown] { } describe('installChromeWebStoreApis', () => { - it('keeps existing runtime members', () => { - const sendMessage = () => {} - const { realm } = installInFreshRealm(() => ({}), { runtime: { sendMessage } }) - expect(member(realm, 'chrome.runtime.sendMessage')).toBe(sendMessage) - }) - it('sets lastError on runtime and extension only while the callback runs', async () => { const { realm, calls } = installInFreshRealm(() => ({ value: 'user_cancelled', diff --git a/src/preload/browser-extension-web-store-main-world.ts b/src/preload/browser/extensions/web-store/browser-extension-web-store-main-world.ts similarity index 100% rename from src/preload/browser-extension-web-store-main-world.ts rename to src/preload/browser/extensions/web-store/browser-extension-web-store-main-world.ts diff --git a/src/preload/browser-extension-web-store-wording.ts b/src/preload/browser/extensions/web-store/browser-extension-web-store-wording.ts similarity index 100% rename from src/preload/browser-extension-web-store-wording.ts rename to src/preload/browser/extensions/web-store/browser-extension-web-store-wording.ts diff --git a/src/preload/browser-passkey-request-watch.test.ts b/src/preload/browser/passkeys/browser-passkey-request-watch.test.ts similarity index 100% rename from src/preload/browser-passkey-request-watch.test.ts rename to src/preload/browser/passkeys/browser-passkey-request-watch.test.ts diff --git a/src/preload/browser-passkey-request-watch.ts b/src/preload/browser/passkeys/browser-passkey-request-watch.ts similarity index 100% rename from src/preload/browser-passkey-request-watch.ts rename to src/preload/browser/passkeys/browser-passkey-request-watch.ts diff --git a/src/preload/browser-password-detector.test.ts b/src/preload/browser/passwords/browser-password-detector.test.ts similarity index 90% rename from src/preload/browser-password-detector.test.ts rename to src/preload/browser/passwords/browser-password-detector.test.ts index a06fdf168c5..2044a7f7565 100644 --- a/src/preload/browser-password-detector.test.ts +++ b/src/preload/browser/passwords/browser-password-detector.test.ts @@ -3,11 +3,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { BROWSER_PASSWORDS_GUEST_CHANNELS, BROWSER_PASSWORDS_TO_GUEST_CHANNELS -} from '../shared/browser-passwords-types' +} from '../../../shared/browser-passwords-types' import type { BrowserPasswordFillRequest, BrowserPasswordGuestEnable -} from '../shared/browser-passwords-types' +} from '../../../shared/browser-passwords-types' import { installBrowserPasswordDetector } from './browser-password-detector' const CHANNEL = BROWSER_PASSWORDS_GUEST_CHANNELS @@ -162,26 +162,6 @@ afterEach(() => { }) describe('browser password detector', () => { - it('installs only its main listeners until main enables the document', async () => { - load(LOGIN_FORM) - const addEventListener = vi.mocked(document.addEventListener) - const observe = vi.mocked(MutationObserver.prototype.observe) - - install() - typeInto(el('#pw'), 'secret') - await vi.advanceTimersByTimeAsync(1000) - - expect([...listeners.keys()].sort()).toEqual( - Object.values(BROWSER_PASSWORDS_TO_GUEST_CHANNELS).sort() - ) - expect(addEventListener).not.toHaveBeenCalled() - expect(observe).not.toHaveBeenCalled() - expect(sent).toEqual([]) - - enable() - expect(observe).toHaveBeenCalled() - }) - it('reports no page forms unless main waits on a login that navigated here', async () => { load(LOGIN_FORM) install() @@ -193,44 +173,6 @@ describe('browser password detector', () => { expect(sentOn(CHANNEL.pageForms)).toEqual([]) }) - it('reports the page forms once after enable and again only when they change', async () => { - load(LOGIN_FORM) - install() - enable({ reportPageForms: true }) - await vi.advanceTimersByTimeAsync(600) - - expect(sentOn(CHANNEL.pageForms)).toEqual([ - { - forms: [ - { - formSignature: expect.any(String), - kind: 'login', - usernameFieldName: 'email', - passwordFieldName: 'password', - action: new URL('/session', window.location.href).toString() - } - ] - } - ]) - - document.body.insertAdjacentHTML('beforeend', '

unrelated

') - await vi.advanceTimersByTimeAsync(600) - expect(sentOn(CHANNEL.pageForms)).toHaveLength(1) - - document.body.insertAdjacentHTML( - 'beforeend', - '
' - ) - layout() - await vi.advanceTimersByTimeAsync(600) - expect(sentOn(CHANNEL.pageForms).at(-1)).toEqual({ - forms: [ - expect.objectContaining({ kind: 'login' }), - expect.objectContaining({ kind: 'signup' }) - ] - }) - }) - it('reports trusted typing after the debounce, with only typed values', async () => { load(LOGIN_FORM) el('#email').value = 'prefilled@example.com' diff --git a/src/preload/browser-password-detector.ts b/src/preload/browser/passwords/browser-password-detector.ts similarity index 99% rename from src/preload/browser-password-detector.ts rename to src/preload/browser/passwords/browser-password-detector.ts index ad35208bc1f..6dba6cd7c0b 100644 --- a/src/preload/browser-password-detector.ts +++ b/src/preload/browser/passwords/browser-password-detector.ts @@ -6,7 +6,7 @@ import type { BrowserPasswordGuestCapture, BrowserPasswordGuestPageForms -} from '../shared/browser-passwords-types' +} from '../../../shared/browser-passwords-types' import { createBrowserPasswordElementTokens } from './browser-password-element-tokens' import { applyBrowserPasswordFill, diff --git a/src/preload/browser-password-element-tokens.ts b/src/preload/browser/passwords/browser-password-element-tokens.ts similarity index 100% rename from src/preload/browser-password-element-tokens.ts rename to src/preload/browser/passwords/browser-password-element-tokens.ts diff --git a/src/preload/browser-password-field-fill.ts b/src/preload/browser/passwords/browser-password-field-fill.ts similarity index 99% rename from src/preload/browser-password-field-fill.ts rename to src/preload/browser/passwords/browser-password-field-fill.ts index ec617be730e..0adcbe2cb78 100644 --- a/src/preload/browser-password-field-fill.ts +++ b/src/preload/browser/passwords/browser-password-field-fill.ts @@ -5,7 +5,7 @@ import type { BrowserPasswordFillRequest, BrowserPasswordFillResult -} from '../shared/browser-passwords-types' +} from '../../../shared/browser-passwords-types' import { isPlaceholderUsernameValue } from './browser-password-field-rules' import type { BrowserPasswordFormScanner } from './browser-password-form-scan' diff --git a/src/preload/browser-password-field-rules.ts b/src/preload/browser/passwords/browser-password-field-rules.ts similarity index 100% rename from src/preload/browser-password-field-rules.ts rename to src/preload/browser/passwords/browser-password-field-rules.ts diff --git a/src/preload/browser-password-focus-tracker.ts b/src/preload/browser/passwords/browser-password-focus-tracker.ts similarity index 98% rename from src/preload/browser-password-focus-tracker.ts rename to src/preload/browser/passwords/browser-password-focus-tracker.ts index 950bb57df21..000aa06c3c9 100644 --- a/src/preload/browser-password-focus-tracker.ts +++ b/src/preload/browser/passwords/browser-password-focus-tracker.ts @@ -6,7 +6,7 @@ import type { BrowserPasswordFieldRole, BrowserPasswordGuestFieldBlurred, BrowserPasswordGuestFieldFocused -} from '../shared/browser-passwords-types' +} from '../../../shared/browser-passwords-types' import { PRELOAD_BROWSER_PASSWORDS_GUEST_CHANNELS } from './browser-password-guest-channels' import type { BrowserPasswordGuestPort } from './browser-password-guest-port' import { diff --git a/src/preload/browser-password-form-parser.test.ts b/src/preload/browser/passwords/browser-password-form-parser.test.ts similarity index 80% rename from src/preload/browser-password-form-parser.test.ts rename to src/preload/browser/passwords/browser-password-form-parser.test.ts index 3139ad42165..61fbbcbfe81 100644 --- a/src/preload/browser-password-form-parser.test.ts +++ b/src/preload/browser/passwords/browser-password-form-parser.test.ts @@ -253,57 +253,4 @@ describe('parsePasswordForm', () => { ])('%s', (_name, fields, text, expected) => { expect(parse(fields, text)).toEqual(expected) }) - - describe('username-first steps', () => { - it.each<[string, TestField[], string, string | null]>([ - [ - 'Google: autocomplete=username identifier box', - [input('identifier', { type: 'email', id: 'identifierId', autocomplete: 'username' })], - 'next', - 'identifier' - ], - [ - 'Microsoft: lone email box in a form that says Next', - [input('loginfmt', { type: 'email' })], - 'sign in next', - 'loginfmt' - ], - [ - 'Okta: autocomplete=username with a remember-me checkbox', - [ - input('identifier', { autocomplete: 'username' }), - input('rememberMe', { type: 'checkbox' }) - ], - 'sign in', - 'identifier' - ], - ['lone autocomplete=email box', [input('mail', { autocomplete: 'email' })], 'go', 'mail'], - ['lone field without any sign-in wording', [input('city')], 'submit', null], - [ - 'contact form: autocomplete=email beside other fields', - [input('name'), input('mail', { autocomplete: 'email' }), input('subject')], - 'send', - null - ], - [ - 'newsletter email form', - [input('email', { type: 'email', autocomplete: 'email' })], - 'subscribe to our newsletter', - null - ], - [ - 'newsletter by field name', - [input('newsletter_email', { type: 'email' })], - 'continue', - null - ], - ['lone search box that says next', [input('q', { type: 'search' })], 'next', null] - ])('%s', (_name, fields, text, username) => { - const parsed = parse(fields, text) - expect(parsed ? parsed.username : null).toBe(username) - if (username) { - expect(parsed?.kind).toBe('username-only') - } - }) - }) }) diff --git a/src/preload/browser-password-form-parser.ts b/src/preload/browser/passwords/browser-password-form-parser.ts similarity index 99% rename from src/preload/browser-password-form-parser.ts rename to src/preload/browser/passwords/browser-password-form-parser.ts index f89ca3a3989..aee557fca18 100644 --- a/src/preload/browser-password-form-parser.ts +++ b/src/preload/browser/passwords/browser-password-form-parser.ts @@ -5,7 +5,7 @@ import type { BrowserPasswordFieldRole, BrowserPasswordFormKind -} from '../shared/browser-passwords-types' +} from '../../../shared/browser-passwords-types' import { autocompleteRoleToken, isCandidatePasswordFormField, diff --git a/src/preload/browser-password-form-scan.ts b/src/preload/browser/passwords/browser-password-form-scan.ts similarity index 99% rename from src/preload/browser-password-form-scan.ts rename to src/preload/browser/passwords/browser-password-form-scan.ts index 57d8c3c7317..74cbbcdba09 100644 --- a/src/preload/browser-password-form-scan.ts +++ b/src/preload/browser/passwords/browser-password-form-scan.ts @@ -6,7 +6,7 @@ import type { BrowserPasswordFieldRole, BrowserPasswordFormSummary -} from '../shared/browser-passwords-types' +} from '../../../shared/browser-passwords-types' import { isCredentialInputType, type PasswordFieldDescriptor } from './browser-password-field-rules' import { parsePasswordForm, diff --git a/src/preload/browser-password-guest-channels.ts b/src/preload/browser/passwords/browser-password-guest-channels.ts similarity index 95% rename from src/preload/browser-password-guest-channels.ts rename to src/preload/browser/passwords/browser-password-guest-channels.ts index 484fe08099e..cd82664a1cd 100644 --- a/src/preload/browser-password-guest-channels.ts +++ b/src/preload/browser/passwords/browser-password-guest-channels.ts @@ -1,7 +1,7 @@ import type { BROWSER_PASSWORDS_GUEST_CHANNELS, BROWSER_PASSWORDS_TO_GUEST_CHANNELS -} from '../shared/browser-passwords-types' +} from '../../../shared/browser-passwords-types' // Why literals: the sandboxed guest preload's `require` resolves only 'electron', so importing the // shared constants would emit a chunk it cannot load. `satisfies` and the test pin them. diff --git a/src/preload/browser-password-guest-port.ts b/src/preload/browser/passwords/browser-password-guest-port.ts similarity index 100% rename from src/preload/browser-password-guest-port.ts rename to src/preload/browser/passwords/browser-password-guest-port.ts diff --git a/src/preload/browser-password-submit-tracker.ts b/src/preload/browser/passwords/browser-password-submit-tracker.ts similarity index 99% rename from src/preload/browser-password-submit-tracker.ts rename to src/preload/browser/passwords/browser-password-submit-tracker.ts index 1c1894abe57..331f236930f 100644 --- a/src/preload/browser-password-submit-tracker.ts +++ b/src/preload/browser/passwords/browser-password-submit-tracker.ts @@ -7,7 +7,7 @@ import type { BrowserPasswordGuestFormGone, BrowserPasswordGuestSubmitted, BrowserPasswordSubmitSource -} from '../shared/browser-passwords-types' +} from '../../../shared/browser-passwords-types' import { PRELOAD_BROWSER_PASSWORDS_GUEST_CHANNELS } from './browser-password-guest-channels' import type { BrowserPasswordGuestPort } from './browser-password-guest-port' import { diff --git a/src/preload/browser/window-controls/browser-window-close-installation.test.ts b/src/preload/browser/window-controls/browser-window-close-installation.test.ts new file mode 100644 index 00000000000..9c0a3f9b884 --- /dev/null +++ b/src/preload/browser/window-controls/browser-window-close-installation.test.ts @@ -0,0 +1,22 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' + +import { installBrowserWindowCloseGuard } from './browser-window-close-installation' + +describe('browser window close guard', () => { + afterEach(() => { + vi.unstubAllGlobals() + }) + + it('replaces the native close with a non-replaceable request to Orca', () => { + const nativeClose = vi.fn() + const requestClose = vi.fn() + vi.stubGlobal('window', { close: nativeClose }) + + installBrowserWindowCloseGuard(requestClose) + + expect(window.close()).toBeUndefined() + expect(nativeClose).not.toHaveBeenCalled() + expect(requestClose).toHaveBeenCalledTimes(1) + expect(Reflect.set(window, 'close', nativeClose)).toBe(false) + }) +}) diff --git a/src/preload/browser-window-close-installation.ts b/src/preload/browser/window-controls/browser-window-close-installation.ts similarity index 100% rename from src/preload/browser-window-close-installation.ts rename to src/preload/browser/window-controls/browser-window-close-installation.ts diff --git a/src/preload/browser-window-prompt-installation.ts b/src/preload/browser/window-controls/browser-window-prompt-installation.ts similarity index 100% rename from src/preload/browser-window-prompt-installation.ts rename to src/preload/browser/window-controls/browser-window-prompt-installation.ts diff --git a/src/preload/doc-preview-link.ts b/src/preload/doc-preview-link.ts index 5cba0144e53..24c402907a4 100644 --- a/src/preload/doc-preview-link.ts +++ b/src/preload/doc-preview-link.ts @@ -1,9 +1,9 @@ import { installDocPreviewLinkInterception, PRELOAD_DOC_PREVIEW_LINK_CLICK_CHANNEL -} from './doc-preview-link-interception' +} from './document-preview/doc-preview-link-interception' import type { IpcRenderer, WebFrame } from 'electron' -import { installNativeChatVisualGuestTheme } from './native-chat-visual-guest-theme' +import { installNativeChatVisualGuestTheme } from './native-chat/native-chat-visual-guest-theme' // Raw require keeps the sandboxed preload standalone in the CJS build. const electron: { diff --git a/src/preload/doc-preview-link-interception.ts b/src/preload/document-preview/doc-preview-link-interception.ts similarity index 97% rename from src/preload/doc-preview-link-interception.ts rename to src/preload/document-preview/doc-preview-link-interception.ts index 99ea276ccf6..d9bee1dd5f3 100644 --- a/src/preload/doc-preview-link-interception.ts +++ b/src/preload/document-preview/doc-preview-link-interception.ts @@ -1,5 +1,5 @@ // Runs in the isolated world so page scripts cannot access the link reporter or its listeners. -import type { DocPreviewLinkModifiers } from '../shared/document-preview/doc-preview-scheme' +import type { DocPreviewLinkModifiers } from '../../shared/document-preview/doc-preview-scheme' export type DocPreviewExternalLinkReporter = ( url: string, diff --git a/src/preload/gitlab-work-item-details.test.ts b/src/preload/gitlab-work-item-details.test.ts deleted file mode 100644 index b7a7c955168..00000000000 --- a/src/preload/gitlab-work-item-details.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { beforeEach, expect, it, vi } from 'vitest' - -const invoke = vi.hoisted(() => vi.fn()) -vi.mock('electron', () => ({ ipcRenderer: { invoke } })) -import { glApi } from './gitlab' - -beforeEach(() => invoke.mockReset()) - -it('preserves the optional review scope through the existing desktop details channel', async () => { - const args = { repoPath: '/repo', iid: 12, type: 'mr' as const, includeReviewDetails: false } - const details = { body: 'MR body', comments: [], pipelineJobs: [] } - invoke.mockResolvedValue(details) - expect(await glApi.workItemDetails(args)).toBe(details) - expect(invoke).toHaveBeenCalledWith('gitlab:workItemDetails', args) - const defaultArgs = { repoPath: '/repo', iid: 12, type: 'mr' as const } - await glApi.workItemDetails(defaultArgs) - expect(invoke).toHaveBeenLastCalledWith('gitlab:workItemDetails', defaultArgs) -}) diff --git a/src/preload/index.ts b/src/preload/index.ts index 6ccddcf2d8c..1830e09c681 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,8 +1,8 @@ import { contextBridge, ipcRenderer } from 'electron' import { HIDDEN_WINDOW_FRAME_PUMP_STOP_CHANNEL } from '../shared/hidden-window-frame-pump' -import { installHiddenWindowFramePump } from './hidden-window-frame-pump' +import { installHiddenWindowFramePump } from './renderer/hidden-window-frame-pump' import type { PreloadApi } from './api-types' -import { installBrowserFindListener } from './preload-runtime-support' +import { installBrowserFindListener } from './renderer/renderer-runtime' import { appApi } from './api/app-bridge' import { orcaProfilesApi } from './api/orca-profiles-bridge' import { platformApi } from './api/platform-bridge' @@ -29,7 +29,7 @@ import { crashReportsApi } from './api/crash-reports-bridge' import { exportApi } from './api/export-bridge' import { ghApi } from './api/gh-bridge' import { hostedReviewApi } from './api/hosted-review-bridge' -import { glApiBridge } from './api/gl-bridge' +import { glApi } from './api/gitlab' import { bitbucketApi } from './api/bitbucket-bridge' import { linearApi } from './api/linear-bridge' import { jiraApi } from './api/jira-bridge' @@ -131,7 +131,7 @@ const api = { export: exportApi, gh: ghApi, hostedReview: hostedReviewApi, - gl: glApiBridge, + gl: glApi, bitbucket: bitbucketApi, linear: linearApi, jira: jiraApi, diff --git a/src/preload/native-chat-visual-guest-theme.ts b/src/preload/native-chat/native-chat-visual-guest-theme.ts similarity index 95% rename from src/preload/native-chat-visual-guest-theme.ts rename to src/preload/native-chat/native-chat-visual-guest-theme.ts index ef3efe9024c..5237d255702 100644 --- a/src/preload/native-chat-visual-guest-theme.ts +++ b/src/preload/native-chat/native-chat-visual-guest-theme.ts @@ -3,7 +3,7 @@ import { NATIVE_CHAT_VISUAL_GUEST_THEME_READ, NATIVE_CHAT_VISUAL_GUEST_THEME_UPDATE, type NativeChatVisualGuestTheme -} from '../shared/native-chat/native-chat-visual-shell' +} from '../../shared/native-chat/native-chat-visual-shell' export function installNativeChatVisualGuestTheme( ipc: Pick, diff --git a/src/preload/opencode-go-credentials.test.ts b/src/preload/opencode-go-credentials.test.ts deleted file mode 100644 index e7c5f6d4bea..00000000000 --- a/src/preload/opencode-go-credentials.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { opencodeGoCredentialsApi } from './api/opencode-go-credentials-bridge' - -const invoke = vi.hoisted(() => vi.fn(async () => ({ apiKeyConfigured: true }))) -vi.mock('electron', () => ({ ipcRenderer: { invoke } })) - -describe('OpenCode Go credential bridge', () => { - it('exposes only status and write operations', async () => { - expect(await opencodeGoCredentialsApi.getStatus()).toEqual({ apiKeyConfigured: true }) - await opencodeGoCredentialsApi.saveApiKey('fake-key') - await opencodeGoCredentialsApi.clearApiKey() - expect(invoke.mock.calls).toEqual([ - ['opencodeGoCredentials:getStatus'], - ['opencodeGoCredentials:saveApiKey', 'fake-key'], - ['opencodeGoCredentials:clearApiKey'] - ]) - expect(Object.keys(opencodeGoCredentialsApi)).toEqual([ - 'getStatus', - 'saveApiKey', - 'clearApiKey' - ]) - }) -}) diff --git a/src/preload/plugin-frame-guest.ts b/src/preload/plugin-frame-guest.ts index f68e4787b4e..42fdb3ea6cd 100644 --- a/src/preload/plugin-frame-guest.ts +++ b/src/preload/plugin-frame-guest.ts @@ -4,12 +4,12 @@ import { PRELOAD_PLUGIN_FRAME_IPC_CHANNEL, PRELOAD_PLUGIN_FRAME_KEY_CHANNEL, PRELOAD_PLUGIN_FRAME_TRANSPORT_GLOBAL -} from './plugin-frame-guest-transport' +} from './plugins/frames/plugin-frame-guest-transport' import { createPluginFrameGestureTracker, createPluginFrameSendBudget, type PluginFrameSendRefusal -} from './plugin-frame-guest-send-budget' +} from './plugins/frames/plugin-frame-guest-send-budget' // Raw require keeps the sandboxed preload standalone in the CJS build. const electron: { diff --git a/src/preload/plugin-frame-guest-render-lane.test.ts b/src/preload/plugins/frames/plugin-frame-guest-render-lane.test.ts similarity index 100% rename from src/preload/plugin-frame-guest-render-lane.test.ts rename to src/preload/plugins/frames/plugin-frame-guest-render-lane.test.ts diff --git a/src/preload/plugin-frame-guest-send-budget.ts b/src/preload/plugins/frames/plugin-frame-guest-send-budget.ts similarity index 100% rename from src/preload/plugin-frame-guest-send-budget.ts rename to src/preload/plugins/frames/plugin-frame-guest-send-budget.ts diff --git a/src/preload/plugin-frame-guest-transport.ts b/src/preload/plugins/frames/plugin-frame-guest-transport.ts similarity index 100% rename from src/preload/plugin-frame-guest-transport.ts rename to src/preload/plugins/frames/plugin-frame-guest-transport.ts diff --git a/src/preload/plugin-frame-guest.test.ts b/src/preload/plugins/frames/plugin-frame-guest.test.ts similarity index 60% rename from src/preload/plugin-frame-guest.test.ts rename to src/preload/plugins/frames/plugin-frame-guest.test.ts index 545a4920794..eef66dc5d5a 100644 --- a/src/preload/plugin-frame-guest.test.ts +++ b/src/preload/plugins/frames/plugin-frame-guest.test.ts @@ -4,9 +4,8 @@ import { fileURLToPath } from 'node:url' import { runInNewContext } from 'node:vm' import { PLUGIN_FRAME_IPC_CHANNEL, - PLUGIN_FRAME_KEY_CHANNEL, PLUGIN_FRAME_TRANSPORT_GLOBAL -} from '../shared/plugins/frames/plugin-frame-guest' +} from '../../../shared/plugins/frames/plugin-frame-guest' import { createPluginFrameGestureTracker, createPluginFrameSendBudget, @@ -17,7 +16,7 @@ type Transport = { send: (message: unknown) => void; subscribe: (fn: (m: unknown type Listener = (event: { isTrusted: boolean } & Record) => void const preloadSource = buildSync({ - entryPoints: [fileURLToPath(new URL('./plugin-frame-guest.ts', import.meta.url))], + entryPoints: [fileURLToPath(new URL('../../plugin-frame-guest.ts', import.meta.url))], bundle: true, platform: 'node', format: 'cjs', @@ -73,65 +72,6 @@ function runPreload(isMainFrame: boolean, contentType = 'text/html') { } describe('plugin frame guest preload', () => { - it('hands the shell a deletable transport and stamps each send with a gesture serial', () => { - const { sendToHost, ipcListeners, pageWindow, input, transport } = runPreload(true) - expect( - Object.getOwnPropertyDescriptor(pageWindow, PLUGIN_FRAME_TRANSPORT_GLOBAL) - ).toMatchObject({ configurable: true, enumerable: false, writable: false }) - transport().send({ orca: 1, kind: 'evt', event: 'ready' }) - expect(sendToHost).toHaveBeenLastCalledWith( - PLUGIN_FRAME_IPC_CHANNEL, - { orca: 1, kind: 'evt', event: 'ready' }, - { gesture: 0 } - ) - // Untrusted (page-dispatched) input is not a gesture. - input('pointerdown', { isTrusted: false }) - transport().send({}) - expect(sendToHost).toHaveBeenLastCalledWith(PLUGIN_FRAME_IPC_CHANNEL, {}, { gesture: 0 }) - input('pointerdown', { isTrusted: true }) - transport().send({}) - expect(sendToHost).toHaveBeenLastCalledWith(PLUGIN_FRAME_IPC_CHANNEL, {}, { gesture: 1 }) - input('keydown', { isTrusted: true, key: 'a', code: 'KeyA' }) - transport().send({}) - expect(sendToHost).toHaveBeenLastCalledWith(PLUGIN_FRAME_IPC_CHANNEL, {}, { gesture: 2 }) - const first = vi.fn() - transport().subscribe(first) - transport().subscribe(vi.fn()) - expect(ipcListeners).toHaveLength(1) - ipcListeners[0]?.({}, { orca: 1, kind: 'res', id: 0 }) - expect(first).toHaveBeenCalledWith({ orca: 1, kind: 'res', id: 0 }) - }) - - it('reports trusted modifier chords on their own channel, never page-dispatched ones', () => { - const { sendToHost, input } = runPreload(true) - const chord = { key: 's', code: 'KeyS', metaKey: true, ctrlKey: false, altKey: false } - input('keydown', { isTrusted: false, ...chord, shiftKey: false }) - expect(sendToHost).not.toHaveBeenCalledWith(PLUGIN_FRAME_KEY_CHANNEL, expect.anything()) - input('keydown', { isTrusted: true, ...chord, shiftKey: false, repeat: false }) - expect(sendToHost).toHaveBeenCalledWith(PLUGIN_FRAME_KEY_CHANNEL, { ...chord, shiftKey: false }) - }) - - it('answers a refused request at once instead of leaving it pending', async () => { - const { sendToHost, transport } = runPreload(true) - const receive = vi.fn() - transport().subscribe(receive) - transport().send({ - orca: 1, - kind: 'req', - id: 7, - method: 'notifications.show', - params: { title: 'x'.repeat(200 * 1024) } - }) - await Promise.resolve() - expect(sendToHost).not.toHaveBeenCalled() - expect(receive).toHaveBeenCalledWith({ - orca: 1, - kind: 'res', - id: 7, - error: expect.objectContaining({ code: 'too_large', retryable: false }) - }) - }) - it('leaves subframes and non-HTML documents without a transport', () => { expect(PLUGIN_FRAME_TRANSPORT_GLOBAL in runPreload(false).pageWindow).toBe(false) expect(PLUGIN_FRAME_TRANSPORT_GLOBAL in runPreload(true, 'image/svg+xml').pageWindow).toBe( diff --git a/src/preload/renderer-restart-wiring.test.ts b/src/preload/renderer-restart-wiring.test.ts deleted file mode 100644 index 595ef92a1cf..00000000000 --- a/src/preload/renderer-restart-wiring.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { EventEmitter } from 'node:events' -import { ORCA_RENDERER_UNLOAD_PREVENTED_EVENT } from '../shared/renderer-shutdown-events' -import { - ORCA_APP_RESTART_ABORTED_EVENT, - ORCA_APP_RESTART_STARTED_EVENT, - ORCA_UPDATER_QUIT_AND_INSTALL_STARTED_EVENT -} from '../shared/updater-renderer-events' -import { - prepareAndInvokeAppRestart, - prepareAndInvokeUpdaterInstall, - registerRendererRestartIpcRelays -} from './renderer-restart-wiring' - -function restartIpc(eventTarget: EventTarget) { - const ipcRenderer = Object.assign(new EventEmitter(), { - invoke: vi.fn(async () => {}), - postMessage: vi.fn(), - send: vi.fn(), - sendSync: vi.fn(), - sendToHost: vi.fn() - }) - const relay = { handleStatus: vi.fn(), abort: vi.fn() } - registerRendererRestartIpcRelays(ipcRenderer, eventTarget, relay) - return { ipcRenderer, ...relay } -} - -describe('renderer restart wiring', () => { - it.each(['no-op', 'failure'] as const)( - 'keeps a committed restart prepared after a later %s', - async (outcome) => { - const eventTarget = new EventTarget() - const { ipcRenderer } = restartIpc(eventTarget) - const aborted = vi.fn() - const started = vi.fn() - const checkpoint = vi.fn(async () => {}) - eventTarget.addEventListener(ORCA_APP_RESTART_ABORTED_EVENT, aborted) - eventTarget.addEventListener(ORCA_APP_RESTART_STARTED_EVENT, started) - await prepareAndInvokeAppRestart( - eventTarget, - async () => { - ipcRenderer.emit('app:restart-committed') - return true - }, - checkpoint, - Boolean - ) - const subsequent = prepareAndInvokeAppRestart( - eventTarget, - async () => { - if (outcome === 'failure') { - throw new Error('already finalized') - } - return false - }, - checkpoint, - Boolean - ) - await (outcome === 'failure' - ? expect(subsequent).rejects.toThrow('already finalized') - : expect(subsequent).resolves.toBe(false)) - expect(checkpoint).toHaveBeenCalledOnce() - expect(started).toHaveBeenCalledOnce() - expect(aborted).not.toHaveBeenCalled() - } - ) - - it('refuses overlapping preparation without abandoning the accepted restart', async () => { - const eventTarget = new EventTarget() - const aborted = vi.fn() - eventTarget.addEventListener(ORCA_APP_RESTART_ABORTED_EVENT, aborted) - const checkpoint = Promise.withResolvers() - const invoke = vi.fn(async () => true) - const first = prepareAndInvokeAppRestart(eventTarget, invoke, () => checkpoint.promise) - const refused = vi.fn(async () => false) - await expect( - prepareAndInvokeAppRestart(eventTarget, refused, async () => {}, Boolean) - ).rejects.toThrow('already in progress') - expect(refused).not.toHaveBeenCalled() - expect(aborted).not.toHaveBeenCalled() - checkpoint.resolve() - await expect(first).resolves.toBe(true) - expect(invoke).toHaveBeenCalledOnce() - }) - - it('retains late commitment across an unrelated unload veto', async () => { - const eventTarget = new EventTarget() - const abandoned = vi.fn() - eventTarget.addEventListener(ORCA_APP_RESTART_ABORTED_EVENT, abandoned) - eventTarget.addEventListener(ORCA_RENDERER_UNLOAD_PREVENTED_EVENT, abandoned) - const { ipcRenderer } = restartIpc(eventTarget) - const checkpoint = vi.fn(async () => {}) - await prepareAndInvokeAppRestart(eventTarget, async () => true, checkpoint, Boolean) - ipcRenderer.emit('app:restart-committed') - await prepareAndInvokeAppRestart(eventTarget, async () => false, checkpoint, Boolean) - expect(checkpoint).toHaveBeenCalledOnce() - ipcRenderer.emit('window:unload-prevented') - await prepareAndInvokeAppRestart(eventTarget, async () => true, checkpoint, Boolean) - expect(checkpoint).toHaveBeenCalledOnce() - expect(abandoned).not.toHaveBeenCalled() - }) - - it('releases preparation ownership and its listener after checkpoint failure', async () => { - const eventTarget = new EventTarget() - const add = vi.spyOn(eventTarget, 'addEventListener') - const remove = vi.spyOn(eventTarget, 'removeEventListener') - await expect( - prepareAndInvokeAppRestart( - eventTarget, - async () => {}, - async () => { - throw new Error('checkpoint failed') - } - ) - ).rejects.toThrow('checkpoint failed') - await prepareAndInvokeAppRestart( - eventTarget, - async () => {}, - async () => {} - ) - expect(add.mock.calls).toHaveLength(2) - expect(remove.mock.calls).toEqual(add.mock.calls) - }) - - it('marks preparation before invoking main and aborts on IPC failure', async () => { - const eventTarget = new EventTarget() - const calls: string[] = [] - eventTarget.addEventListener(ORCA_UPDATER_QUIT_AND_INSTALL_STARTED_EVENT, () => { - calls.push('prepared') - }) - const relay = { - markPrepared: () => calls.push('marked'), - abort: () => calls.push('aborted') - } - const invoke = vi.fn(async () => { - calls.push('invoked') - throw new Error('IPC failed') - }) - - await expect( - prepareAndInvokeUpdaterInstall(eventTarget, relay, invoke, async () => { - calls.push('checkpoint-flushed') - }) - ).rejects.toThrow('IPC failed') - - expect(calls).toEqual(['prepared', 'checkpoint-flushed', 'marked', 'invoked', 'aborted']) - }) - - it('never installs the update when the shutdown checkpoint fails to persist', async () => { - const eventTarget = new EventTarget() - const invoke = vi.fn(() => Promise.resolve()) - const relay = { markPrepared: vi.fn(), abort: vi.fn() } - - await expect( - prepareAndInvokeUpdaterInstall(eventTarget, relay, invoke, () => - Promise.reject(new Error('Failed to persist renderer state before unload.')) - ) - ).rejects.toThrow('Failed to persist renderer state before unload.') - - expect(invoke).not.toHaveBeenCalled() - expect(relay.markPrepared).not.toHaveBeenCalled() - }) -}) diff --git a/src/preload/browser-client-page-renderer-requests.test.ts b/src/preload/renderer/browser-pages/browser-client-page-renderer-requests.test.ts similarity index 81% rename from src/preload/browser-client-page-renderer-requests.test.ts rename to src/preload/renderer/browser-pages/browser-client-page-renderer-requests.test.ts index 6baaf0215ba..5f0f88322b2 100644 --- a/src/preload/browser-client-page-renderer-requests.test.ts +++ b/src/preload/renderer/browser-pages/browser-client-page-renderer-requests.test.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import type { BrowserClientPageRendererReply, BrowserClientPageRendererRequest -} from '../shared/browser/client-hosting/browser-client-page-renderer-protocol' +} from '../../../shared/browser/client-hosting/browser-client-page-renderer-protocol' import { createBrowserClientPageRendererRequests } from './browser-client-page-renderer-requests' type Listener = (event: unknown, request: unknown) => void @@ -230,51 +230,4 @@ describe('browser client page renderer preload requests', () => { expect(callback).toHaveBeenCalledTimes(2) expect(ipc.sent).toHaveLength(1) }) - - it.each(['dispose', 'unsubscribe', 'replace', 'timeout'] as const)( - 'does not start already-settled callbacks after %s', - async (action) => { - vi.useFakeTimers() - const ipc = new FakeIpc() - const requests = createBrowserClientPageRendererRequests({ - ipc, - isTopFrame: () => true, - timeoutMs: 50 - }) - const callback = vi.fn(() => ({ type: 'mounted' as const, webContentsId: 91 })) - const unsubscribe = requests.subscribe(callback) - for (let index = 0; index < 64; index += 1) { - ipc.emit(request(`request-${index}`)) - } - let errorCode = 'browser_client_page_renderer_preload_disposed' - if (action === 'dispose') { - requests.dispose() - } else if (action === 'unsubscribe') { - unsubscribe() - errorCode = 'browser_client_page_renderer_subscriber_unavailable' - } else if (action === 'replace') { - requests.subscribe(() => ({ type: 'mounted', webContentsId: 92 })) - errorCode = 'browser_client_page_renderer_subscriber_replaced' - } else { - vi.advanceTimersByTime(50) - errorCode = 'browser_client_page_renderer_subscriber_timeout' - } - try { - await flush() - expect(ipc.sent.map(({ reply }) => reply)).toEqual( - Array.from({ length: 64 }, (_, index) => ({ - type: 'failed', - errorCode, - requestId: `request-${index}`, - page: PAGE, - operation: 'mountPage' - })) - ) - expect(vi.getTimerCount()).toBe(0) - expect(callback).not.toHaveBeenCalled() - } finally { - requests.dispose() - } - } - ) }) diff --git a/src/preload/browser-client-page-renderer-requests.ts b/src/preload/renderer/browser-pages/browser-client-page-renderer-requests.ts similarity index 99% rename from src/preload/browser-client-page-renderer-requests.ts rename to src/preload/renderer/browser-pages/browser-client-page-renderer-requests.ts index 46aec46359e..d52ef50759e 100644 --- a/src/preload/browser-client-page-renderer-requests.ts +++ b/src/preload/renderer/browser-pages/browser-client-page-renderer-requests.ts @@ -6,7 +6,7 @@ import { BrowserClientPageRendererRequest, type BrowserClientPageRendererOutcome as RendererOutcome, type BrowserClientPageRendererRequest as RendererRequest -} from '../shared/browser/client-hosting/browser-client-page-renderer-protocol' +} from '../../../shared/browser/client-hosting/browser-client-page-renderer-protocol' const DEFAULT_MAX_PENDING = 512 const DEFAULT_TIMEOUT_MS = 10_000 diff --git a/src/preload/browser-find-subscriptions.test.ts b/src/preload/renderer/browser-pages/browser-find-subscriptions.test.ts similarity index 70% rename from src/preload/browser-find-subscriptions.test.ts rename to src/preload/renderer/browser-pages/browser-find-subscriptions.test.ts index c25b00baa1d..f85bfb4835b 100644 --- a/src/preload/browser-find-subscriptions.test.ts +++ b/src/preload/renderer/browser-pages/browser-find-subscriptions.test.ts @@ -77,34 +77,4 @@ describe('browser Find subscriptions', () => { expect(callback).not.toHaveBeenCalled() }) - - it('drops empty ownership entries when subscribers clean up', () => { - const subscriptions = createBrowserFindSubscriptions() - const callback = vi.fn() - const unsubscribe = subscriptions.subscribe(FIRST_SOURCE, callback) - expect(subscriptions.subscribedPageCount()).toBe(1) - - unsubscribe() - - subscriptions.dispatch(FIRST_SOURCE) - subscriptions.dispatch({ browserPageId: FIRST_SOURCE.browserPageId }) - expect(callback).not.toHaveBeenCalled() - expect(subscriptions.subscribedPageCount()).toBe(0) - }) - - it('keeps the other workspaces on a page when one of them unsubscribes', () => { - const subscriptions = createBrowserFindSubscriptions() - const firstCallback = vi.fn() - const samePageCallback = vi.fn() - const unsubscribe = subscriptions.subscribe(FIRST_SOURCE, firstCallback) - subscriptions.subscribe(SAME_PAGE_SOURCE, samePageCallback) - - unsubscribe() - subscriptions.dispatch(SAME_PAGE_SOURCE) - subscriptions.dispatch({ browserPageId: FIRST_SOURCE.browserPageId }) - - expect(firstCallback).not.toHaveBeenCalled() - expect(samePageCallback).toHaveBeenCalledTimes(2) - expect(subscriptions.subscribedPageCount()).toBe(1) - }) }) diff --git a/src/preload/browser-find-subscriptions.ts b/src/preload/renderer/browser-pages/browser-find-subscriptions.ts similarity index 94% rename from src/preload/browser-find-subscriptions.ts rename to src/preload/renderer/browser-pages/browser-find-subscriptions.ts index 8bd9e2a76d0..5e4928acd08 100644 --- a/src/preload/browser-find-subscriptions.ts +++ b/src/preload/renderer/browser-pages/browser-find-subscriptions.ts @@ -1,17 +1,15 @@ -import { asBrowserFindTarget, type BrowserFindSource } from '../shared/browser-find-source' +import { asBrowserFindTarget, type BrowserFindSource } from '../../../shared/browser-find-source' type BrowserFindCallback = () => void export function createBrowserFindSubscriptions(): { dispatch: (target: unknown) => void subscribe: (source: BrowserFindSource, callback: BrowserFindCallback) => () => void - subscribedPageCount: () => number } { // Page-only targets must reach every workspace holding that page. const callbacksByPage = new Map>>() return { - subscribedPageCount: () => callbacksByPage.size, dispatch: (target) => { const findTarget = asBrowserFindTarget(target) if (!findTarget) { diff --git a/src/preload/renderer-heap-statistics-reader.test.ts b/src/preload/renderer/diagnostics/renderer-heap-statistics-reader.test.ts similarity index 100% rename from src/preload/renderer-heap-statistics-reader.test.ts rename to src/preload/renderer/diagnostics/renderer-heap-statistics-reader.test.ts diff --git a/src/preload/renderer-heap-statistics-reader.ts b/src/preload/renderer/diagnostics/renderer-heap-statistics-reader.ts similarity index 91% rename from src/preload/renderer-heap-statistics-reader.ts rename to src/preload/renderer/diagnostics/renderer-heap-statistics-reader.ts index aa578e8ff46..9126e041574 100644 --- a/src/preload/renderer-heap-statistics-reader.ts +++ b/src/preload/renderer/diagnostics/renderer-heap-statistics-reader.ts @@ -1,4 +1,4 @@ -import type { RendererHeapStatistics } from '../shared/renderer-heap-statistics' +import type { RendererHeapStatistics } from '../../../shared/renderer-heap-statistics' type HeapStatisticsSource = Pick diff --git a/src/preload/renderer-process-memory-reader.test.ts b/src/preload/renderer/diagnostics/renderer-process-memory-reader.test.ts similarity index 100% rename from src/preload/renderer-process-memory-reader.test.ts rename to src/preload/renderer/diagnostics/renderer-process-memory-reader.test.ts diff --git a/src/preload/renderer-process-memory-reader.ts b/src/preload/renderer/diagnostics/renderer-process-memory-reader.ts similarity index 89% rename from src/preload/renderer-process-memory-reader.ts rename to src/preload/renderer/diagnostics/renderer-process-memory-reader.ts index 2e501b3e740..01dbb1e2414 100644 --- a/src/preload/renderer-process-memory-reader.ts +++ b/src/preload/renderer/diagnostics/renderer-process-memory-reader.ts @@ -1,4 +1,4 @@ -import type { RendererProcessMemory } from '../shared/renderer-process-memory' +import type { RendererProcessMemory } from '../../../shared/renderer-process-memory' type ProcessMemorySource = Pick diff --git a/src/preload/e2e-config.ts b/src/preload/renderer/e2e-config.ts similarity index 93% rename from src/preload/e2e-config.ts rename to src/preload/renderer/e2e-config.ts index c62c844a7c8..d524eb2c5da 100644 --- a/src/preload/e2e-config.ts +++ b/src/preload/renderer/e2e-config.ts @@ -1,4 +1,4 @@ -import { createE2EConfig } from '../shared/e2e-config' +import { createE2EConfig } from '../../shared/e2e-config' const preloadEnv = ( import.meta as ImportMeta & { diff --git a/src/preload/hidden-window-frame-pump.ts b/src/preload/renderer/hidden-window-frame-pump.ts similarity index 97% rename from src/preload/hidden-window-frame-pump.ts rename to src/preload/renderer/hidden-window-frame-pump.ts index 94e415f8bbe..015ae7ff6b2 100644 --- a/src/preload/hidden-window-frame-pump.ts +++ b/src/preload/renderer/hidden-window-frame-pump.ts @@ -1,7 +1,7 @@ import { HIDDEN_WINDOW_FRAME_PUMP_INTERVAL_MS, hasHiddenWindowFramePumpArgument -} from '../shared/hidden-window-frame-pump' +} from '../../shared/hidden-window-frame-pump' // Near-black so the single damaged frame is indistinguishable even if the window were visible. const FLIP_COLOR = 'rgb(1, 1, 1)' diff --git a/src/preload/preload-runtime-support.ts b/src/preload/renderer/renderer-runtime.ts similarity index 82% rename from src/preload/preload-runtime-support.ts rename to src/preload/renderer/renderer-runtime.ts index f8753d7794e..d3f4e6d89fc 100644 --- a/src/preload/preload-runtime-support.ts +++ b/src/preload/renderer/renderer-runtime.ts @@ -1,9 +1,9 @@ import { ipcRenderer } from 'electron' -import { createBrowserClientPageRendererRequests } from './browser-client-page-renderer-requests' -import { createBrowserFindSubscriptions } from './browser-find-subscriptions' -import { registerRendererRestartIpcRelays } from './renderer-restart-wiring' -import { createUpdaterQuitAbortRelay } from '../shared/renderer-restart-preparation' -import { ORCA_UPDATER_QUIT_AND_INSTALL_ABORTED_EVENT } from '../shared/updater-renderer-events' +import { createBrowserClientPageRendererRequests } from './browser-pages/browser-client-page-renderer-requests' +import { createBrowserFindSubscriptions } from './browser-pages/browser-find-subscriptions' +import { registerRendererRestartIpcRelays } from './restart/renderer-restart-wiring' +import { createUpdaterQuitAbortRelay } from '../../shared/renderer-restart-preparation' +import { ORCA_UPDATER_QUIT_AND_INSTALL_ABORTED_EVENT } from '../../shared/updater-renderer-events' export async function awaitBeforeUnloadCheckpoint(): Promise { const result = (await ipcRenderer.invoke('app:await-before-unload-checkpoint')) as { ok?: unknown diff --git a/src/preload/renderer/restart/app-restart-checkpoint-routing.test.ts b/src/preload/renderer/restart/app-restart-checkpoint-routing.test.ts new file mode 100644 index 00000000000..94000e9bbff --- /dev/null +++ b/src/preload/renderer/restart/app-restart-checkpoint-routing.test.ts @@ -0,0 +1,96 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { PreloadApi } from '../../api-types' +import { ORCA_APP_RESTART_STARTED_EVENT } from '../../../shared/updater-renderer-events' + +const { exposeInMainWorld, invoke, on, removeListener, send, sendSync, getPathForFile } = + vi.hoisted(() => ({ + exposeInMainWorld: vi.fn(), + invoke: vi.fn(), + on: vi.fn(), + removeListener: vi.fn(), + send: vi.fn(), + sendSync: vi.fn(), + getPathForFile: vi.fn(() => '/files/notes.txt') + })) + +vi.mock('electron', () => ({ + contextBridge: { exposeInMainWorld }, + ipcRenderer: { invoke, on, removeListener, send, sendSync }, + webFrame: { + getZoomFactor: vi.fn(() => 1), + setZoomFactor: vi.fn(), + setVisualZoomLevelLimits: vi.fn() + }, + webUtils: { getPathForFile } +})) + +describe('native preload destructive app actions', () => { + const originalContextIsolated = Object.getOwnPropertyDescriptor(process, 'contextIsolated') + let eventTarget: EventTarget + + beforeEach(() => { + vi.resetModules() + exposeInMainWorld.mockReset() + invoke.mockReset() + on.mockReset() + removeListener.mockReset() + send.mockReset() + sendSync.mockReset() + Object.defineProperty(process, 'contextIsolated', { configurable: true, value: true }) + eventTarget = new EventTarget() + vi.stubGlobal('window', eventTarget) + vi.stubGlobal('document', { addEventListener: vi.fn() }) + }) + + afterEach(() => { + vi.unstubAllGlobals() + if (originalContextIsolated) { + Object.defineProperty(process, 'contextIsolated', originalContextIsolated) + } else { + Reflect.deleteProperty(process, 'contextIsolated') + } + }) + + const loadApi = async (): Promise => { + await import('../../index') + return exposeInMainWorld.mock.calls.find(([name]) => name === 'api')?.[1] as PreloadApi + } + + it('exposes the durable checkpoint join the lazy-chunk recovery reload depends on', async () => { + const api = await loadApi() + invoke.mockResolvedValue({ ok: true }) + + await expect(api.app.awaitBeforeUnloadCheckpoint()).resolves.toBeUndefined() + expect(invoke).toHaveBeenCalledWith('app:await-before-unload-checkpoint') + + invoke.mockResolvedValue({ ok: false }) + + await expect(api.app.awaitBeforeUnloadCheckpoint()).rejects.toThrow( + 'Failed to persist renderer state before unload.' + ) + }) + + it('awaits renderer durability before profile maintenance and preserves its result', async () => { + const api = await loadApi() + const started = vi.fn() + eventTarget.addEventListener(ORCA_APP_RESTART_STARTED_EVENT, started) + let finishCheckpoint = (_result: { ok: boolean }): void => {} + const checkpoint = new Promise((resolve) => { + finishCheckpoint = resolve + }) + const result = { status: 'relaunching' } + invoke.mockImplementation((channel: string) => + channel === 'app:await-before-unload-checkpoint' ? checkpoint : Promise.resolve(result) + ) + + const switching = api.orcaProfiles.switchProfile({ profileId: 'target' }) + await vi.waitFor(() => + expect(invoke).toHaveBeenCalledWith('app:await-before-unload-checkpoint') + ) + expect(started).toHaveBeenCalledOnce() + expect(invoke).not.toHaveBeenCalledWith('orcaProfiles:switch', expect.anything()) + finishCheckpoint({ ok: true }) + await expect(switching).resolves.toBe(result) + expect(invoke).toHaveBeenLastCalledWith('orcaProfiles:switch', { profileId: 'target' }) + }) +}) diff --git a/src/preload/renderer/restart/renderer-restart-wiring.test.ts b/src/preload/renderer/restart/renderer-restart-wiring.test.ts new file mode 100644 index 00000000000..dd382790100 --- /dev/null +++ b/src/preload/renderer/restart/renderer-restart-wiring.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it, vi } from 'vitest' + +import { + ORCA_APP_RESTART_ABORTED_EVENT, + ORCA_UPDATER_QUIT_AND_INSTALL_STARTED_EVENT +} from '../../../shared/updater-renderer-events' +import { + prepareAndInvokeAppRestart, + prepareAndInvokeUpdaterInstall +} from './renderer-restart-wiring' + +describe('renderer restart wiring', () => { + it('refuses overlapping preparation without abandoning the accepted restart', async () => { + const eventTarget = new EventTarget() + const aborted = vi.fn() + eventTarget.addEventListener(ORCA_APP_RESTART_ABORTED_EVENT, aborted) + const checkpoint = Promise.withResolvers() + const invoke = vi.fn(async () => true) + const first = prepareAndInvokeAppRestart(eventTarget, invoke, () => checkpoint.promise) + const refused = vi.fn(async () => false) + await expect( + prepareAndInvokeAppRestart(eventTarget, refused, async () => {}, Boolean) + ).rejects.toThrow('already in progress') + expect(refused).not.toHaveBeenCalled() + expect(aborted).not.toHaveBeenCalled() + checkpoint.resolve() + await expect(first).resolves.toBe(true) + expect(invoke).toHaveBeenCalledOnce() + }) + + it('marks preparation before invoking main and aborts on IPC failure', async () => { + const eventTarget = new EventTarget() + const calls: string[] = [] + eventTarget.addEventListener(ORCA_UPDATER_QUIT_AND_INSTALL_STARTED_EVENT, () => { + calls.push('prepared') + }) + const relay = { + markPrepared: () => calls.push('marked'), + abort: () => calls.push('aborted') + } + const invoke = vi.fn(async () => { + calls.push('invoked') + throw new Error('IPC failed') + }) + + await expect( + prepareAndInvokeUpdaterInstall(eventTarget, relay, invoke, async () => { + calls.push('checkpoint-flushed') + }) + ).rejects.toThrow('IPC failed') + + expect(calls).toEqual(['prepared', 'checkpoint-flushed', 'marked', 'invoked', 'aborted']) + }) + + it('never installs the update when the shutdown checkpoint fails to persist', async () => { + const eventTarget = new EventTarget() + const invoke = vi.fn(() => Promise.resolve()) + const relay = { markPrepared: vi.fn(), abort: vi.fn() } + + await expect( + prepareAndInvokeUpdaterInstall(eventTarget, relay, invoke, () => + Promise.reject(new Error('Failed to persist renderer state before unload.')) + ) + ).rejects.toThrow('Failed to persist renderer state before unload.') + + expect(invoke).not.toHaveBeenCalled() + expect(relay.markPrepared).not.toHaveBeenCalled() + }) +}) diff --git a/src/preload/renderer-restart-wiring.ts b/src/preload/renderer/restart/renderer-restart-wiring.ts similarity index 92% rename from src/preload/renderer-restart-wiring.ts rename to src/preload/renderer/restart/renderer-restart-wiring.ts index 67b14d33c71..eeed8069f01 100644 --- a/src/preload/renderer-restart-wiring.ts +++ b/src/preload/renderer/restart/renderer-restart-wiring.ts @@ -1,16 +1,16 @@ import type { IpcRenderer } from 'electron' -import { ORCA_RENDERER_UNLOAD_PREVENTED_EVENT } from '../shared/renderer-shutdown-events' +import { ORCA_RENDERER_UNLOAD_PREVENTED_EVENT } from '../../../shared/renderer-shutdown-events' import { prepareRendererForAppRestart, type UpdaterQuitAbortRelay -} from '../shared/renderer-restart-preparation' -import type { UpdateStatus } from '../shared/update-status-types' +} from '../../../shared/renderer-restart-preparation' +import type { UpdateStatus } from '../../../shared/update-status-types' import { ORCA_APP_RESTART_ABORTED_EVENT, ORCA_APP_RESTART_STARTED_EVENT, ORCA_UPDATER_QUIT_AND_INSTALL_ABORTED_EVENT, ORCA_UPDATER_QUIT_AND_INSTALL_STARTED_EVENT -} from '../shared/updater-renderer-events' +} from '../../../shared/updater-renderer-events' type AppRestartState = { committed: boolean; pending: boolean } const appRestartStates = new WeakMap() diff --git a/src/preload/close-active-tab-payload-admission.test.ts b/src/preload/renderer/window-commands/close-active-tab-payload-admission.test.ts similarity index 100% rename from src/preload/close-active-tab-payload-admission.test.ts rename to src/preload/renderer/window-commands/close-active-tab-payload-admission.test.ts diff --git a/src/preload/close-active-tab-payload-admission.ts b/src/preload/renderer/window-commands/close-active-tab-payload-admission.ts similarity index 90% rename from src/preload/close-active-tab-payload-admission.ts rename to src/preload/renderer/window-commands/close-active-tab-payload-admission.ts index 17428cb6a7a..c407c94376e 100644 --- a/src/preload/close-active-tab-payload-admission.ts +++ b/src/preload/renderer/window-commands/close-active-tab-payload-admission.ts @@ -1,4 +1,4 @@ -import type { CloseActiveTabPayload } from './api/ui-command-event-api' +import type { CloseActiveTabPayload } from '../../api/ui-command-event-api' export type AdmittedCloseActiveTabPayload = | { kind: 'legacy' } diff --git a/src/preload/runtime-environment-subscriptions.ts b/src/preload/runtime/runtime-environment-subscriptions.ts similarity index 98% rename from src/preload/runtime-environment-subscriptions.ts rename to src/preload/runtime/runtime-environment-subscriptions.ts index 38baf347fd8..0629a2ca6e5 100644 --- a/src/preload/runtime-environment-subscriptions.ts +++ b/src/preload/runtime/runtime-environment-subscriptions.ts @@ -3,9 +3,9 @@ import { type CrewSubscriptionAttemptArgs, type CrewSubscriptionAttemptCallbacks, type CrewSubscriptionSetupResult -} from '../shared/remote-runtime/host-catalog/runtime-environment-subscription-setup' +} from '../../shared/remote-runtime/host-catalog/runtime-environment-subscription-setup' -import type { RuntimeRpcResponse } from '../shared/runtime-rpc-envelope' +import type { RuntimeRpcResponse } from '../../shared/runtime-rpc-envelope' type RuntimeEnvironmentSubscribeArgs = { subscriptionId?: string diff --git a/src/preload/updater-package-recovery.test.ts b/src/preload/updater-package-recovery.test.ts deleted file mode 100644 index fb58791e9cc..00000000000 --- a/src/preload/updater-package-recovery.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { PreloadApi } from './api-types' - -const { exposeInMainWorld, invoke, on, removeListener, send, sendSync } = vi.hoisted(() => ({ - exposeInMainWorld: vi.fn(), - invoke: vi.fn(), - on: vi.fn(), - removeListener: vi.fn(), - send: vi.fn(), - sendSync: vi.fn() -})) - -vi.mock('electron', () => ({ - contextBridge: { exposeInMainWorld }, - ipcRenderer: { invoke, on, removeListener, send, sendSync }, - webFrame: { - getZoomFactor: vi.fn(() => 1), - setZoomFactor: vi.fn(), - setVisualZoomLevelLimits: vi.fn() - }, - webUtils: { getPathForFile: vi.fn(() => '') } -})) - -describe('native preload linux package recovery methods', () => { - const originalContextIsolated = Object.getOwnPropertyDescriptor(process, 'contextIsolated') - - beforeEach(() => { - vi.resetModules() - exposeInMainWorld.mockReset() - invoke.mockReset() - on.mockReset() - removeListener.mockReset() - send.mockReset() - sendSync.mockReset() - Object.defineProperty(process, 'contextIsolated', { configurable: true, value: true }) - vi.stubGlobal('window', { - addEventListener: vi.fn(), - dispatchEvent: vi.fn(), - removeEventListener: vi.fn() - }) - vi.stubGlobal('document', { addEventListener: vi.fn() }) - }) - - afterEach(() => { - vi.unstubAllGlobals() - if (originalContextIsolated) { - Object.defineProperty(process, 'contextIsolated', originalContextIsolated) - } else { - Reflect.deleteProperty(process, 'contextIsolated') - } - }) - - const loadApi = async (): Promise => { - await import('./index') - return exposeInMainWorld.mock.calls.find(([name]) => name === 'api')?.[1] as PreloadApi - } - - it('invokes only the instructions channel and returns the main-built command', async () => { - const api = await loadApi() - invoke.mockResolvedValueOnce({ - command: "sudo apt install -- '/cache/orca.deb'", - packageFileName: 'orca.deb' - }) - invoke.mockClear() - - await expect(api.updater.getLinuxPackageInstallInstructions()).resolves.toEqual({ - command: "sudo apt install -- '/cache/orca.deb'", - packageFileName: 'orca.deb' - }) - - expect(invoke.mock.calls).toEqual([['updater:getLinuxPackageInstallInstructions']]) - }) - - it('invokes only the show-package channel', async () => { - const api = await loadApi() - invoke.mockResolvedValueOnce(undefined) - invoke.mockClear() - - await expect(api.updater.showLinuxPackage()).resolves.toBeUndefined() - - expect(invoke.mock.calls).toEqual([['updater:showLinuxPackage']]) - }) -}) diff --git a/src/preload/usage-provider-api.test.ts b/src/preload/usage-provider-api.test.ts deleted file mode 100644 index 2faae9158e1..00000000000 --- a/src/preload/usage-provider-api.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { createUsageProviderApi } from './usage-provider-api' - -describe('usage provider preload API', () => { - it('keeps every provider on its existing IPC channels', async () => { - const invoke = vi.fn().mockResolvedValue(undefined) - const query = { scope: 'orca' as const, range: '30d' as const } - - for (const prefix of ['claudeUsage', 'codexUsage', 'openCodeUsage', 'museUsage'] as const) { - const api = createUsageProviderApi({ invoke } as never, prefix) - await api.getScanState() - await api.setEnabled({ enabled: true }) - await api.refresh() - await api.getSnapshot({ ...query, limit: 7 }) - await api.getSummary(query) - await api.getDaily(query) - await api.getBreakdown({ ...query, kind: 'project' }) - await api.getRecentSessions({ ...query, limit: 4 }) - - expect(invoke.mock.calls).toEqual([ - [`${prefix}:getScanState`], - [`${prefix}:setEnabled`, { enabled: true }], - [`${prefix}:refresh`, undefined], - [`${prefix}:getSnapshot`, { ...query, limit: 7 }], - [`${prefix}:getSummary`, query], - [`${prefix}:getDaily`, query], - [`${prefix}:getBreakdown`, { ...query, kind: 'project' }], - [`${prefix}:getRecentSessions`, { ...query, limit: 4 }] - ]) - invoke.mockClear() - } - }) -}) diff --git a/src/preload/backend-port-client.ts b/src/preload/usage/backend-port-client.ts similarity index 97% rename from src/preload/backend-port-client.ts rename to src/preload/usage/backend-port-client.ts index dae6df4e82b..13c3bec8d70 100644 --- a/src/preload/backend-port-client.ts +++ b/src/preload/usage/backend-port-client.ts @@ -1,5 +1,5 @@ import { ipcRenderer } from 'electron' -import { BackendPortRpc, type BackendPort } from '../shared/backend-port-rpc' +import { BackendPortRpc, type BackendPort } from '../../shared/backend-port-rpc' import { BACKEND_CONNECTION_CHANGED, BACKEND_PORT_DELIVERY, @@ -7,7 +7,7 @@ import { type BackendConnectionPurpose, type BackendConnectionStatus, type BackendConnectionListener -} from '../shared/backend-service-contract' +} from '../../shared/backend-service-contract' let connection: BackendPortRpc | undefined let connecting: Promise | undefined diff --git a/src/preload/usage-provider-api.ts b/src/preload/usage/usage-provider-api.ts similarity index 95% rename from src/preload/usage-provider-api.ts rename to src/preload/usage/usage-provider-api.ts index 3b9723a6aa0..ba73e86e1f9 100644 --- a/src/preload/usage-provider-api.ts +++ b/src/preload/usage/usage-provider-api.ts @@ -1,8 +1,8 @@ import type { BackendConnectionListener, BackendConnectionPurpose -} from '../shared/backend-service-contract' -import type { PreloadApi } from './api-types' +} from '../../shared/backend-service-contract' +import type { PreloadApi } from '../api-types' type UsageProviderApiKey = 'claudeUsage' | 'codexUsage' | 'openCodeUsage' | 'museUsage' type UsageProviderApi = PreloadApi[UsageProviderApiKey] diff --git a/src/renderer/src/components/plugins/catalog/PluginCatalogGroups.tsx b/src/renderer/src/components/plugins/catalog/PluginCatalogGroups.tsx index 05860ee7947..11e7d89a4a4 100644 --- a/src/renderer/src/components/plugins/catalog/PluginCatalogGroups.tsx +++ b/src/renderer/src/components/plugins/catalog/PluginCatalogGroups.tsx @@ -1,5 +1,5 @@ import { AlertTriangle, CircleAlert } from 'lucide-react' -import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api/plugin-host-api' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' import { formatShortTimeAgo } from '@/lib/time-format/short-time-ago' diff --git a/src/renderer/src/components/plugins/catalog/PluginCatalogHighlights.fixture.tsx b/src/renderer/src/components/plugins/catalog/PluginCatalogHighlights.fixture.tsx index 80f9bca736d..e63598b1df8 100644 --- a/src/renderer/src/components/plugins/catalog/PluginCatalogHighlights.fixture.tsx +++ b/src/renderer/src/components/plugins/catalog/PluginCatalogHighlights.fixture.tsx @@ -1,4 +1,4 @@ -import type { PluginMarketplaceHostListing } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostListing } from '../../../../../preload/api/plugin-host-api' import { defineFixture } from '../../../component-fixtures/component-fixture' import { usePluginInstallCountsStore } from '@/store/plugins/installation/plugin-install-counts' import { PluginListGroup } from '../list/PluginListGroup' diff --git a/src/renderer/src/components/plugins/catalog/plugin-catalog-highlights.test.ts b/src/renderer/src/components/plugins/catalog/plugin-catalog-highlights.test.ts index d76073fd690..afdf2bfdd33 100644 --- a/src/renderer/src/components/plugins/catalog/plugin-catalog-highlights.test.ts +++ b/src/renderer/src/components/plugins/catalog/plugin-catalog-highlights.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import type { PluginMarketplaceHostListing } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostListing } from '../../../../../preload/api/plugin-host-api' import { pluginListEntryFromListing, type PluginListEntry } from '../list/plugin-list-entries' import { pickFeaturedPlugins, pickNewlyAddedPlugins } from './plugin-catalog-highlights' diff --git a/src/renderer/src/components/plugins/dialogs/PluginInstallDialog.tsx b/src/renderer/src/components/plugins/dialogs/PluginInstallDialog.tsx index a84095a4aad..5f1351a7df2 100644 --- a/src/renderer/src/components/plugins/dialogs/PluginInstallDialog.tsx +++ b/src/renderer/src/components/plugins/dialogs/PluginInstallDialog.tsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { Loader2 } from 'lucide-react' -import type { PluginHostInstallSource } from '../../../../../preload/api-types' +import type { PluginHostInstallSource } from '../../../../../preload/api/plugin-host-api' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' import { Button } from '../../ui/button' diff --git a/src/renderer/src/components/plugins/dialogs/PluginMarketplaceSourceDialog.tsx b/src/renderer/src/components/plugins/dialogs/PluginMarketplaceSourceDialog.tsx index 663400c6923..8362ba2779d 100644 --- a/src/renderer/src/components/plugins/dialogs/PluginMarketplaceSourceDialog.tsx +++ b/src/renderer/src/components/plugins/dialogs/PluginMarketplaceSourceDialog.tsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { Loader2, RefreshCw, Trash2 } from 'lucide-react' -import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api/plugin-host-api' import { useTranslate } from '@/i18n/use-translate' import { Button } from '../../ui/button' import { Badge } from '../../ui/badge' diff --git a/src/renderer/src/components/plugins/dialogs/PluginRemoveDialog.tsx b/src/renderer/src/components/plugins/dialogs/PluginRemoveDialog.tsx index ee788a3cddc..c3cc9e79488 100644 --- a/src/renderer/src/components/plugins/dialogs/PluginRemoveDialog.tsx +++ b/src/renderer/src/components/plugins/dialogs/PluginRemoveDialog.tsx @@ -1,6 +1,6 @@ import { useRef } from 'react' import { Loader2 } from 'lucide-react' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { useTranslate } from '@/i18n/use-translate' import { Button } from '../../ui/button' import { diff --git a/src/renderer/src/components/plugins/dialogs/PluginRollbackDialog.tsx b/src/renderer/src/components/plugins/dialogs/PluginRollbackDialog.tsx index 130c30ff908..89e4b6075c8 100644 --- a/src/renderer/src/components/plugins/dialogs/PluginRollbackDialog.tsx +++ b/src/renderer/src/components/plugins/dialogs/PluginRollbackDialog.tsx @@ -1,6 +1,6 @@ import { useRef } from 'react' import { Loader2 } from 'lucide-react' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { useTranslate } from '@/i18n/use-translate' import { Button } from '../../ui/button' import { usePluginRollbackVersion } from '../state/use-plugin-rollback-version' diff --git a/src/renderer/src/components/plugins/dialogs/PluginUpdateAllDialog.test.tsx b/src/renderer/src/components/plugins/dialogs/PluginUpdateAllDialog.test.tsx index b246ee93c30..70a637a0e83 100644 --- a/src/renderer/src/components/plugins/dialogs/PluginUpdateAllDialog.test.tsx +++ b/src/renderer/src/components/plugins/dialogs/PluginUpdateAllDialog.test.tsx @@ -3,10 +3,8 @@ import { act } from 'react' import { createRoot, type Root } from 'react-dom/client' import { afterEach, describe, expect, it, vi } from 'vitest' -import type { - PluginHostListEntry, - PluginMarketplaceHostInstallPreview -} from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' +import type { PluginMarketplaceHostInstallPreview } from '../../../../../preload/api/plugin-host-api' import { pluginManifestSchema } from '../../../../../shared/plugins/manifest/plugin-manifest' import type { PluginCapabilityKind } from '../../../../../shared/plugins/capabilities/plugin-capabilities' import type { PluginListEntry } from '../list/plugin-list-entries' @@ -152,12 +150,6 @@ async function renderDialog( return container } -function reviewButton(container: HTMLElement, pluginKey: string): HTMLButtonElement | undefined { - return Array.from( - container.querySelectorAll(`[data-plugin-update="${pluginKey}"] button`) - ).find((candidate) => candidate.textContent === 'Review') -} - function button(container: HTMLElement, text: string): HTMLButtonElement | undefined { return Array.from(container.querySelectorAll('button')).find( (candidate) => candidate.textContent === text @@ -165,188 +157,6 @@ function button(container: HTMLElement, text: string): HTMLButtonElement | undef } describe('PluginUpdateAllDialog', () => { - it('lists every update in one dialog and bulk-installs only those asking for nothing new', async () => { - const installMarketplacePlugin = vi.fn().mockResolvedValue({ ok: true, pluginKey: 'b' }) - Object.defineProperty(window, 'api', { - configurable: true, - value: { - plugins: { - previewMarketplaceUpdate: vi.fn(({ pluginKey }: { pluginKey: string }) => - Promise.resolve(preview(pluginKey, pluginKey === 'a' ? ['secrets:own'] : [])) - ), - installMarketplacePlugin, - getInstalledManifest: vi.fn().mockResolvedValue(null) - } - } - }) - const onUpdated = vi.fn().mockResolvedValue(undefined) - const container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) - await act(async () => { - root?.render( - - ) - }) - expect(container.querySelector('h2')?.textContent).toBe('Update 2 plugins') - expect(container.textContent).toContain('New: Store its own encrypted secrets') - expect(container.querySelector('[data-plugin-update="b"]')?.textContent).toBe('b1.0.0 → 1.1.0') - - // Why no checkbox: an update that grows access is never part of the bulk Update. - expect(container.querySelector('#plugin-update-a')).toBeNull() - const confirm = Array.from(container.querySelectorAll('button')).find( - (button) => button.textContent === 'Update' - ) - await act(async () => confirm?.click()) - - expect(installMarketplacePlugin).toHaveBeenCalledTimes(1) - expect(installMarketplacePlugin).toHaveBeenCalledWith( - expect.objectContaining({ pluginKey: 'b' }) - ) - expect(onUpdated).toHaveBeenCalledWith(['b']) - }) - - it('names what failed, keeps what updated, and retries only the failures', async () => { - const installMarketplacePlugin = vi.fn(({ pluginKey }: { pluginKey: string }) => - Promise.resolve(pluginKey === 'a' ? { ok: false, error: 'boom' } : { ok: true, pluginKey }) - ) - installApi({ - previewMarketplaceUpdate: vi.fn(({ pluginKey }: { pluginKey: string }) => - Promise.resolve(preview(pluginKey, [])) - ), - installMarketplacePlugin - }) - const onUpdated = vi.fn().mockResolvedValue(undefined) - const container = await renderDialog([entry('a', []), entry('b', [])], { onUpdated }) - - await act(async () => button(container, 'Update')?.click()) - expect(container.textContent).toContain('Couldn’t update a. The others were updated.') - expect(onUpdated).toHaveBeenCalledWith(['b']) - expect(container.querySelector('[data-plugin-update="b"]')?.textContent).toContain('Updated') - - installMarketplacePlugin.mockClear() - await act(async () => button(container, 'Try again')?.click()) - expect(installMarketplacePlugin).toHaveBeenCalledTimes(1) - expect(installMarketplacePlugin).toHaveBeenCalledWith( - expect.objectContaining({ pluginKey: 'a' }) - ) - // Why: "b" updated on the first pass, so a second failure isn't "nothing was updated". - expect(container.textContent).toContain('Couldn’t update a. The others were updated.') - }) - - it('keeps the set it opened with while the list reloads mid-run', async () => { - const previewMarketplaceUpdate = vi.fn(({ pluginKey }: { pluginKey: string }) => - Promise.resolve(preview(pluginKey, [])) - ) - installApi({ - previewMarketplaceUpdate, - installMarketplacePlugin: vi.fn(({ pluginKey }: { pluginKey: string }) => - Promise.resolve(pluginKey === 'a' ? { ok: false, error: 'boom' } : { ok: true, pluginKey }) - ) - }) - const onUpdated = vi.fn().mockResolvedValue(undefined) - const container = await renderDialog([entry('a', []), entry('b', [])], { onUpdated }) - await act(async () => button(container, 'Update')?.click()) - // Why: after "b" updates, the reloaded list no longer has it among pending updates. - await act(async () => { - root?.render( - - ) - }) - expect(previewMarketplaceUpdate).toHaveBeenCalledTimes(2) - expect(container.querySelector('[data-plugin-update="b"]')?.textContent).toContain('Updated') - }) - - it('approves an update that grows access only from its own before/after review', async () => { - const installMarketplacePlugin = vi.fn(({ pluginKey }: { pluginKey: string }) => - Promise.resolve({ ok: true, pluginKey, consentFingerprint: 'f' }) - ) - const consent = vi.fn().mockResolvedValue([]) - installApi({ - previewMarketplaceUpdate: vi.fn(({ pluginKey }: { pluginKey: string }) => - Promise.resolve(pluginKey === 'a' ? workerUpdate('a', false) : preview(pluginKey, [])) - ), - installMarketplacePlugin, - consent - }) - const onUpdated = vi.fn().mockResolvedValue(undefined) - const container = await renderDialog([entry('a', ['storage:own']), entry('b', [])], { - onUpdated - }) - - await act(async () => reviewButton(container, 'a')?.click()) - expect(container.querySelector('h2')?.textContent).toBe('Review update to a') - const review = container.querySelector('[data-plugin-update-review="a"]') - expect(review?.textContent).toContain('BeforeContent onlyStore its own data') - expect(review?.textContent).toContain('New: Full access') - expect(review?.textContent).toContain('New: Store its own encrypted secrets') - expect(review?.textContent).toContain('Not an official plugin:') - expect(installMarketplacePlugin).not.toHaveBeenCalled() - - await act(async () => button(container, 'Approve & update')?.click()) - expect(installMarketplacePlugin).toHaveBeenCalledTimes(1) - expect(installMarketplacePlugin).toHaveBeenCalledWith( - expect.objectContaining({ pluginKey: 'a' }) - ) - expect(consent).toHaveBeenCalledWith({ - pluginKey: 'a', - reviewedFingerprint: 'f', - decision: 'approve' - }) - expect(onUpdated).toHaveBeenCalledWith(['a']) - // Why back to the list: "b" still waits for the bulk Update. - expect(container.querySelector('[data-plugin-update="a"]')?.textContent).toContain('Updated') - }) - - it('opens a lone content-only update from a moved repository to its own review', async () => { - const installMarketplacePlugin = vi.fn(({ pluginKey }: { pluginKey: string }) => - Promise.resolve({ ok: true, pluginKey, consentFingerprint: 'f', needsReview: true }) - ) - const consent = vi.fn().mockResolvedValue([]) - installApi({ - previewMarketplaceUpdate: vi.fn(() => - Promise.resolve({ - ...preview('a', []), - source: { kind: 'git', url: 'https://example.com/someone-else/theme.git', ref: 'main' }, - repositoryMoved: true as const - }) - ), - installMarketplacePlugin, - consent - }) - const theme = entry('a', []) - theme.installed!.source = { - kind: 'marketplace', - reference: 'https://example.com/acme/theme.git', - resolvedCommit: 'r0', - contentHash: 'h0' - } - const container = await renderDialog([theme]) - - // Same access, so only the source line explains why this one asks again. - expect(container.querySelector('h2')?.textContent).toBe('Review update to a') - expect(container.textContent).toContain( - 'New source: https://example.com/someone-else/theme.git (was https://example.com/acme/theme.git)' - ) - expect(installMarketplacePlugin).not.toHaveBeenCalled() - await act(async () => button(container, 'Approve & update')?.click()) - expect(consent).toHaveBeenCalledWith({ - pluginKey: 'a', - reviewedFingerprint: 'f', - decision: 'approve' - }) - }) - it('never approves bytes other than the ones it reviewed', async () => { const consent = vi.fn().mockResolvedValue([]) installApi({ diff --git a/src/renderer/src/components/plugins/dialogs/PluginUpdateAllDialog.tsx b/src/renderer/src/components/plugins/dialogs/PluginUpdateAllDialog.tsx index 67d734cdb92..699f1c7a3d9 100644 --- a/src/renderer/src/components/plugins/dialogs/PluginUpdateAllDialog.tsx +++ b/src/renderer/src/components/plugins/dialogs/PluginUpdateAllDialog.tsx @@ -1,6 +1,6 @@ import { useEffect, useLayoutEffect, useRef, useState } from 'react' import { Loader2 } from 'lucide-react' -import type { PluginMarketplaceHostInstallPreview } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostInstallPreview } from '../../../../../preload/api/plugin-host-api' import { satisfiesOrcaEngineRange } from '../../../../../shared/plugins/manifest/plugin-manifest' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/plugins/list/PluginFilterPopover.tsx b/src/renderer/src/components/plugins/list/PluginFilterPopover.tsx index 41348461f4a..81ace9ed222 100644 --- a/src/renderer/src/components/plugins/list/PluginFilterPopover.tsx +++ b/src/renderer/src/components/plugins/list/PluginFilterPopover.tsx @@ -1,5 +1,5 @@ import { ListFilter } from 'lucide-react' -import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api/plugin-host-api' import { useTranslate } from '@/i18n/use-translate' import { Button } from '../../ui/button' import { Checkbox } from '../../ui/checkbox' diff --git a/src/renderer/src/components/plugins/list/PluginListToolbar.tsx b/src/renderer/src/components/plugins/list/PluginListToolbar.tsx index 8ca9164ef4d..30e7b9ddda9 100644 --- a/src/renderer/src/components/plugins/list/PluginListToolbar.tsx +++ b/src/renderer/src/components/plugins/list/PluginListToolbar.tsx @@ -1,5 +1,5 @@ import { RefreshCw } from 'lucide-react' -import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api/plugin-host-api' import { useTranslate } from '@/i18n/use-translate' import { Button } from '../../ui/button' import { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip' diff --git a/src/renderer/src/components/plugins/list/PluginManager.test.tsx b/src/renderer/src/components/plugins/list/PluginManager.test.tsx deleted file mode 100644 index 9074843f380..00000000000 --- a/src/renderer/src/components/plugins/list/PluginManager.test.tsx +++ /dev/null @@ -1,217 +0,0 @@ -// @vitest-environment happy-dom - -import { act } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { - PluginHostListEntry, - PluginMarketplaceHostListing, - PluginMarketplaceHostSourceState -} from '../../../../../preload/api-types' -import { OFFICIAL_MARKETPLACE_GIT_SOURCE } from '../../../../../shared/plugins/catalog/plugin-marketplace' -import { PLUGIN_GROUP_ROW_CAP, PluginManager } from './PluginManager' -import { usePluginPageNavigation } from '../state/use-plugin-page-navigation' - -vi.mock('../../ui/tooltip', () => ({ - Tooltip: ({ children }: { children: React.ReactNode }) => <>{children}, - TooltipTrigger: ({ children }: { children: React.ReactNode }) => <>{children}, - TooltipContent: () => null -})) -vi.mock('../dialogs/PluginMarketplaceSourceDialog', () => ({ - PluginMarketplaceSourceDialog: () => null -})) - -const installedPlugin: PluginHostListEntry = { - pluginKey: 'stablyai.orca-nord-theme', - consentFingerprint: 'sha256-nord', - name: 'Orca Nord', - version: '1.0.0', - publisher: 'stablyai', - status: 'idle', - needsReconsent: false, - isDev: false, - official: true, - bundled: false, - capabilities: [], - panels: [], - commands: [], - hasWorker: false, - restarts: 0, - source: { - kind: 'marketplace', - reference: 'https://example.invalid/nord.git', - resolvedCommit: 'a'.repeat(40), - contentHash: 'b'.repeat(64) - } -} - -const source: PluginMarketplaceHostSourceState = { - id: 'official', - source: { kind: 'git', url: 'https://example.invalid/catalog.git', ref: 'main' }, - addedAt: 0, - marketplace: { - name: 'Orca Official Plugins', - owner: 'stablyai', - resolvedCommit: 'c', - fetchedAt: 0 - }, - stale: false, - official: true -} - -function listing(pluginKey: string, extra: Partial = {}) { - const value: PluginMarketplaceHostListing = { - marketplaceSourceId: 'official', - marketplaceName: 'Orca Official Plugins', - marketplaceOwner: 'stablyai', - marketplaceCommit: 'c', - pluginKey, - source: { kind: 'git', url: 'https://example.invalid/plugin.git', ref: 'main' }, - description: `Description of ${pluginKey}`, - categories: ['themes'], - official: true, - bundled: false, - ...extra - } - return value -} - -const catalogListings = [ - listing('stablyai.orca-nord-theme', { - release: { - version: '1.2.0', - commit: 'd'.repeat(40), - contentHash: 'e'.repeat(64), - execution: 'data', - minimumOrca: '1.4.0', - types: ['app-theme'], - name: 'Orca Nord' - }, - license: 'MIT', - changelog: 'Warmer comments.\nnot markup' - }), - ...Array.from({ length: PLUGIN_GROUP_ROW_CAP + 2 }, (_, index) => - listing(`stablyai.extra-${index}`) - ) -] - -let root: Root | null = null - -function installApi(overrides: Record = {}): void { - Object.defineProperty(window, 'api', { - configurable: true, - value: { - plugins: { - listMarketplaces: vi.fn().mockResolvedValue([source]), - listMarketplacePlugins: vi.fn().mockResolvedValue(catalogListings), - refreshMarketplaces: vi.fn().mockResolvedValue(undefined), - previewMarketplacePlugin: vi.fn().mockResolvedValue(null), - getRollbackVersion: vi.fn().mockResolvedValue('1.0.0-rc.1'), - getInstalledManifest: vi.fn().mockResolvedValue(null), - ...overrides - }, - updater: { getVersion: vi.fn().mockResolvedValue('1.4.214') }, - ui: { writeClipboardText: vi.fn() } - } - }) -} - -function Harness({ plugins }: { plugins: PluginHostListEntry[] }): React.JSX.Element { - const page = usePluginPageNavigation() - return ( - - ) -} - -async function render(plugins: PluginHostListEntry[] = [installedPlugin]): Promise { - const container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) - await act(async () => { - root?.render() - }) - return container -} - -beforeEach(() => installApi()) - -afterEach(() => { - act(() => root?.unmount()) - root = null - document.body.innerHTML = '' -}) - -describe('PluginManager', () => { - it('keeps installed plugins when the catalog never loads', async () => { - installApi({ listMarketplaces: vi.fn().mockRejectedValue(new Error('offline')) }) - const container = await render() - expect(container.querySelector('[data-plugin-key="stablyai.orca-nord-theme"]')).not.toBeNull() - expect(container.textContent).toContain('Couldn’t load the plugin catalog.') - }) - - it('renders each plugin card once and no Featured row when nothing is newly added', async () => { - const popular = ['themes', 'editors', 'icons', 'panels'].map((category, index) => - listing(`stablyai.popular-${index}`, { categories: [category] }) - ) - installApi({ - // Why the real official URL: only Orca's public catalogs have install counts to feature. - listMarketplaces: vi - .fn() - .mockResolvedValue([{ ...source, source: OFFICIAL_MARKETPLACE_GIT_SOURCE }]), - listMarketplacePlugins: vi.fn().mockResolvedValue([...popular, ...catalogListings.slice(1)]), - getInstallCounts: vi - .fn() - .mockResolvedValue( - Object.fromEntries(popular.map((item, index) => [item.pluginKey, 1000 - index])) - ) - }) - const container = await render([]) - const keys = [...container.querySelectorAll('[data-plugin-key]')].map((card) => - card.getAttribute('data-plugin-key') - ) - - expect(keys).toContain('stablyai.popular-0') - expect(new Set(keys).size).toBe(keys.length) - expect(container.textContent).not.toContain('Featured') - }) - - it('shows release notes as text, license and the rollback version on the plugin page', async () => { - const container = await render() - await act(async () => - container - .querySelector( - '[data-plugin-key="stablyai.orca-nord-theme"] [data-plugin-row-name]' - ) - ?.click() - ) - expect(window.api.plugins.getRollbackVersion).toHaveBeenCalledWith({ - pluginKey: 'stablyai.orca-nord-theme' - }) - expect(container.textContent).toContain('What’s new') - expect(container.textContent).toContain('not markup') - expect(container.querySelector('b')).toBeNull() - expect(container.textContent).toContain('MIT') - expect(container.textContent).toContain('1.0.0-rc.1') - }) -}) diff --git a/src/renderer/src/components/plugins/list/PluginManager.tsx b/src/renderer/src/components/plugins/list/PluginManager.tsx index 4682667e00d..2c0a7106399 100644 --- a/src/renderer/src/components/plugins/list/PluginManager.tsx +++ b/src/renderer/src/components/plugins/list/PluginManager.tsx @@ -1,5 +1,5 @@ import { useCallback, useState } from 'react' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' import { Button } from '../../ui/button' diff --git a/src/renderer/src/components/plugins/list/PluginManagerRows.tsx b/src/renderer/src/components/plugins/list/PluginManagerRows.tsx index a24562edc75..37a72874ef3 100644 --- a/src/renderer/src/components/plugins/list/PluginManagerRows.tsx +++ b/src/renderer/src/components/plugins/list/PluginManagerRows.tsx @@ -1,5 +1,5 @@ import { ArrowUpCircle, Copy, FileText, Hash, Loader2, RotateCcw, Trash2 } from 'lucide-react' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' import { Button } from '../../ui/button' diff --git a/src/renderer/src/components/plugins/list/PluginSettingsOverview.tsx b/src/renderer/src/components/plugins/list/PluginSettingsOverview.tsx index ed39d102d8e..1f42f5db18d 100644 --- a/src/renderer/src/components/plugins/list/PluginSettingsOverview.tsx +++ b/src/renderer/src/components/plugins/list/PluginSettingsOverview.tsx @@ -1,4 +1,4 @@ -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { PluginManager } from './PluginManager' import { PluginsOffState } from './PluginsOffState' import type { InstalledRowHandlers } from './PluginManagerRows' diff --git a/src/renderer/src/components/plugins/list/plugin-list-entries.test.ts b/src/renderer/src/components/plugins/list/plugin-list-entries.test.ts index dc1056f5844..ae129d7ac73 100644 --- a/src/renderer/src/components/plugins/list/plugin-list-entries.test.ts +++ b/src/renderer/src/components/plugins/list/plugin-list-entries.test.ts @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import type { - PluginHostListEntry, PluginMarketplaceHostListing, PluginMarketplaceHostSourceState -} from '../../../../../preload/api-types' +} from '../../../../../preload/api/plugin-host-api' import { buildPluginListEntries } from './plugin-list-entries' function listing(pluginKey: string, extra: Partial = {}) { diff --git a/src/renderer/src/components/plugins/list/plugin-list-entries.ts b/src/renderer/src/components/plugins/list/plugin-list-entries.ts index 3cd87cf6fa0..355d7fdacb1 100644 --- a/src/renderer/src/components/plugins/list/plugin-list-entries.ts +++ b/src/renderer/src/components/plugins/list/plugin-list-entries.ts @@ -1,8 +1,8 @@ +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import type { - PluginHostListEntry, PluginMarketplaceHostListing, PluginMarketplaceHostSourceState -} from '../../../../../preload/api-types' +} from '../../../../../preload/api/plugin-host-api' import { satisfiesOrcaEngineRange } from '../../../../../shared/plugins/manifest/plugin-manifest' import { pluginCatalogReleaseTypes, diff --git a/src/renderer/src/components/plugins/list/plugin-public-catalog.ts b/src/renderer/src/components/plugins/list/plugin-public-catalog.ts index 11d2c98b745..295cd45bf11 100644 --- a/src/renderer/src/components/plugins/list/plugin-public-catalog.ts +++ b/src/renderer/src/components/plugins/list/plugin-public-catalog.ts @@ -1,7 +1,7 @@ import type { PluginMarketplaceHostListing, PluginMarketplaceHostSourceState -} from '../../../../../preload/api-types' +} from '../../../../../preload/api/plugin-host-api' import { isOfficialMarketplaceGitSource, isSameGitRepository diff --git a/src/renderer/src/components/plugins/page/PluginAddsSection.test.tsx b/src/renderer/src/components/plugins/page/PluginAddsSection.test.tsx index 5e603c168a4..abb75a56361 100644 --- a/src/renderer/src/components/plugins/page/PluginAddsSection.test.tsx +++ b/src/renderer/src/components/plugins/page/PluginAddsSection.test.tsx @@ -2,7 +2,7 @@ import '@testing-library/jest-dom/vitest' import { cleanup, fireEvent, render, screen } from '@testing-library/react' import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { useAppStore } from '@/store' import { usePluginLanguagePackStore } from '@/store/plugins/localization/plugin-language-packs' import { usePluginSyntaxStore } from '@/store/plugins/syntax/plugin-syntax-packs' diff --git a/src/renderer/src/components/plugins/page/PluginAddsSection.tsx b/src/renderer/src/components/plugins/page/PluginAddsSection.tsx index 4f2ee9cb711..df3ee47e8ec 100644 --- a/src/renderer/src/components/plugins/page/PluginAddsSection.tsx +++ b/src/renderer/src/components/plugins/page/PluginAddsSection.tsx @@ -15,10 +15,8 @@ import { SquareTerminal, Type } from 'lucide-react' -import type { - PluginHostListEntry, - PluginMarketplaceHostInstallPreview -} from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' +import type { PluginMarketplaceHostInstallPreview } from '../../../../../preload/api/plugin-host-api' import { formatKeybinding } from '../../../../../shared/keybindings/formatting' import type { PluginCatalogType } from '../../../../../shared/plugins/catalog/plugin-catalog-release' import { translate } from '@/i18n/i18n' diff --git a/src/renderer/src/components/plugins/page/PluginContributionActionEvents.tsx b/src/renderer/src/components/plugins/page/PluginContributionActionEvents.tsx index e42a3848050..10617501da1 100644 --- a/src/renderer/src/components/plugins/page/PluginContributionActionEvents.tsx +++ b/src/renderer/src/components/plugins/page/PluginContributionActionEvents.tsx @@ -1,5 +1,5 @@ import { Zap } from 'lucide-react' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import type { PluginEventName } from '../../../../../shared/plugins/contributions/plugin-event-contribution' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/plugins/page/PluginContributionActionPanel.tsx b/src/renderer/src/components/plugins/page/PluginContributionActionPanel.tsx index ca2efc2b2d1..f191fb320e8 100644 --- a/src/renderer/src/components/plugins/page/PluginContributionActionPanel.tsx +++ b/src/renderer/src/components/plugins/page/PluginContributionActionPanel.tsx @@ -1,4 +1,4 @@ -import type { PluginHostPanel } from '../../../../../preload/api-types' +import type { PluginHostPanel } from '../../../../../shared/plugins/plugin-list-entry' import { useTranslate } from '@/i18n/use-translate' import { useAppStore } from '@/store' import { Button } from '../../ui/button' diff --git a/src/renderer/src/components/plugins/page/PluginDetailPage.fixture.tsx b/src/renderer/src/components/plugins/page/PluginDetailPage.fixture.tsx index 0704a2a80ae..af29f4412fa 100644 --- a/src/renderer/src/components/plugins/page/PluginDetailPage.fixture.tsx +++ b/src/renderer/src/components/plugins/page/PluginDetailPage.fixture.tsx @@ -1,4 +1,4 @@ -import type { PluginMarketplaceHostListing } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostListing } from '../../../../../preload/api/plugin-host-api' import { defineFixture } from '../../../component-fixtures/component-fixture' import { usePluginInstallCountsStore } from '@/store/plugins/installation/plugin-install-counts' import { diff --git a/src/renderer/src/components/plugins/page/PluginDetailPage.test.tsx b/src/renderer/src/components/plugins/page/PluginDetailPage.test.tsx index dd49d08965f..3ecc8c94bcd 100644 --- a/src/renderer/src/components/plugins/page/PluginDetailPage.test.tsx +++ b/src/renderer/src/components/plugins/page/PluginDetailPage.test.tsx @@ -6,7 +6,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { PluginMarketplaceHostInstallPreview, PluginMarketplaceHostListing -} from '../../../../../preload/api-types' +} from '../../../../../preload/api/plugin-host-api' import { usePluginSyntaxFailureStore } from '@/store/plugins/syntax/plugin-syntax-failures' import { usePluginSyntaxStore } from '@/store/plugins/syntax/plugin-syntax-packs' import { usePluginThemeStore } from '@/store/plugins/appearance/plugin-themes' diff --git a/src/renderer/src/components/plugins/page/plugin-contribution-action-availability.ts b/src/renderer/src/components/plugins/page/plugin-contribution-action-availability.ts index fca72c65538..4ad7b884417 100644 --- a/src/renderer/src/components/plugins/page/plugin-contribution-action-availability.ts +++ b/src/renderer/src/components/plugins/page/plugin-contribution-action-availability.ts @@ -1,4 +1,4 @@ -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { isWorkspaceScopedPluginContext } from '../../../../../shared/plugins/contributions/plugin-command-contribution' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/plugins/presentation/plugin-access-profile.ts b/src/renderer/src/components/plugins/presentation/plugin-access-profile.ts index c6004f47199..a6a2b2f6ba1 100644 --- a/src/renderer/src/components/plugins/presentation/plugin-access-profile.ts +++ b/src/renderer/src/components/plugins/presentation/plugin-access-profile.ts @@ -1,7 +1,5 @@ -import type { - PluginHostListEntry, - PluginMarketplaceHostListing -} from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' +import type { PluginMarketplaceHostListing } from '../../../../../preload/api/plugin-host-api' import { pluginExecutionProfile, type PluginExecutionProfile diff --git a/src/renderer/src/components/plugins/presentation/plugin-install-trust.ts b/src/renderer/src/components/plugins/presentation/plugin-install-trust.ts index 0a35ca38449..b17c458acd1 100644 --- a/src/renderer/src/components/plugins/presentation/plugin-install-trust.ts +++ b/src/renderer/src/components/plugins/presentation/plugin-install-trust.ts @@ -2,7 +2,7 @@ import type { PluginHostInstallSource, PluginMarketplaceHostInstallPreview, PluginMarketplaceHostListing -} from '../../../../../preload/api-types' +} from '../../../../../preload/api/plugin-host-api' import { pluginExecutionProfile } from '../../../../../shared/plugins/contributions/plugin-execution-profile' import { splitQualifiedPluginKey } from '../../../../../shared/plugins/catalog/plugin-marketplace' import { pluginRepositoryOrigin } from '../../../../../shared/plugins/catalog/plugin-repository-link' diff --git a/src/renderer/src/components/plugins/presentation/plugin-status-presentation.ts b/src/renderer/src/components/plugins/presentation/plugin-status-presentation.ts index d739be7111e..b650d519a27 100644 --- a/src/renderer/src/components/plugins/presentation/plugin-status-presentation.ts +++ b/src/renderer/src/components/plugins/presentation/plugin-status-presentation.ts @@ -1,4 +1,4 @@ -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/plugins/review/PluginInstallConsent.tsx b/src/renderer/src/components/plugins/review/PluginInstallConsent.tsx index 09bfbd8bf4b..31636fb8388 100644 --- a/src/renderer/src/components/plugins/review/PluginInstallConsent.tsx +++ b/src/renderer/src/components/plugins/review/PluginInstallConsent.tsx @@ -1,6 +1,6 @@ import { useId } from 'react' import { AlertTriangle, Check, Loader2, ShieldAlert } from 'lucide-react' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { getIntlLocale } from '@/i18n/i18n' import { useIntlLocale, useTranslate } from '@/i18n/use-translate' import { getShortcutPlatform } from '@/lib/keyboard/shortcut-platform' diff --git a/src/renderer/src/components/plugins/review/PluginUpdateCandidate.tsx b/src/renderer/src/components/plugins/review/PluginUpdateCandidate.tsx index db9cb757573..371aac0478e 100644 --- a/src/renderer/src/components/plugins/review/PluginUpdateCandidate.tsx +++ b/src/renderer/src/components/plugins/review/PluginUpdateCandidate.tsx @@ -1,5 +1,5 @@ import { Check, ChevronDown } from 'lucide-react' -import type { PluginMarketplaceHostInstallPreview } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostInstallPreview } from '../../../../../preload/api/plugin-host-api' import { pluginExecutionProfile } from '../../../../../shared/plugins/contributions/plugin-execution-profile' import { pluginCapabilityReachable, diff --git a/src/renderer/src/components/plugins/review/PluginUpdateReview.tsx b/src/renderer/src/components/plugins/review/PluginUpdateReview.tsx index 8dccb41a6fc..4dd43a8f758 100644 --- a/src/renderer/src/components/plugins/review/PluginUpdateReview.tsx +++ b/src/renderer/src/components/plugins/review/PluginUpdateReview.tsx @@ -1,5 +1,5 @@ import { ArrowRight } from 'lucide-react' -import type { PluginMarketplaceHostInstallPreview } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostInstallPreview } from '../../../../../preload/api/plugin-host-api' import { pluginExecutionProfile } from '../../../../../shared/plugins/contributions/plugin-execution-profile' import { reachablePluginCapabilities } from '../../../../../shared/plugins/capabilities/plugin-capability-reach' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/plugins/review/PluginVmRecipeConsentPreview.tsx b/src/renderer/src/components/plugins/review/PluginVmRecipeConsentPreview.tsx index 3ec8748498c..ea833f15fab 100644 --- a/src/renderer/src/components/plugins/review/PluginVmRecipeConsentPreview.tsx +++ b/src/renderer/src/components/plugins/review/PluginVmRecipeConsentPreview.tsx @@ -1,4 +1,4 @@ -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { PluginReviewSection } from './plugin-review-dialog-layout' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/plugins/state/plugin-preview-cache.ts b/src/renderer/src/components/plugins/state/plugin-preview-cache.ts index 7ee5c867bb7..43f5401e0cd 100644 --- a/src/renderer/src/components/plugins/state/plugin-preview-cache.ts +++ b/src/renderer/src/components/plugins/state/plugin-preview-cache.ts @@ -1,4 +1,4 @@ -import type { PluginMarketplaceHostListing } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostListing } from '../../../../../preload/api/plugin-host-api' import type { PluginPagePreview } from './use-plugin-catalog' /** Plugins whose preview one Settings visit keeps; each holds a parsed manifest. */ diff --git a/src/renderer/src/components/plugins/state/use-plugin-art-palettes.ts b/src/renderer/src/components/plugins/state/use-plugin-art-palettes.ts index 7cad8688a13..1653258bd2e 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-art-palettes.ts +++ b/src/renderer/src/components/plugins/state/use-plugin-art-palettes.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react' -import type { PluginMarketplaceHostListing } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostListing } from '../../../../../preload/api/plugin-host-api' import { usePluginTerminalThemes, usePluginThemes } from '@/store/plugins/appearance/plugin-themes' import type { PluginArtPalette } from '../list/PluginCardArt' diff --git a/src/renderer/src/components/plugins/state/use-plugin-catalog.ts b/src/renderer/src/components/plugins/state/use-plugin-catalog.ts index 7bd93a7ea59..139c996ba5f 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-catalog.ts +++ b/src/renderer/src/components/plugins/state/use-plugin-catalog.ts @@ -3,7 +3,7 @@ import type { PluginMarketplaceHostInstallPreview, PluginMarketplaceHostListing, PluginMarketplaceHostSourceState -} from '../../../../../preload/api-types' +} from '../../../../../preload/api/plugin-host-api' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' import { usePluginPreviewRequestStore } from '@/store/plugins/navigation/plugin-preview-request' diff --git a/src/renderer/src/components/plugins/state/use-plugin-contribution-source.ts b/src/renderer/src/components/plugins/state/use-plugin-contribution-source.ts index 7507945b66c..c53b5768d04 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-contribution-source.ts +++ b/src/renderer/src/components/plugins/state/use-plugin-contribution-source.ts @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from 'react' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import type { PluginManifest } from '../../../../../shared/plugins/manifest/plugin-manifest' import { usePluginIconThemes } from '@/store/plugins/appearance/plugin-icon-themes' import { diff --git a/src/renderer/src/components/plugins/state/use-plugin-hover-prefetch.ts b/src/renderer/src/components/plugins/state/use-plugin-hover-prefetch.ts index 241b7ae2cb8..035ccb780de 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-hover-prefetch.ts +++ b/src/renderer/src/components/plugins/state/use-plugin-hover-prefetch.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef } from 'react' -import type { PluginMarketplaceHostListing } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostListing } from '../../../../../preload/api/plugin-host-api' import type { PluginPagePreview } from './use-plugin-catalog' /** Pointer rest before a hover counts as intent. */ diff --git a/src/renderer/src/components/plugins/state/use-plugin-install-action.test.tsx b/src/renderer/src/components/plugins/state/use-plugin-install-action.test.tsx deleted file mode 100644 index 93e0589f6c9..00000000000 --- a/src/renderer/src/components/plugins/state/use-plugin-install-action.test.tsx +++ /dev/null @@ -1,148 +0,0 @@ -// @vitest-environment happy-dom - -import { act } from 'react' -import { createRoot } from 'react-dom/client' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { - PluginMarketplaceHostInstallPreview, - PluginMarketplaceHostListing -} from '../../../../../preload/api-types' -import { pluginManifestSchema } from '../../../../../shared/plugins/manifest/plugin-manifest' -import { - settlePluginInstallTrust, - usePluginInstallTrustStore -} from '@/store/plugins/installation/plugin-install-trust-request' -import type { PluginInstallTrustPrompt } from '../presentation/plugin-install-trust' -import { usePluginInstallAction } from './use-plugin-install-action' - -vi.mock('sonner', () => ({ toast: { success: vi.fn(), warning: vi.fn() } })) -vi.mock('./plugin-theme-apply', () => ({ applyPluginThemesNow: vi.fn() })) -import { toast } from 'sonner' -import { applyPluginThemesNow } from './plugin-theme-apply' - -const listing: PluginMarketplaceHostListing = { - marketplaceSourceId: 'source', - marketplaceName: 'Catalog', - marketplaceOwner: 'acme', - marketplaceCommit: 'b'.repeat(40), - pluginKey: 'acme.dusk', - source: { kind: 'git', url: 'https://example.invalid/dusk.git', ref: 'main' }, - categories: [], - official: false, - bundled: false, - release: { - version: '1.0.0', - commit: 'c'.repeat(40), - contentHash: 'd'.repeat(64), - execution: 'data', - minimumOrca: '1.0.0', - types: ['app-theme'] - } -} - -const preview: PluginMarketplaceHostInstallPreview = { - ...listing, - resolvedCommit: 'c'.repeat(40), - contentHash: 'd'.repeat(64), - consentFingerprint: 'sha256-shown', - manifest: pluginManifestSchema.parse({ - manifestVersion: 1, - id: 'dusk', - publisher: 'acme', - name: 'Dusk', - version: '1.0.0', - engines: { orca: '>=1.0.0' }, - pluginApi: 1, - contributes: { themes: [{ id: 'dusk', title: 'Dusk', path: 'themes/dusk.json' }] } - }) -} - -const codePreview: PluginMarketplaceHostInstallPreview = { - ...preview, - manifest: pluginManifestSchema.parse({ - ...preview.manifest, - contributes: { commands: [{ id: 'go', title: 'Go', action: 'sidebar.left.toggle' }] } - }) -} - -function setup(isFromInstallLink?: (pluginKey: string) => boolean) { - const onInstalled = vi.fn().mockResolvedValue(undefined) - const fetchPreview = vi.fn().mockResolvedValue({ ok: true, preview }) - const hook: { current: ReturnType | null } = { current: null } - function Probe(): null { - hook.current = usePluginInstallAction({ fetchPreview, onInstalled, isFromInstallLink }) - return null - } - act(() => createRoot(document.createElement('div')).render()) - return { hook, onInstalled, fetchPreview } -} - -function installResult(on: boolean) { - return { - ok: true as const, - pluginKey: listing.pluginKey, - version: '1.0.0', - contentHash: 'd'.repeat(64), - consentFingerprint: 'sha256-shown', - resolvedCommit: 'c'.repeat(40), - needsReview: !on - } -} - -// Answers every trust ask the way the current test chose, recording what was shown. -const asked: PluginInstallTrustPrompt[] = [] -let answer = true -usePluginInstallTrustStore.subscribe(({ pending }) => { - if (pending) { - asked.push(pending.prompt) - queueMicrotask(() => settlePluginInstallTrust(answer)) - } -}) - -beforeEach(() => { - asked.length = 0 - answer = true - vi.mocked(toast.success).mockClear() - vi.mocked(toast.warning).mockClear() - vi.mocked(applyPluginThemesNow).mockReset().mockResolvedValue(false) - Object.defineProperty(window, 'api', { - configurable: true, - value: { - plugins: { - installMarketplacePlugin: vi.fn().mockResolvedValue(installResult(true)), - consent: vi.fn().mockResolvedValue([]) - } - } - }) -}) - -describe('usePluginInstallAction', () => { - it('leaves an install link’s replacement from a moved repository off and unapproved', async () => { - const codeListing = { - ...listing, - release: { ...listing.release!, execution: 'declarative' as const } - } - const movedCode = { ...codePreview, repositoryMoved: true as const } - for (const [target, moved, approve] of [ - [listing, { ...preview, repositoryMoved: true as const }, undefined], - [codeListing, movedCode, { preview: movedCode }] - ] as const) { - const { hook, fetchPreview, onInstalled } = setup(() => true) - fetchPreview.mockResolvedValue({ ok: true, preview: moved }) - vi.mocked(window.api.plugins.installMarketplacePlugin).mockResolvedValue(installResult(false)) - await act(async () => hook.current?.install(target, approve)) - expect(window.api.plugins.installMarketplacePlugin).toHaveBeenLastCalledWith( - expect.not.objectContaining({ enableIfData: true }) - ) - expect(window.api.plugins.consent).not.toHaveBeenCalled() - expect(applyPluginThemesNow).not.toHaveBeenCalled() - expect(hook.current?.phaseOf(listing.pluginKey)).toBeNull() - expect(onInstalled).toHaveBeenCalledWith(listing.pluginKey) - expect(toast.warning).toHaveBeenLastCalledWith( - 'Dusk 1.0.0 comes from a different repository', - expect.objectContaining({ description: 'It stays off until you review it.' }) - ) - expect(toast.success).not.toHaveBeenCalled() - } - }) -}) diff --git a/src/renderer/src/components/plugins/state/use-plugin-install-action.ts b/src/renderer/src/components/plugins/state/use-plugin-install-action.ts index 763cf3231f5..7aa390a7ad0 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-install-action.ts +++ b/src/renderer/src/components/plugins/state/use-plugin-install-action.ts @@ -3,7 +3,7 @@ import { toast } from 'sonner' import type { PluginMarketplaceHostInstallPreview, PluginMarketplaceHostListing -} from '../../../../../preload/api-types' +} from '../../../../../preload/api/plugin-host-api' import { pluginExecutionProfile } from '../../../../../shared/plugins/contributions/plugin-execution-profile' import { translate } from '@/i18n/i18n' import { pluginInstallErrorMessage } from '../presentation/plugin-error-presentation' diff --git a/src/renderer/src/components/plugins/state/use-plugin-install-link.test.tsx b/src/renderer/src/components/plugins/state/use-plugin-install-link.test.tsx index 67b758c34f3..10f6017c80a 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-install-link.test.tsx +++ b/src/renderer/src/components/plugins/state/use-plugin-install-link.test.tsx @@ -3,7 +3,7 @@ import { act } from 'react' import { createRoot, type Root } from 'react-dom/client' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api/plugin-host-api' import { parsePluginInstallLink } from '../../../../../shared/plugins/catalog/plugin-install-link' import { usePluginPreviewRequestStore } from '@/store/plugins/navigation/plugin-preview-request' import { usePluginInstallLink } from './use-plugin-install-link' diff --git a/src/renderer/src/components/plugins/state/use-plugin-install-link.ts b/src/renderer/src/components/plugins/state/use-plugin-install-link.ts index 3641aed31a4..6e30f077d07 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-install-link.ts +++ b/src/renderer/src/components/plugins/state/use-plugin-install-link.ts @@ -1,5 +1,5 @@ import { useEffect, useLayoutEffect, useRef } from 'react' -import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api-types' +import type { PluginMarketplaceHostSourceState } from '../../../../../preload/api/plugin-host-api' import { isSameGitRepository } from '../../../../../shared/plugins/catalog/plugin-marketplace' import type { PluginInstallLinkCatalog } from '../../../../../shared/plugins/catalog/plugin-install-link' import { usePluginPreviewRequestStore } from '@/store/plugins/navigation/plugin-preview-request' diff --git a/src/renderer/src/components/plugins/state/use-plugin-logs.ts b/src/renderer/src/components/plugins/state/use-plugin-logs.ts index 9b1b173b676..79856bf4b20 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-logs.ts +++ b/src/renderer/src/components/plugins/state/use-plugin-logs.ts @@ -1,5 +1,6 @@ import { useEffect, useRef, useState, type MutableRefObject } from 'react' -import type { PluginHostListEntry, PluginHostLogLine } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' +import type { PluginHostLogLine } from '../../../../../preload/api/plugin-host-api' import { useTranslate } from '@/i18n/use-translate' export type PluginLogsState = { diff --git a/src/renderer/src/components/plugins/state/use-plugin-marketplace-lifecycle.ts b/src/renderer/src/components/plugins/state/use-plugin-marketplace-lifecycle.ts index 93f5d3c9ba4..b2ec116b68e 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-marketplace-lifecycle.ts +++ b/src/renderer/src/components/plugins/state/use-plugin-marketplace-lifecycle.ts @@ -1,5 +1,5 @@ import { useState, type Dispatch, type MutableRefObject, type SetStateAction } from 'react' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { useTranslate } from '@/i18n/use-translate' import { pluginNeedsReview } from '../presentation/plugin-status-presentation' diff --git a/src/renderer/src/components/plugins/state/use-plugin-page-model.ts b/src/renderer/src/components/plugins/state/use-plugin-page-model.ts index 561819da1aa..d16846e4ce4 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-page-model.ts +++ b/src/renderer/src/components/plugins/state/use-plugin-page-model.ts @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import type { PluginMarketplaceHostInstallPreview, PluginMarketplaceHostListing -} from '../../../../../preload/api-types' +} from '../../../../../preload/api/plugin-host-api' import { pluginExecutionProfile } from '../../../../../shared/plugins/contributions/plugin-execution-profile' import { PLUGIN_CONTRIBUTION_SUMMARY_LIMIT } from '../../../../../shared/plugins/contributions/plugin-contribution-summary' import { diff --git a/src/renderer/src/components/plugins/state/use-plugin-page-navigation.test.tsx b/src/renderer/src/components/plugins/state/use-plugin-page-navigation.test.tsx deleted file mode 100644 index 76329f55c0c..00000000000 --- a/src/renderer/src/components/plugins/state/use-plugin-page-navigation.test.tsx +++ /dev/null @@ -1,146 +0,0 @@ -// @vitest-environment happy-dom - -import { act, useEffect } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, describe, expect, it } from 'vitest' -import type { PluginHostListEntry } from '../../../../../preload/api-types' -import { usePluginReviewQueue } from './use-plugin-review-queue' -import { usePluginPageNavigation, type PluginPageNavigation } from './use-plugin-page-navigation' - -let root: Root | null = null -let nav: PluginPageNavigation | null = null -let reviews: ReturnType | null = null - -function pending(pluginKey: string): PluginHostListEntry { - return { - pluginKey, - consentFingerprint: null, - name: pluginKey, - version: '1.0.0', - publisher: 'acme', - status: 'pending', - needsReconsent: true, - isDev: false, - official: false, - bundled: false, - capabilities: [], - panels: [], - commands: [], - hasWorker: false, - restarts: 0 - } -} - -function Harness(): React.JSX.Element { - const queue = usePluginReviewQueue() - reviews = queue - nav = usePluginPageNavigation(queue.navigate) - const open = nav.open - useEffect(() => { - if (queue.reviewKey) { - open(queue.reviewKey, { replace: queue.replacePage }) - } - }, [queue.reviewKey, queue.replacePage, open]) - return ( - <> - -
- -
- - -
- -
- - ) -} - -async function mountOpen(): Promise { - const container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) - await act(async () => root?.render()) - await act(async () => nav?.open('acme.notes')) - return container -} - -function pressBack(target: Element | null): void { - target?.dispatchEvent( - new KeyboardEvent('keydown', { key: 'ArrowLeft', altKey: true, bubbles: true }) - ) -} - -afterEach(() => { - act(() => root?.unmount()) - root = null - document.body.innerHTML = '' -}) - -describe('usePluginPageNavigation', () => { - it('returns through detail pages with Back and Alt+Left, then focuses the original list entry', async () => { - const container = await mountOpen() - await act(async () => nav?.open('acme.icons')) - await act(async () => nav?.open('acme.third')) - await act(async () => container.querySelector('[data-back]')?.click()) - expect(nav?.openKey).toBe('acme.icons') - await act(async () => pressBack(container.querySelector('[data-plain]'))) - expect(nav?.openKey).toBe('acme.notes') - await act(async () => pressBack(container.querySelector('[data-plain]'))) - expect(nav?.openKey).toBeNull() - expect(document.activeElement).toBe(container.querySelector('[data-plugin-row-name]')) - }) - - it('pauses A when visiting B, keeps B decisions outside the queue, and resumes A before the next review', async () => { - await mountOpen() - await act(async () => reviews?.enqueue(['acme.notes', 'acme.third'])) - await act(async () => - reviews?.advance('acme.icons', [pending('acme.notes'), pending('acme.third')]) - ) - expect(reviews?.reviewKey).toBe('acme.notes') - await act(async () => nav?.open('acme.icons')) - await act(async () => - reviews?.advance('acme.icons', [pending('acme.notes'), pending('acme.third')]) - ) - expect(nav?.openKey).toBe('acme.icons') - expect(reviews?.reviewKey).toBeNull() - - await act(async () => nav?.back()) - expect(nav?.openKey).toBe('acme.notes') - expect(reviews?.reviewKey).toBe('acme.notes') - await act(async () => reviews?.advance('acme.notes', [pending('acme.third')])) - expect(nav?.openKey).toBe('acme.third') - await act(async () => nav?.back()) - expect(nav?.openKey).toBeNull() - }) - - it.each(['button', 'keyboard'])( - 'pauses review on Back via %s, reopens the same plugin and accepts later update-all reviews', - async (method) => { - const container = await mountOpen() - await act(async () => reviews?.show('acme.notes')) - const back = (): void => { - if (method === 'button') { - container.querySelector('[data-back]')?.click() - } else { - pressBack(container.querySelector('[data-plain]')) - } - } - await act(async () => back()) - expect(nav?.openKey).toBeNull() - expect(reviews?.reviewKey).toBeNull() - await act(async () => reviews?.show('acme.notes')) - expect(nav?.openKey).toBe('acme.notes') - await act(async () => reviews?.advance('acme.notes', [])) - await act(async () => back()) - await act(async () => reviews?.enqueue(['acme.tasks', 'acme.third'])) - expect(nav?.openKey).toBe('acme.tasks') - await act(async () => reviews?.advance('acme.tasks', [pending('acme.third')])) - expect(nav?.openKey).toBe('acme.third') - await act(async () => back()) - expect(nav?.openKey).toBeNull() - } - ) -}) diff --git a/src/renderer/src/components/plugins/state/use-plugin-review-queue.test.tsx b/src/renderer/src/components/plugins/state/use-plugin-review-queue.test.tsx index 364be859c3c..3f876597a16 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-review-queue.test.tsx +++ b/src/renderer/src/components/plugins/state/use-plugin-review-queue.test.tsx @@ -3,7 +3,7 @@ import { act } from 'react' import { createRoot, type Root } from 'react-dom/client' import { afterEach, describe, expect, it } from 'vitest' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { usePluginReviewQueue } from './use-plugin-review-queue' let root: Root | null = null diff --git a/src/renderer/src/components/plugins/state/use-plugin-review-queue.ts b/src/renderer/src/components/plugins/state/use-plugin-review-queue.ts index c72cbc1f2ab..28a19408402 100644 --- a/src/renderer/src/components/plugins/state/use-plugin-review-queue.ts +++ b/src/renderer/src/components/plugins/state/use-plugin-review-queue.ts @@ -1,5 +1,5 @@ import { useCallback, useRef, useState } from 'react' -import type { PluginHostListEntry } from '../../../../../preload/api-types' +import type { PluginHostListEntry } from '../../../../../shared/plugins/plugin-list-entry' import { pluginNeedsReview } from '../presentation/plugin-status-presentation' /** diff --git a/src/renderer/src/components/shared/MacFolderAccessFixDialog.tsx b/src/renderer/src/components/shared/MacFolderAccessFixDialog.tsx index be7b6ec50bb..02a53a509f1 100644 --- a/src/renderer/src/components/shared/MacFolderAccessFixDialog.tsx +++ b/src/renderer/src/components/shared/MacFolderAccessFixDialog.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useState } from 'react' import { CircleCheck, CircleDashed, LoaderCircle } from 'lucide-react' -import type { PtyManagementFolderAccessMismatch } from '../../../../preload/api-types' +import type { PtyManagementFolderAccessMismatch } from '../../../../preload/api/pty-management-api' import { isMacTccFolderClass } from '../../../../shared/daemon-adoption-telemetry' import { useMountedRef } from '@/hooks/useMountedRef' import { translate } from '@/i18n/i18n' diff --git a/src/renderer/src/components/shared/mac-folder-access-folder-name.ts b/src/renderer/src/components/shared/mac-folder-access-folder-name.ts index b2238c8db28..f469a0b1ce8 100644 --- a/src/renderer/src/components/shared/mac-folder-access-folder-name.ts +++ b/src/renderer/src/components/shared/mac-folder-access-folder-name.ts @@ -1,10 +1,10 @@ -import type { PtyManagementDaemonCwdClass } from '../../../../preload/api-types' +import type { DaemonPtyCwdClass } from '../../../../shared/daemon-adoption-telemetry' import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' /** Use the same macOS folder phrase in toast and remedy so they read as one notice. */ export function macFolderAccessFolderName( - cwdClass: PtyManagementDaemonCwdClass, + cwdClass: DaemonPtyCwdClass, t: Translate = translate ): string { switch (cwdClass) { diff --git a/src/renderer/src/components/sidebar/SeveredTerminalDaemonBanner.tsx b/src/renderer/src/components/sidebar/SeveredTerminalDaemonBanner.tsx index 530ee21e6a4..f0eaf47dcea 100644 --- a/src/renderer/src/components/sidebar/SeveredTerminalDaemonBanner.tsx +++ b/src/renderer/src/components/sidebar/SeveredTerminalDaemonBanner.tsx @@ -15,7 +15,7 @@ import { useTranslate, type Translate } from '@/i18n/use-translate' import type { PtyManagementSeveredDaemon, PtyManagementSeveredDaemonRestartResult -} from '../../../../preload/api-types' +} from '../../../../preload/api/pty-management-api' const POLL_INTERVAL_MS = 60_000 const DISMISSED_KEY = 'orca.severedTerminalDaemonBanner.dismissed' diff --git a/src/renderer/src/components/task-source-provider-availability.ts b/src/renderer/src/components/task-source-provider-availability.ts index 8efa20117d3..296dd5d1e57 100644 --- a/src/renderer/src/components/task-source-provider-availability.ts +++ b/src/renderer/src/components/task-source-provider-availability.ts @@ -1,6 +1,6 @@ import { parseExecutionHostId } from '../../../shared/execution-host' import type { TaskProvider } from '../../../shared/work-items/task-providers' -import type { PreflightStatus } from '../../../preload/api-types' +import type { PreflightStatus } from '../../../preload/api/preflight-api' import type { TaskSourceContext } from '../../../shared/work-items/task-source-context' import type { TaskSourceHostAvailability } from './task-source-context-summary' diff --git a/src/renderer/src/hooks/ipc-events/linear-linked-issue-refresh.test.ts b/src/renderer/src/hooks/ipc-events/linear-linked-issue-refresh.test.ts index d62f5bc6a47..e52609d01e7 100644 --- a/src/renderer/src/hooks/ipc-events/linear-linked-issue-refresh.test.ts +++ b/src/renderer/src/hooks/ipc-events/linear-linked-issue-refresh.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, expect, it, vi } from 'vitest' import { useAppStore } from '@/store' import { registerRuntimeClientIpcBridge } from './runtime-client-ipc-bridge' -import { subscribeRuntimeEnvironmentFromPreload } from '../../../../preload/runtime-environment-subscriptions' +import { subscribeRuntimeEnvironmentFromPreload } from '../../../../preload/runtime/runtime-environment-subscriptions' import { linearApi } from '../../../../preload/api/linear-bridge' import { createCompatibleRuntimeStatusResponse } from '@/runtime/rpc/testing/runtime-compatibility-test-fixture' import { runtimeCompatibilityChecks } from '@/runtime/rpc/compatibility/runtime-compatibility-cache' @@ -265,62 +265,6 @@ function harness(rows: Worktree[]) { } type AppStateSettings = NonNullable['settings']> -it.each([100, 1000])( - 'coalesces two actual same-source badge keys across %i remote notices without multiplying legacy refreshes', - async (population) => { - const a = row('same', source('runtime:host-a')) - const a2 = row('second', source('runtime:host-a'), 'w1') - const local = row('same', source('local')) - const wrongAccount = row('wrong-account', { - ...source('runtime:host-a'), - providerIdentity: { provider: 'linear', workspaceId: 'w2' } - }) - const h = harness([a, a2, local, wrongAccount]) - const keys = [await h.hydrate(a), await h.hydrate(a2)] - const localKey = await h.hydrate(local) - const wrongKey = await h.hydrate(wrongAccount) - h.request.mockClear() - h.responses.length = 0 - h.start() - for (let i = 0; i < population; i += 1) { - h.emit('same', true) - h.emit('second', true) - h.emit('wrong-account', true) - } - await settle() - const badgeCalls = () => h.request.mock.calls.filter((call) => call[1] === 'eng-1') - const legacyCalls = () => h.request.mock.calls.filter((call) => call[1] === 'ENG-1') - expect(badgeCalls()).toEqual([ - ['host-a', 'eng-1', 'all'], - ['host-a', 'eng-1', 'w1'] - ]) - expect(legacyCalls()).toHaveLength(population === 100 ? 300 : 3000) - expect(legacyCalls().every((call) => call[0] === 'host-b')).toBe(true) - h.responses - .filter((call) => call.id === 'eng-1') - .forEach((call) => call.response.resolve(issue('eng-1'))) - await settle() - expect(badgeCalls()).toHaveLength(4) - h.responses.forEach((call) => - call.response.resolve({ - ...issue(call.id), - workspaceId: 'w1', - state: { name: 'Done', type: 'completed', color: '#888888' } - }) - ) - await settle() - expect( - keys.map((key) => useAppStore.getState().linearIssueCache[key].data?.state.name) - ).toEqual(['Done', 'Done']) - expect(useAppStore.getState().linearIssueCache[localKey].data?.state.name).toBe('Todo') - expect(useAppStore.getState().linearIssueCache[wrongKey].data?.state.name).toBe('Todo') - expect(badgeCalls()).toHaveLength(4) - expect(legacyCalls()).toHaveLength(population === 100 ? 300 : 3000) - h.stop() - h.finishSetup() - } -) - it('tears down pending/trailing local and remote demand, rejects late setup and avoids late publication', async () => { const workspace = row('wt', source('local')) const h = harness([workspace]) diff --git a/src/renderer/src/hooks/ipc-events/runtime-client-ipc-bridge-ownership.test.ts b/src/renderer/src/hooks/ipc-events/runtime-client-ipc-bridge-ownership.test.ts index 3e28b442a3a..ac6047bce37 100644 --- a/src/renderer/src/hooks/ipc-events/runtime-client-ipc-bridge-ownership.test.ts +++ b/src/renderer/src/hooks/ipc-events/runtime-client-ipc-bridge-ownership.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, expect, it, vi } from 'vitest' import { useAppStore } from '@/store' -import { subscribeRuntimeEnvironmentFromPreload } from '../../../../preload/runtime-environment-subscriptions' +import { subscribeRuntimeEnvironmentFromPreload } from '../../../../preload/runtime/runtime-environment-subscriptions' import { tagRuntimeSubscriptionReplayResponse } from '../../../../shared/runtime-subscription-replay' import { createCompatibleRuntimeStatusResponse } from '@/runtime/rpc/testing/runtime-compatibility-test-fixture' import { registerRuntimeClientIpcBridge } from './runtime-client-ipc-bridge' @@ -153,28 +153,6 @@ function createHarness() { } } -it('does no Linear read dispatch or cache publication after cleanup while setup is pending', async () => { - const h = createHarness() - let publications = 0 - const stopCounting = useAppStore.subscribe((state, previous) => { - if (state.linearIssueCache !== previous.linearIssueCache) { - publications += 1 - } - }) - try { - h.start()() - for (let index = 0; index < 100; index += 1) { - h.emit(0) - } - expect(h.getIssue).not.toHaveBeenCalled() - expect(publications).toBe(0) - } finally { - stopCounting() - await h.finish() - } - expect(h.ipc.removeListener).toHaveBeenCalledOnce() -}) - it('does not navigate for an unaddressed activation from an older remote host', async () => { const h = createHarness() try { @@ -252,38 +230,6 @@ it('does not mistake a local repo with the same id for the publishing remote rep } }) -it.each(['cleanup', 're-pair'] as const)( - 'cancels an accepted activation after %s during repository discovery', - async (change) => { - const h = createHarness() - const repos = Promise.withResolvers() - h.fetchRuntimeEnvironmentRepos.mockImplementation(() => repos.promise) - try { - const stop = h.start() - h.emit(0, false, { - type: 'activateWorktree', - repoId: 'remote-repo', - worktreeId: 'remote-wt', - navigation: 'clients' - }) - expect(h.fetchRuntimeEnvironmentRepos).toHaveBeenCalledWith('host-a') - if (change === 'cleanup') { - stop() - } else { - replaceRuntimeEnvironmentRevisions([{ id: 'host-a', createdAt: 1, pairingRevision: 2 }]) - } - repos.resolve([]) - for (let index = 0; index < 10; index += 1) { - await Promise.resolve() - } - expect(h.activateNotifiedWorktree).not.toHaveBeenCalled() - } finally { - repos.resolve([]) - await h.finish() - } - } -) - it('accepts a fresh bridge early frame while rejecting the previous bridge frame', async () => { const h = createHarness() try { diff --git a/src/renderer/src/hooks/useMacTccAttributionSeveredNotice.ts b/src/renderer/src/hooks/useMacTccAttributionSeveredNotice.ts index bb783027e16..e0060ab9654 100644 --- a/src/renderer/src/hooks/useMacTccAttributionSeveredNotice.ts +++ b/src/renderer/src/hooks/useMacTccAttributionSeveredNotice.ts @@ -4,7 +4,7 @@ import { toast } from 'sonner' import type { PtyManagementFolderAccessMismatch, PtyManagementMacTccAttributionHealth -} from '../../../preload/api-types' +} from '../../../preload/api/pty-management-api' import { isPluginUiLanguage } from '../../../shared/ui-language' import { useAppStore } from '@/store/index' import { diff --git a/src/renderer/src/store/slices/usage/usage-backend-port.test.ts b/src/renderer/src/store/slices/usage/usage-backend-port.test.ts index 437872eac3d..29a37373c3f 100644 --- a/src/renderer/src/store/slices/usage/usage-backend-port.test.ts +++ b/src/renderer/src/store/slices/usage/usage-backend-port.test.ts @@ -68,7 +68,7 @@ afterEach(() => { it('publishes port loss before rejection and preserves fresh work through duplicate loss', async () => { const { backendUsageTransport: transport } = - await import('../../../../../preload/backend-port-client') + await import('../../../../../preload/usage/backend-port-client') const events: string[] = [] let fresh: Promise | undefined detach.push( @@ -108,7 +108,7 @@ it('publishes port loss before rejection and preserves fresh work through duplic it('keeps a replacement handshake when the old handshake rejects after loss', async () => { const { backendUsageTransport: transport } = - await import('../../../../../preload/backend-port-client') + await import('../../../../../preload/usage/backend-port-client') let fresh: Promise | undefined detach.push( transport.onConnectionChanged((connected, detail) => { @@ -132,7 +132,7 @@ it('keeps a replacement handshake when the old handshake rejects after loss', as it('initializes terminal presentation without RPCs and preserves a fresh handshake through terminal escalation', async () => { const { backendUsageTransport: transport } = - await import('../../../../../preload/backend-port-client') + await import('../../../../../preload/usage/backend-port-client') emit(BACKEND_CONNECTION_CHANGED, { connected: false }) const fresh = transport.invoke('snapshot') emit(BACKEND_CONNECTION_CHANGED, { connected: false, unavailable: true }) @@ -149,7 +149,7 @@ it('initializes terminal presentation without RPCs and preserves a fresh handsha it('promotes pending recovery once and ignores its stale delivery and rejection', async () => { const { backendUsageTransport: transport } = - await import('../../../../../preload/backend-port-client') + await import('../../../../../preload/usage/backend-port-client') const old = Promise.withResolvers() electron.invoke.mockImplementationOnce(() => old.promise) const recovery = transport.invoke('snapshot', undefined, 'recovery') @@ -176,7 +176,7 @@ it('promotes pending recovery once and ignores its stale delivery and rejection' it('cancels pending recovery on loss without rearming, and admits fresh post-loss demand', async () => { const { backendUsageTransport: transport } = - await import('../../../../../preload/backend-port-client') + await import('../../../../../preload/usage/backend-port-client') const old = Promise.withResolvers() electron.invoke.mockImplementationOnce(() => old.promise) const recovery = transport.invoke('snapshot', undefined, 'recovery') diff --git a/src/renderer/src/web/preload-api/web-host-capability-api.ts b/src/renderer/src/web/preload-api/web-host-capability-api.ts index 19376cdd63e..86887d493de 100644 --- a/src/renderer/src/web/preload-api/web-host-capability-api.ts +++ b/src/renderer/src/web/preload-api/web-host-capability-api.ts @@ -1,8 +1,5 @@ -import type { - PreflightStatus, - PreloadApi, - RefreshAgentsResult -} from '../../../../preload/api-types' +import type { PreflightStatus, RefreshAgentsResult } from '../../../../preload/api/preflight-api' +import type { PreloadApi } from '../../../../preload/api-types' import type { ComputerUsePermissionSetupResult, ComputerUsePermissionStatusResult diff --git a/src/renderer/src/web/preload-api/web-native-chat-api.ts b/src/renderer/src/web/preload-api/web-native-chat-api.ts index 6130fb181e4..a81cb128e88 100644 --- a/src/renderer/src/web/preload-api/web-native-chat-api.ts +++ b/src/renderer/src/web/preload-api/web-native-chat-api.ts @@ -1,4 +1,7 @@ -import type { NativeChatApi, NativeChatAppendedMessages } from '../../../../preload/api-types' +import type { + NativeChatApi, + NativeChatAppendedMessages +} from '../../../../preload/api/native-chat-api' import { buildNativeChatUnsubscribe } from '../../../../shared/native-chat/native-chat-stream-unsubscribe' import { parseRuntimeNativeChatReadSessionResult, diff --git a/src/renderer/src/web/web-runtime-crew-follow.test.tsx b/src/renderer/src/web/web-runtime-crew-follow.test.tsx deleted file mode 100644 index 66eb161c09d..00000000000 --- a/src/renderer/src/web/web-runtime-crew-follow.test.tsx +++ /dev/null @@ -1,185 +0,0 @@ -// @vitest-environment happy-dom -import { act, cleanup, render } from '@testing-library/react' -import { afterEach, beforeEach, expect, it, vi } from 'vitest' - -import { - type RuntimeEnvironmentStatus, - runtimeEnvironmentStatusFromSnapshot -} from '../../../shared/remote-runtime/host-status/runtime-host-status' -import type { StoredWebRuntimeEnvironment } from './web-runtime-environment' -import type * as RuntimeSession from './preload-api/web-runtime-session' -import type { PreloadApi } from '../../../preload/api-types' -import { replaceRuntimeEnvironmentRevisions } from '../runtime/rpc/environment-revisions/runtime-environment-revision' -import { useOrchestrationCrew } from '../components/orchestration-crew/use-orchestration-crew' -import { - type CrewSocket, - flushCrewWork, - installCrewSocketGlobals, - latestCrewSocket, - publicKeyB64, - verifyCrewPrimary -} from './web-runtime-crew.test-fixture' - -const store = vi.hoisted(() => ({ - state: { runtimeStatusByEnvironmentId: new Map() }, - listeners: new Set<() => void>() -})) -vi.mock('@/store', () => ({ - useAppStore: { - getState: () => store.state, - subscribe: (wake: () => void) => { - store.listeners.add(wake) - return () => store.listeners.delete(wake) - } - } -})) -const environment: StoredWebRuntimeEnvironment = { - id: 'host', - name: 'Host', - createdAt: 1, - updatedAt: 1, - pairingRevision: 1, - runtimeId: 'rt', - lastUsedAt: null, - preferredEndpointId: 'endpoint', - endpoints: [ - { - id: 'endpoint', - kind: 'websocket', - label: 'WebSocket', - endpoint: 'ws://127.0.0.1:6768', - deviceToken: 'token', - publicKeyB64 - } - ] -} -const target = { kind: 'environment', environmentId: 'host' } as const -let session: typeof RuntimeSession -let api: PreloadApi['runtimeEnvironments'] -let disposeStatus: () => void -const domWindow = window -function Pane({ party = 'term_one' }: { party?: string }) { - const crew = useOrchestrationCrew(target, party) - return ( - - {crew.status === 'ok' ? crew.sections.managing.length : 'unknown'} - - ) -} -function requestId(socket: CrewSocket): string { - const request = socket.read(2) - if ( - !request || - typeof request !== 'object' || - !('id' in request) || - typeof request.id !== 'string' || - !('method' in request) || - request.method !== 'orchestration.crewSubscribe' - ) { - throw new Error('Expected actual Crew subscribe request') - } - return request.id -} -async function ready(socket = latestCrewSocket()): Promise { - socket.open() - socket.ready() - await flushCrewWork() - socket.authenticated() - await flushCrewWork() - return requestId(socket) -} -function frame(socket: CrewSocket, id: string, result: unknown, runtimeId = 'rt'): void { - socket.frame({ id, ok: true, result, _meta: { runtimeId } }) -} -async function complete(socket: CrewSocket, id: string, runtimeId = 'rt'): Promise { - frame(socket, id, { type: 'begin', status: 'ok' }, runtimeId) - frame( - socket, - id, - { - type: 'rows', - section: 'managing', - rows: [ - { - partyKey: 'worker', - role: 'worker', - label: 'Worker', - agentKind: 'codex', - status: 'working' - } - ] - }, - runtimeId - ) - frame(socket, id, { type: 'complete' }, runtimeId) - await flushCrewWork() -} -beforeEach(async () => { - vi.useFakeTimers() - vi.spyOn(Math, 'random').mockReturnValue(0) - installCrewSocketGlobals() - vi.stubGlobal('window', domWindow) - localStorage.clear() - localStorage.setItem('orca.web.runtimeEnvironment.v1', JSON.stringify(environment)) - session = await import('./preload-api/web-runtime-session') - session.manuallyDisconnectedEnvironmentIds.clear() - session.webRuntimeState.activeEnvironment = environment - api = (await import('./preload-api/web-runtime-environments-api')).createRuntimeEnvironmentsApi() - Object.assign(window, { api: { runtimeEnvironments: api } }) - store.state = { runtimeStatusByEnvironmentId: new Map() } - disposeStatus = session.subscribeWebRuntimeStatus((snapshot) => { - store.state = { - runtimeStatusByEnvironmentId: new Map(store.state.runtimeStatusByEnvironmentId).set( - 'host', - runtimeEnvironmentStatusFromSnapshot(snapshot) - ) - } - store.listeners.forEach((wake) => wake()) - }) - replaceRuntimeEnvironmentRevisions([{ id: 'host', createdAt: 1 }]) - await verifyCrewPrimary(session.getClientForEnvironment(environment)) -}) -afterEach(() => { - cleanup() - - disposeStatus() - session.closeActiveRuntimeClients() - vi.restoreAllMocks() - vi.useRealTimers() - vi.unstubAllGlobals() -}) - -it('retires the old source and follows the real verified runtime publication after its writer completes', async () => { - const ordinary = vi.spyOn(api, 'subscribe') - const retry = vi.spyOn(api, 'subscribeCrewAttempt') - const view = render() - const first = latestCrewSocket() - let oldId = '' - await act(async () => { - oldId = await ready(first) - await complete(first, oldId) - const owner = session.webRuntimeState.activeClient!.statusOwner! - owner.acceptVerified({ - id: 'status', - ok: true, - result: { ...owner.read().status!, runtimeId: 'next' }, - _meta: { runtimeId: 'next' } - }) - await complete(first, oldId) - }) - expect(view.getByTestId('term_one').textContent).toBe('unknown') - expect(retry).not.toHaveBeenCalled() - await act(async () => { - await vi.advanceTimersByTimeAsync(250) - }) - expect(retry).toHaveBeenCalledOnce() - expect(retry.mock.calls[0][0].expectedEnvironmentRuntimeId).toBe('next') - const current = latestCrewSocket() - await act(async () => { - const id = await ready(current) - await complete(current, id, 'next') - }) - expect(view.getByTestId('term_one').textContent).toBe('1') - expect(ordinary).toHaveBeenCalledOnce() - view.unmount() -}) diff --git a/src/renderer/src/web/web-runtime-crew.test-fixture.ts b/src/renderer/src/web/web-runtime-crew.test-fixture.ts deleted file mode 100644 index 5bdb32fcd58..00000000000 --- a/src/renderer/src/web/web-runtime-crew.test-fixture.ts +++ /dev/null @@ -1,137 +0,0 @@ -import type { WebRuntimeClient } from './web-runtime-client' -import { REMOTE_RUNTIME_SHARED_CONTROL_CAPABILITY } from '../../../shared/protocol-version' -import { vi } from 'vitest' -import { NODE_PAIRED_E2EE_PRIMITIVES as crypto } from '../../../shared/remote-runtime/e2ee/node/paired-e2ee-node-primitives' - -const host = crypto.generateKeyPair() -export const publicKeyB64 = crypto.publicKeyToBase64(host.publicKey) - -export class CrewSocket { - static readonly CONNECTING = 0 - static readonly OPEN = 1 - static sockets: CrewSocket[] = [] - readyState = CrewSocket.CONNECTING - binaryType = 'arraybuffer' - onopen: (() => void) | null = null - onclose: (() => void) | null = null - onerror: (() => void) | null = null - onmessage: ((event: { data: unknown }) => void) | null = null - close = vi.fn(() => { - this.readyState = 3 - }) - send = vi.fn((_data: unknown) => {}) - private key: Uint8Array | undefined - - constructor() { - CrewSocket.sockets.push(this) - } - - open(): void { - this.readyState = CrewSocket.OPEN - this.onopen?.() - } - - ready(): void { - const hello: unknown = JSON.parse(String(this.send.mock.calls[0]?.[0])) - if ( - !hello || - typeof hello !== 'object' || - !('publicKeyB64' in hello) || - typeof hello.publicKeyB64 !== 'string' - ) { - throw new Error('Invalid legacy hello') - } - this.key = crypto.deriveSharedKey( - host.secretKey, - crypto.publicKeyFromBase64(hello.publicKeyB64) - ) - this.onmessage?.({ data: JSON.stringify({ type: 'e2ee_ready' }) }) - } - - frame(value: unknown): void { - if (!this.key) { - throw new Error('No fixture session') - } - this.onmessage?.({ data: crypto.sealLegacyText(JSON.stringify(value), this.key) }) - } - - authenticated(): void { - this.frame({ type: 'e2ee_authenticated' }) - } - - read(index: number): unknown { - const frame = String(this.send.mock.calls[index]?.[0]) - const plaintext = this.key ? crypto.openLegacyText(frame, this.key) : null - if (!plaintext) { - throw new Error('Invalid fixture frame') - } - return JSON.parse(plaintext) - } -} - -export function installCrewSocketGlobals(): void { - CrewSocket.sockets = [] - vi.stubGlobal('WebSocket', CrewSocket) - vi.stubGlobal('window', { - setTimeout, - clearTimeout, - setInterval, - clearInterval, - atob: (value: string) => Buffer.from(value, 'base64').toString('binary'), - btoa: (value: string) => Buffer.from(value, 'binary').toString('base64') - }) -} - -export async function flushCrewWork(): Promise { - for (let index = 0; index < 20; index++) { - await Promise.resolve() - } -} - -export function latestCrewSocket(): CrewSocket { - const socket = CrewSocket.sockets.at(-1) - if (!socket) { - throw new Error('No fixture socket') - } - return socket -} - -/** Complete the existing primary's real status request over the controlled encrypted socket. */ -export async function verifyCrewPrimary(client: WebRuntimeClient): Promise { - const socket = latestCrewSocket() - socket.open() - socket.ready() - await flushCrewWork() - socket.authenticated() - await flushCrewWork() - const request = socket.read(2) - if ( - !request || - typeof request !== 'object' || - !('id' in request) || - typeof request.id !== 'string' || - !('method' in request) || - request.method !== 'status.get' - ) { - throw new Error('Expected actual primary status request') - } - socket.frame({ - id: request.id, - ok: true, - result: { - runtimeId: 'rt', - rendererGraphEpoch: 1, - graphStatus: 'ready', - authoritativeWindowId: null, - liveTabCount: 0, - liveLeafCount: 0, - capabilities: [REMOTE_RUNTIME_SHARED_CONTROL_CAPABILITY] - }, - _meta: { runtimeId: 'rt' } - }) - await flushCrewWork() - if (client.statusOwner?.read().verification !== 'verified') { - throw new Error('Primary did not verify') - } - return socket -} From ee8ad028604e6e3f9763278bb86e48365e20952e Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 02:20:31 -0800 Subject: [PATCH 067/178] test: remove hook and generation replays and cache assertions (#41951) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 4 +- .../scripts/ci/sharding/ci-shard-timings.json | 4 - .../branch-change-list-cache-real-git.test.ts | 4 - .../status-branch-compare-real-ref.test.ts | 34 +---- .../antigravity-generation-stream.test.ts | 136 ------------------ ...message-text-generation-regression.test.ts | 47 ------ .../hook-archive-timeout-observation.test.ts | 33 ----- .../hook-termination-real-process.test.ts | 8 -- .../hooks-archive-exit-observation.test.ts | 25 ---- .../hooks-orca-yaml-unc-drive.test.ts | 44 ------ src/main/workspace-hooks/hooks-runner.test.ts | 53 ------- 11 files changed, 2 insertions(+), 390 deletions(-) delete mode 100644 src/main/text-generation/failures/antigravity-generation-stream.test.ts delete mode 100644 src/main/workspace-hooks/hook-archive-timeout-observation.test.ts delete mode 100644 src/main/workspace-hooks/hooks-orca-yaml-unc-drive.test.ts delete mode 100644 src/main/workspace-hooks/hooks-runner.test.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index 7ccccd20106..9bbf1ed4d8d 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -20,7 +20,6 @@ 5 test-no-in-test-import-actual:src/main/ssh/config/ssh-config-loader-regression.test.ts 2 test-no-in-test-import-actual:src/main/ssh/connection/ssh-connection-auth-fallback.test.ts 1 test-no-in-test-import-actual:src/main/window/file-drop/dropped-path-preparation.test.ts -2 test-no-in-test-import-actual:src/main/workspace-hooks/hooks-runner.test.ts 1 test-no-in-test-import-actual:src/main/workspaces/removal/local-worktree-removal-recovery.test.ts 1 test-no-in-test-import-actual:src/renderer/src/components/terminal-pane/pty-connection/notifications/pty-connection-hook-idle-arbitration.test.ts 3 test-no-in-test-import-actual:src/renderer/src/components/terminal-pane/snapshots/remote-snapshot-alt-screen-host-chain.test.ts @@ -624,8 +623,7 @@ 1 test-timeout-needs-reason:src/main/windows/windows-msys-job.win32.test.ts 2 test-timeout-needs-reason:src/main/windows/windows-pty-job.win32.test.ts 5 test-timeout-needs-reason:src/main/worker-thread-child-process/worker-thread-child-process.test.ts -1 test-timeout-needs-reason:src/main/workspace-hooks/hook-archive-timeout-observation.test.ts -2 test-timeout-needs-reason:src/main/workspace-hooks/hook-termination-real-process.test.ts +1 test-timeout-needs-reason:src/main/workspace-hooks/hook-termination-real-process.test.ts 2 test-timeout-needs-reason:src/main/workspaces/removal/local-worktree-filesystem-wsl-banner.wsl.test.ts 4 test-timeout-needs-reason:src/main/wsl/execution/wsl-runner.wsl.test.ts 4 test-timeout-needs-reason:src/packages/process-host/src/run-process.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index b19b9bd1a84..9aa6ee2513c 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -1891,11 +1891,8 @@ "src/main/hermes/hermes-config-source-edits.test.ts": 280, "src/main/hermes/hermes-windows-home.test.ts": 121, "src/main/hermes/hook-service.test.ts": 589, - "src/main/workspace-hooks/hook-archive-timeout-observation.test.ts": 1103, "src/main/workspace-hooks/hook-termination-real-process.test.ts": 3284, "src/main/workspace-hooks/hooks-archive-exit-observation.test.ts": 339, - "src/main/workspace-hooks/hooks-orca-yaml-unc-drive.test.ts": 402, - "src/main/workspace-hooks/hooks-runner.test.ts": 314, "src/main/filesystem/host-checkout-removal.test.ts": 52, "src/main/filesystem/host-tree-removal-asar.electron.test.ts": 308, "src/main/host/deferred-secret-protection-report.test.ts": 81, @@ -3886,7 +3883,6 @@ "src/main/pty/history/terminal-history-gc.test.ts": 824, "src/main/pty/history/terminal-history-tombstone-retry.test.ts": 715, "src/main/text-generation/failures/agent-failure-output.test.ts": 49, - "src/main/text-generation/failures/antigravity-generation-stream.test.ts": 479, "src/main/text-generation/execution/command-environment-process.test.ts": 1044, "src/main/text-generation/execution/commit-message-agent-environment.test.ts": 95, "src/main/text-generation/requests/commit-message-text-generation-branch-name.test.ts": 496, diff --git a/src/main/git/source-control/comparisons/branch-change-list-cache-real-git.test.ts b/src/main/git/source-control/comparisons/branch-change-list-cache-real-git.test.ts index 06a8bdda317..29c40d1ce5f 100644 --- a/src/main/git/source-control/comparisons/branch-change-list-cache-real-git.test.ts +++ b/src/main/git/source-control/comparisons/branch-change-list-cache-real-git.test.ts @@ -4,7 +4,6 @@ import { tmpdir } from 'node:os' import * as path from 'node:path' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { getBranchCompare } from './branch-compare' -import { settledBranchChangeListCache } from './branch-change-list-cache' // Old mtimes put every input past the racy-write margin, so only stamp contents decide reuse. const aged = new Date('2020-01-01T00:00:00Z') @@ -67,9 +66,6 @@ describe('branch compare changed-files reuse', () => { const counted = [{ path: 'docs/notes.txt', status: 'modified', added: 1, removed: 0 }] const binary = [{ path: 'docs/notes.txt', status: 'modified' }] expect((await getBranchCompare(repo, 'base')).entries).toEqual(counted) - const hitsBefore = settledBranchChangeListCache.stats().hits - expect((await getBranchCompare(repo, 'base')).entries).toEqual(counted) - expect(settledBranchChangeListCache.stats().hits).toBe(hitsBefore + 1) await writeFile(nestedAttributes, '*.txt -diff\n') await age(nestedAttributes) diff --git a/src/main/git/source-control/comparisons/status-branch-compare-real-ref.test.ts b/src/main/git/source-control/comparisons/status-branch-compare-real-ref.test.ts index a5f40187594..60351448a1b 100644 --- a/src/main/git/source-control/comparisons/status-branch-compare-real-ref.test.ts +++ b/src/main/git/source-control/comparisons/status-branch-compare-real-ref.test.ts @@ -1,5 +1,5 @@ import { execFileSync } from 'node:child_process' -import { mkdtemp, readFile, readdir, realpath, rm, utimes, writeFile } from 'node:fs/promises' +import { mkdtemp, readdir, realpath, rm, utimes, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import * as path from 'node:path' import { afterEach, describe, expect, it } from 'vitest' @@ -136,21 +136,6 @@ describe('getBranchCompare real refs', () => { expect(await commitsAhead(repo)).toBe(1) }) - it('recounts after an existing loose replace ref is edited in place', async () => { - const { repo, oids } = await createChainRepo() - git(repo, ['replace', '--graft', oids.tip, oids.a]) - const replaceRef = path.join(repo, '.git', 'refs', 'replace', oids.tip) - const tipOntoA = await readFile(replaceRef, 'utf8') - git(repo, ['replace', '--force', '--graft', oids.tip, oids.root]) - await ageReplaceRefs(repo) - expect(await commitsAhead(repo)).toBe(1) - - await writeFile(replaceRef, tipOntoA) - await utimes(path.dirname(replaceRef), aged, aged) - - expect(await commitsAhead(repo)).toBe(2) - }) - it('recounts after a replace ref is packed away from refs/replace', async () => { const { repo, oids } = await createChainRepo() expect(await commitsAhead(repo)).toBe(3) @@ -163,20 +148,6 @@ describe('getBranchCompare real refs', () => { expect(await commitsAhead(repo)).toBe(1) }) - it('keeps per-worktree core.useReplaceRefs answers apart', async () => { - const { repo, oids } = await createChainRepo() - git(repo, ['replace', '--graft', oids.tip, oids.root]) - const linked = path.join(path.dirname(repo), `${path.basename(repo)}-linked`) - tempRoots.push(linked) - git(repo, ['worktree', 'add', '-q', '--detach', linked, oids.tip]) - git(repo, ['config', 'extensions.worktreeConfig', 'true']) - git(linked, ['config', '--worktree', 'core.useReplaceRefs', 'false']) - await ageReplaceRefs(repo) - - expect(await commitsAhead(repo)).toBe(1) - expect(await commitsAhead(linked)).toBe(3) - }) - it('honors a custom GIT_REPLACE_REF_BASE namespace', async () => { const { repo, oids } = await createChainRepo() const previous = process.env.GIT_REPLACE_REF_BASE @@ -211,9 +182,6 @@ describe('getBranchCompare real refs', () => { execFileSync('git', ['clone', '-q', source, client]) git(client, ['fetch', source, 'refs/tags/annotated:refs/remotes/origin/tagbase']) - expect(git(client, ['branch', '-r', '--format=%(refname:short)']).split(/\r?\n/)).toContain( - 'origin/tagbase' - ) const rawOid = git(client, ['rev-parse', '--verify', 'refs/remotes/origin/tagbase']) const peeledOid = git(client, [ 'rev-parse', diff --git a/src/main/text-generation/failures/antigravity-generation-stream.test.ts b/src/main/text-generation/failures/antigravity-generation-stream.test.ts deleted file mode 100644 index 6c1f6ed1ea8..00000000000 --- a/src/main/text-generation/failures/antigravity-generation-stream.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { parseAntigravityGenerationOutput } from '../../../shared/antigravity-generation-stream' -import { - generateCommitMessageFromContext, - generatePullRequestFieldsFromContext -} from '../commit-message-text-generation' -import { generatePullRequestFields } from '../requests/source-control-text-generation-requests' -import { - createMockDiscoveryChild, - withPlatform -} from '../execution/commit-message-text-generation-test-harness' - -const resultEvent = (response: string, status = 'SUCCESS', error?: string): string => - JSON.stringify({ event: 'result', result: { status, response, error } }) -const context = { - branch: 'feature/large-diff', - base: 'main', - branchChangedByPreparation: false, - currentTitle: '', - currentBody: '', - currentDraft: false, - commitSummary: 'Add fixture', - changeSummary: 'A\tfixture.txt', - patch: `diff --git a/fixture.txt b/fixture.txt\n+${'x'.repeat(200_000)}` -} -const params = { agentId: 'antigravity', model: 'default' } as const -const fields = { base: 'main', title: 'Add fixture', body: 'Describe the fixture', draft: false } - -describe('Antigravity generation stream', () => { - it('generates pull request fields on Windows without putting a large patch on argv', async () => { - await withPlatform('win32', async () => { - const child = createMockDiscoveryChild() - const pending = generatePullRequestFields({ - context, - params, - target: { kind: 'local', cwd: 'C:\\folder-workspace' }, - spawnAgent: (input) => { - if (input.args.includes('--help')) { - const probe = createMockDiscoveryChild() - probe.stdin.end.mockImplementation(() => - queueMicrotask(() => { - probe.stdout.emit('data', Buffer.from(' --settings \n')) - probe.emit('close', 0) - }) - ) - return probe - } - expect(input.args.join(' ').length).toBeLessThan(30_000) - expect(input.args).toContain('--input-format=stream-json') - return child - } - }) - await vi.waitFor(() => expect(child.stdin.end).toHaveBeenCalled()) - const input = JSON.parse(child.stdin.end.mock.calls[0]?.[0] ?? '') - expect(input.message.content[0].text).toContain('(diff truncated,') - expect(input.message.content[0].text.length).toBeGreaterThan(200_000) - child.stdout.emit('data', Buffer.from(resultEvent(JSON.stringify(fields)))) - child.emit('close', 0) - await expect(pending).resolves.toMatchObject({ success: true, fields }) - }) - }) - - it('sends structured stdin and parses only the final answer across remote execution', async () => { - const result = await generatePullRequestFieldsFromContext(context, params, { - kind: 'remote', - cwd: '/folder-workspace', - missingBinaryLocation: 'remote PATH', - execute: async (plan) => { - expect(JSON.parse(plan.stdinPayload ?? '').event).toBe('user') - return { - stdout: [ - JSON.stringify({ event: 'init', init: { tools: ['read_file'] } }), - JSON.stringify({ event: 'step_update', step_update: { text_delta: 'Partial' } }), - resultEvent(JSON.stringify(fields)) - ].join('\r\n'), - stderr: '', - exitCode: 0, - timedOut: false - } - } - }) - expect(result).toMatchObject({ success: true, fields, agentLabel: 'Antigravity' }) - }) - - it('does not treat a failed stream turn as a successful commit message when exit code is zero', async () => { - const result = await generateCommitMessageFromContext( - { branch: 'main', stagedSummary: 'A\tfixture.txt', stagedPatch: '+fixture' }, - params, - { - kind: 'remote', - cwd: '/folder-workspace', - missingBinaryLocation: 'remote PATH', - execute: async () => ({ - stdout: resultEvent('Partial answer', 'ERROR', 'Model unavailable'), - stderr: '', - exitCode: 0, - timedOut: false - }) - } - ) - expect(result).toMatchObject({ success: false, error: 'Model unavailable' }) - }) - - it('preserves actionable stderr when a nonzero exit has a malformed result event', async () => { - const result = await generateCommitMessageFromContext( - { branch: 'main', stagedSummary: 'A\tfixture.txt', stagedPatch: '+fixture' }, - params, - { - kind: 'remote', - cwd: '/folder-workspace', - missingBinaryLocation: 'remote PATH', - execute: async () => ({ - stdout: '{"event":"result"}', - stderr: 'Not logged into Antigravity. Run agy login.', - exitCode: 1, - timedOut: false - }) - } - ) - expect(result).toMatchObject({ success: false }) - if (result.success) { - throw new Error('Expected generation to fail') - } - expect(result.error).toContain('Not logged into Antigravity. Run agy login.') - expect(result.error).not.toContain('invalid result') - }) - - it.each([ - 'plain output', - '{"event":"result"}', - '{"event":"step_update","step_update":{"text_delta":"Partial"}}', - '{"event":"result","result":{"status":"SUCCESS","response":42}}' - ])('rejects malformed or incomplete output: %s', (output) => { - expect(parseAntigravityGenerationOutput(output)).toMatchObject({ ok: false }) - }) -}) diff --git a/src/main/text-generation/requests/commit-message-text-generation-regression.test.ts b/src/main/text-generation/requests/commit-message-text-generation-regression.test.ts index b7a66d2a1e6..0faff1733a0 100644 --- a/src/main/text-generation/requests/commit-message-text-generation-regression.test.ts +++ b/src/main/text-generation/requests/commit-message-text-generation-regression.test.ts @@ -2,53 +2,6 @@ import { describe, expect, it } from 'vitest' import { generateCommitMessageFromContext } from '../commit-message-text-generation' describe('commit-message generation regressions', () => { - it('renders the complete commit policy before handing execution to the owning host', async () => { - let prompt = '' - let operation = '' - const result = await generateCommitMessageFromContext( - { - branch: 'feature/policy', - stagedSummary: 'M src/policy.ts', - stagedPatch: '+preserve host scope', - linkedIssue: 73 - }, - { - agentId: 'custom', - model: '', - customAgentCommand: 'agent', - commandInputTemplate: - '{basePrompt}\n\nBranch: {branch}\nFiles: {stagedFiles}\nPatch: {stagedPatch}\nIssue: {linkedIssue}' - }, - { - kind: 'remote', - cwd: '/remote/repo', - missingBinaryLocation: 'remote PATH', - execute: async (plan, _cwd, _timeout, generationOperation) => { - prompt = plan.stdinPayload ?? '' - operation = generationOperation - return { - stdout: 'Preserve source-control policy\n', - stderr: '', - exitCode: 0, - timedOut: false - } - } - } - ) - - expect(operation).toBe('commit-message') - expect(prompt).toContain('Branch: feature/policy') - expect(prompt).toContain('Files: M src/policy.ts') - expect(prompt).toContain('Patch: +preserve host scope') - expect(prompt).toContain('Issue: 73') - expect(prompt).not.toMatch(/\{(?:branch|stagedFiles|stagedPatch|linkedIssue)\}/) - expect(result).toEqual({ - success: true, - message: 'Preserve source-control policy', - agentLabel: 'agent' - }) - }) - it('keeps bounded diagnostic captures out of commit-message results', async () => { const result = await generateCommitMessageFromContext( { diff --git a/src/main/workspace-hooks/hook-archive-timeout-observation.test.ts b/src/main/workspace-hooks/hook-archive-timeout-observation.test.ts deleted file mode 100644 index 683decf17fd..00000000000 --- a/src/main/workspace-hooks/hook-archive-timeout-observation.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import type { Repo } from '../../shared/repo-types' - -vi.mock('./effective-hook-config', () => ({ - getEffectiveHooksFromConfig: (_repo: unknown, hooks: unknown) => hooks -})) - -const REPO: Repo = { id: 'r', path: '/repo', displayName: 'r', badgeColor: '#000', addedAt: 0 } - -async function runArchive(script: string, timeoutMs = 400) { - const { runHook } = await import('./hooks') - const dir = mkdtempSync(join(tmpdir(), 'orca-hook-deadline-')) - writeFileSync(join(dir, 'orca.yaml'), `scripts:\n archive: |\n ${script}\n`) - try { - return await runHook('archive', dir, REPO, dir, undefined, { timeoutMs }) - } finally { - rmSync(dir, { recursive: true, force: true }) - } -} - -// Real shells expose SIGTERM traps that exit 0 after an archive timeout (#19334). -describe.skipIf(process.platform === 'win32')('archive hook deadline', () => { - it('fails a hook that traps SIGTERM and exits zero, despite its zero exit', async () => { - const result = await runArchive("trap 'exit 0' TERM; sleep 30") - expect(result.success).toBe(false) - // Missing exitCode keeps the removal verdict unverifiable. - expect(result.exitCode).toBeUndefined() - expect(result.output).toContain('timed out') - }, 20_000) -}) diff --git a/src/main/workspace-hooks/hook-termination-real-process.test.ts b/src/main/workspace-hooks/hook-termination-real-process.test.ts index fc4123fd6e5..54fdb9f5bcc 100644 --- a/src/main/workspace-hooks/hook-termination-real-process.test.ts +++ b/src/main/workspace-hooks/hook-termination-real-process.test.ts @@ -49,14 +49,6 @@ async function survivorsAfterDeadline( // Mocked kill probes cannot prove a process group exists; exercise real signals here. describe.skipIf(process.platform === 'win32')('hook termination against real processes', () => { - it('kills the shell and its child when the deadline expires', async () => { - const { shell, child, output } = await survivorsAfterDeadline( - 'echo "archive step 3 of 7"\nsleep 120 &\necho "$$ $!" > "$PWD/pids"\nwait' - ) - expect({ shell, child }).toEqual({ shell: false, child: false }) - expect(output).toContain('archive step 3 of 7') - }, 30_000) - it('kills a descendant that ignores SIGTERM', async () => { // Only the group SIGKILL can end this one; a SIGTERM to the shell alone leaves it running. const { child } = await survivorsAfterDeadline( diff --git a/src/main/workspace-hooks/hooks-archive-exit-observation.test.ts b/src/main/workspace-hooks/hooks-archive-exit-observation.test.ts index f178a4e1a45..11158084d23 100644 --- a/src/main/workspace-hooks/hooks-archive-exit-observation.test.ts +++ b/src/main/workspace-hooks/hooks-archive-exit-observation.test.ts @@ -77,37 +77,12 @@ async function runArchiveWith( // Real-process timeout/group tests live in hook-termination-real-process.test.ts (#19334). describe('archive hook exit observation', () => { - it.each([ - ['a non-zero exit', 23], - ['a shell command-not-found', 127] - ])('reports %s as the observed exit it is', async (_label, code) => { - await expect(runArchiveWith({ code })).resolves.toMatchObject({ - success: false, - exitCode: code - }) - }) - - it('caps what it retains from a hook that floods stdout', async () => { - const megabyte = 'x'.repeat(1024 * 1024) - const result = await runArchiveWith( - { code: 0 }, - Array.from({ length: 12 }, () => megabyte) - ) - expect(result.output.length).toBeLessThan(11 * 1024 * 1024) - expect(result.output).toContain('output truncated at 10485760 bytes') - }) - it('survives an error on the output stream', async () => { await expect( runArchiveWith({ code: 0 }, ['partial'], new Error('EIO: read failed')) ).resolves.toMatchObject({ success: true }) }) - it('names a signalled exit as one rather than reporting "exit code null"', async () => { - const result = await runArchiveWith({ code: null, signal: 'SIGKILL' }) - expect(result.output).toContain('terminated without reporting an exit code') - }) - it.each([ ['was killed by a signal', { code: null, signal: 'SIGKILL' as const }], // Spawn errors carry string codes; a bare Error would not exercise the numeric-code guard. diff --git a/src/main/workspace-hooks/hooks-orca-yaml-unc-drive.test.ts b/src/main/workspace-hooks/hooks-orca-yaml-unc-drive.test.ts deleted file mode 100644 index 1aba51458e0..00000000000 --- a/src/main/workspace-hooks/hooks-orca-yaml-unc-drive.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type * as NodeFs from 'node:fs' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' - -const fsMock = vi.hoisted(() => ({ existsSync: vi.fn(), readFileSync: vi.fn() })) -vi.mock('node:fs', async (importOriginal) => ({ - ...(await importOriginal()), - ...fsMock -})) - -import { hasHooksFile, hasUnrecognizedOrcaYamlKeys, loadHooks } from './hooks' - -const UNC = String.raw`\\wsl.localhost\Ubuntu\mnt\c\src\app` -const DRIVE_YAML = String.raw`C:\src\app\orca.yaml` -const originalPlatform = process.platform - -describe('orca.yaml of a UNC drive-mount repo', () => { - beforeEach(() => { - Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' }) - // WSL 2.7 refuses Windows access to a drive mount over UNC (existsSync swallows it as false). - fsMock.existsSync.mockImplementation((path: string) => path === DRIVE_YAML) - fsMock.readFileSync.mockImplementation((path: string) => { - if (path !== DRIVE_YAML) { - throw Object.assign(new Error(`EPERM: operation not permitted, open '${path}'`), { - code: 'EPERM' - }) - } - return 'scripts:\n setup: pnpm install\nfutureKey: 1\n' - }) - }) - - afterEach(() => { - Object.defineProperty(process, 'platform', { configurable: true, value: originalPlatform }) - }) - - it('reads the setup hooks through the drive path', () => { - expect(hasHooksFile(UNC)).toBe(true) - expect(loadHooks(UNC)?.scripts?.setup).toBe('pnpm install') - expect(hasUnrecognizedOrcaYamlKeys(UNC)).toBe(true) - expect(fsMock.readFileSync).not.toHaveBeenCalledWith( - expect.stringMatching(/^\\\\/), - expect.anything() - ) - }) -}) diff --git a/src/main/workspace-hooks/hooks-runner.test.ts b/src/main/workspace-hooks/hooks-runner.test.ts deleted file mode 100644 index 45205adbc4f..00000000000 --- a/src/main/workspace-hooks/hooks-runner.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type * as NodeChildProcess from 'node:child_process' -import type * as NodeFs from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' - -import { describe, expect, it, vi } from 'vitest' - -const { execFileSyncMock, statSyncMock } = vi.hoisted(() => ({ - execFileSyncMock: vi.fn(), - statSyncMock: vi.fn() -})) - -vi.mock('fs', () => ({ - readFileSync: vi.fn(), - statSync: statSyncMock, - existsSync: vi.fn(), - mkdirSync: vi.fn(), - writeFileSync: vi.fn(), - chmodSync: vi.fn() -})) - -vi.mock('child_process', () => ({ - exec: vi.fn(), - execFileSync: execFileSyncMock, - execFile: vi.fn(), - spawn: vi.fn() -})) - -describe('createSetupRunnerScript', () => { - it.skipIf(process.platform === 'win32')( - 'runs a script whose shebang carries invocation-only bash flags', - async () => { - // Reapplying login/interactive flags through set would abort before the first user line. - const { buildPosixRunnerScript } = await import('./setup-runner-script-text') - const { mkdtempSync, rmSync, writeFileSync } = await vi.importActual('node:fs') - const { execFileSync } = await vi.importActual('node:child_process') - - const dir = mkdtempSync(join(tmpdir(), 'orca-posix-runner-')) - try { - const runnerPath = join(dir, 'setup-runner.sh') - writeFileSync( - runnerPath, - buildPosixRunnerScript('#!/bin/bash -l\necho SETUP_RAN\n'), - 'utf-8' - ) - - expect(execFileSync('bash', [runnerPath], { encoding: 'utf-8' })).toContain('SETUP_RAN') - } finally { - rmSync(dir, { recursive: true, force: true }) - } - } - ) -}) From c228a0bbc74fccef8e7e4b4f8431608f1f25dc11 Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 10:21:09 +0000 Subject: [PATCH 068/178] fix(native-chat): the browser Show button says why when the page can't be opened (#41956) The button threw away activateBrowserPagePaletteResult's failure, so a missing page, tab or workspace made it do nothing. It now shows the open-tab switcher's messages, shared through one helper. A blocked activation stays quiet like every other caller: the activation reports it. --- .../runs/NativeChatBrowserPageButton.tsx | 13 ++++-- .../tab-bar/open-tab-selection-routing.ts | 46 +++++++++++-------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.tsx index 1c53ea2a44b..8fb6f802a35 100644 --- a/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.tsx +++ b/src/renderer/src/components/native-chat/tools/runs/NativeChatBrowserPageButton.tsx @@ -1,10 +1,12 @@ import { useEffect, useState, useSyncExternalStore } from 'react' +import { toast } from 'sonner' import { BrowserFavicon } from '@/components/browser-favicon' import { useTranslate } from '@/i18n/use-translate' import { useAppStore } from '@/store' import { findPage } from '@/store/slices/browser/pages/browser-page-records' import { findBrowserWorkspaceForRemotePage } from '@/runtime/web-session-tabs-sync/mirrored-browser-tabs' import { activateBrowserPagePaletteResult } from '@/lib/browser/palette/browser-page-palette-activation' +import { browserPageActivationFailureMessage } from '@/components/tab-bar/open-tab-selection-routing' import { useLastBrowserAgentPageId } from '../../../browser-pane/agent-cursor/browser-agent-activity-state' import { useNativeChatVisualOwner, @@ -160,13 +162,18 @@ function BrowserPageButton({ aria-label={t('components.native-chat.tool.showBrowserPageLabel', 'Show {{value0}}', { value0: title })} - onClick={() => - activateBrowserPagePaletteResult({ + onClick={() => { + const activation = activateBrowserPagePaletteResult({ pageId: page.id, workspaceId: page.workspaceId, worktreeId: page.worktreeId }) - } + // A blocked activation already told the user why. + if (activation.status !== 'failed') { + return + } + toast.error(browserPageActivationFailureMessage(activation.reason)) + }} > {t('components.native-chat.tool.showBrowserPage', 'Show')} diff --git a/src/renderer/src/components/tab-bar/open-tab-selection-routing.ts b/src/renderer/src/components/tab-bar/open-tab-selection-routing.ts index be253e1f4b3..2678380d42a 100644 --- a/src/renderer/src/components/tab-bar/open-tab-selection-routing.ts +++ b/src/renderer/src/components/tab-bar/open-tab-selection-routing.ts @@ -2,7 +2,10 @@ import { requestBrowserFocus } from '@/components/browser-pane/host-guest/browser-focus' import { translate } from '@/i18n/i18n' -import { activateBrowserPagePaletteResult } from '@/lib/browser/palette/browser-page-palette-activation' +import { + activateBrowserPagePaletteResult, + type BrowserPagePaletteActivationFailure +} from '@/lib/browser/palette/browser-page-palette-activation' import { focusTerminalTabSurface } from '@/components/terminal-pane/input/focus/focus-terminal-tab-surface' import { activateSimulatorTabPaletteResult } from '@/lib/simulator/tabs/simulator-tab-palette-activation' import { activateWorkspaceTabPaletteResult } from '@/lib/workspace/search/tabs/workspace-tab-palette-activation' @@ -15,17 +18,30 @@ export type OpenTabSelectionOutcome = | { status: 'failed'; message: string } // Distinguish dead workspaces from stale targets inside live ones across all sources. +function failureMessage(reason: string, staleMessage: string): string { + return reason === 'missing-worktree' + ? translate( + 'auto.components.tab.bar.TabBarCreateEntry.2c38630a01', + 'Workspace no longer exists' + ) + : staleMessage +} + function failed(reason: string, staleMessage: string): OpenTabSelectionOutcome { - return { - status: 'failed', - message: - reason === 'missing-worktree' - ? translate( - 'auto.components.tab.bar.TabBarCreateEntry.2c38630a01', - 'Workspace no longer exists' - ) - : staleMessage - } + return { status: 'failed', message: failureMessage(reason, staleMessage) } +} + +/** Why a browser page could not be shown: a gone workspace, or a gone page or tab. */ +export function browserPageActivationFailureMessage( + reason: BrowserPagePaletteActivationFailure +): string { + return failureMessage( + reason, + translate( + 'auto.components.tab.bar.TabBarCreateEntry.d7d496a451', + 'Browser page no longer exists' + ) + ) } export function activateOpenTabSearchResult(result: OpenTabSearchResult): OpenTabSelectionOutcome { @@ -40,13 +56,7 @@ export function activateOpenTabSearchResult(result: OpenTabSearchResult): OpenTa return activation } if (activation.status === 'failed') { - return failed( - activation.reason, - translate( - 'auto.components.tab.bar.TabBarCreateEntry.d7d496a451', - 'Browser page no longer exists' - ) - ) + return { status: 'failed', message: browserPageActivationFailureMessage(activation.reason) } } return { status: 'activated', From 51d0c97e39009b9ee8f1fb6b451c0f694302d6be Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 10:23:30 +0000 Subject: [PATCH 069/178] fix(browser): publish title, loading and history state for server-run browser tabs (#41952) Streamed (offscreen) pages only republished their session-tab row on commit, in-page navigation, first favicon and load failure. A title parsed after commit, a live document.title change and the end of a load never reached the desktop tab strip or the phone, and the row hardcoded loading and back/forward as false. Publish title and load-state changes through a per-page coalescer and read real loading and history state into the row. --- .../session-routing/agent-browser-bridge-tabs.ts | 5 +++++ .../manager/browser-manager-queries.ts | 13 +++++++++++++ .../manager/browser-manager-state.ts | 6 ++++++ .../browser-manager-guest-navigation-policy.ts | 13 +++++++++++++ .../client-pages/renderer-browser-lifecycle.test.ts | 3 ++- .../orca-runtime-serve-browser-restore.test.ts | 1 + .../build-headless-mobile-session-browser-tabs.ts | 7 ++++--- .../mobile-session-legacy-navigation.unit.test.ts | 1 + 8 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/main/browser/agent-browser/session-routing/agent-browser-bridge-tabs.ts b/src/main/browser/agent-browser/session-routing/agent-browser-bridge-tabs.ts index b972e0e2257..af0ec949cff 100644 --- a/src/main/browser/agent-browser/session-routing/agent-browser-bridge-tabs.ts +++ b/src/main/browser/agent-browser/session-routing/agent-browser-bridge-tabs.ts @@ -122,6 +122,11 @@ export abstract class AgentBrowserBridgeTabs extends AgentBrowserBridgeState { getRemoteTabFaviconUrl(browserPageId: string): string | null { return this.browserManager.getBrowserPageRemoteTabFaviconUrl(browserPageId) } + getPageNavigationState( + browserPageId: string + ): { loading: boolean; canGoBack: boolean; canGoForward: boolean } | null { + return this.browserManager.getBrowserPageNavigationState(browserPageId) + } onTabChanged(webContentsId: number, worktreeId?: string): void { this.selectForPerson(webContentsId, worktreeId) this.options.onTabsChanged?.(worktreeId) diff --git a/src/main/browser/guest-lifecycle/manager/browser-manager-queries.ts b/src/main/browser/guest-lifecycle/manager/browser-manager-queries.ts index 39148d307f6..08b454f12b2 100644 --- a/src/main/browser/guest-lifecycle/manager/browser-manager-queries.ts +++ b/src/main/browser/guest-lifecycle/manager/browser-manager-queries.ts @@ -13,6 +13,7 @@ import { listBrowserRouteGuestPopups } from '../../routing/guest-authority/browser-route-guest-popup-ownership' import { browserPopupPageId, browserPopupPageWebContentsId } from '../popups/browser-popup-page-id' +import { readGuestNavigationState } from '../navigation/browser-guest-navigation-state' export abstract class BrowserManagerQueries extends BrowserManagerRegistration { getGuestWebContentsId(browserTabId: string): number | null { @@ -120,6 +121,17 @@ export abstract class BrowserManagerQueries extends BrowserManagerRegistration { : (this.remoteTabFaviconUrlByGuestId.get(webContentsId) ?? null) } + getBrowserPageNavigationState( + browserPageId: string + ): { loading: boolean; canGoBack: boolean; canGoForward: boolean } | null { + const webContentsId = this.webContentsIdByTabId.get(browserPageId) + const guest = webContentsId === undefined ? undefined : webContents.fromId(webContentsId) + if (!guest || guest.isDestroyed()) { + return null + } + return { loading: guest.isLoadingMainFrame(), ...readGuestNavigationState(guest) } + } + getBrowserPageCertificateFailure(browserPageId: string): BrowserCertificateFailure | null { return this.certificateTrustController?.getFailure(browserPageId) ?? null } @@ -181,6 +193,7 @@ export abstract class BrowserManagerQueries extends BrowserManagerRegistration { } protected notifyBrowserGuestStateChanged(webContentsId: number): void { + this.offscreenGuestStateNotices.cancel(String(webContentsId)) if (!this.offscreenGuestIds.has(webContentsId)) { return } diff --git a/src/main/browser/guest-lifecycle/manager/browser-manager-state.ts b/src/main/browser/guest-lifecycle/manager/browser-manager-state.ts index 378f2bef58f..e0c93da3595 100644 --- a/src/main/browser/guest-lifecycle/manager/browser-manager-state.ts +++ b/src/main/browser/guest-lifecycle/manager/browser-manager-state.ts @@ -20,6 +20,7 @@ import type { import type { BrowserGrabCancelReason } from '../../../../shared/browser/element-grab/browser-grab-types' import { BrowserManagerViewportScrollState } from './viewport/browser-manager-viewport-scroll-state' import { BrowserGuestDevToolsDock } from '../devtools/browser-guest-devtools-dock' +import { createKeyedTrailingEdgeCoalescer } from '../../../runtime/client/events/keyed-trailing-edge-coalescer' export type BrowserManagerSettings = { keybindings?: KeybindingOverrides @@ -168,6 +169,11 @@ export abstract class BrowserManagerState extends BrowserManagerViewportScrollSt // Offscreen pages have no renderer to hold their icon for session-tab rows. protected readonly remoteTabFaviconUrlByGuestId = new Map() protected browserGuestStateChangedListener: ((worktreeId: string) => void) | null = null + // Title and spinner churn on offscreen pages publishes at most about once a second. + protected readonly offscreenGuestStateNotices = createKeyedTrailingEdgeCoalescer( + (webContentsId) => this.notifyBrowserGuestStateChanged(Number(webContentsId)), + { flushMs: 250, maxWaitMs: 1000 } + ) protected offscreenTabOpener: ((request: OffscreenBrowserTabOpenRequest) => void) | null = null protected certificateTrustController: BrowserCertificateTrustController | null = null protected siteDisplayController: BrowserSiteDisplayController | null = null diff --git a/src/main/browser/guest-lifecycle/manager/navigation/browser-manager-guest-navigation-policy.ts b/src/main/browser/guest-lifecycle/manager/navigation/browser-manager-guest-navigation-policy.ts index cf70c4ee0bc..49035073e57 100644 --- a/src/main/browser/guest-lifecycle/manager/navigation/browser-manager-guest-navigation-policy.ts +++ b/src/main/browser/guest-lifecycle/manager/navigation/browser-manager-guest-navigation-policy.ts @@ -195,12 +195,22 @@ export abstract class BrowserManagerGuestNavigationPolicy extends BrowserManager } } + // Offscreen pages have no renderer; titles parse after commit and loads finish later. + const offscreenStateChangedHandler = (): void => { + if (this.offscreenGuestIds.has(guest.id)) { + this.offscreenGuestStateNotices.schedule(String(guest.id)) + } + } + guest.on('will-navigate', willNavigateHandler) guest.on('will-redirect', willRedirectHandler) guest.on('did-start-navigation', didStartNavigationHandler) guest.on('did-navigate', didNavigateHandler) guest.on('did-navigate-in-page', didNavigateInPageHandler) guest.on('page-favicon-updated', faviconUpdatedHandler) + guest.on('page-title-updated', offscreenStateChangedHandler) + guest.on('did-start-loading', offscreenStateChangedHandler) + guest.on('did-stop-loading', offscreenStateChangedHandler) guest.on('did-fail-load', didFailLoadHandler) // Why emit: an extension-blocked guest main frame commits no error page, so Electron reports // nothing; every did-fail-load reader (this policy, the pane, webNavigation) needs the failure. @@ -234,6 +244,9 @@ export abstract class BrowserManagerGuestNavigationPolicy extends BrowserManager guest.off('did-navigate', didNavigateHandler) guest.off('did-navigate-in-page', didNavigateInPageHandler) guest.off('page-favicon-updated', faviconUpdatedHandler) + guest.off('page-title-updated', offscreenStateChangedHandler) + guest.off('did-start-loading', offscreenStateChangedHandler) + guest.off('did-stop-loading', offscreenStateChangedHandler) guest.off('did-fail-load', didFailLoadHandler) guest.off('will-prevent-unload', allowPageUnload) } diff --git a/src/main/runtime/browser/client-pages/renderer-browser-lifecycle.test.ts b/src/main/runtime/browser/client-pages/renderer-browser-lifecycle.test.ts index cfaa857e841..12660d845b2 100644 --- a/src/main/runtime/browser/client-pages/renderer-browser-lifecycle.test.ts +++ b/src/main/runtime/browser/client-pages/renderer-browser-lifecycle.test.ts @@ -53,7 +53,8 @@ function browserRuntime(closeTab = vi.fn(async (_pageId: string) => {}), runtime // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Only the bridge inventory is used by this lifecycle test. runtime.setAgentBrowserBridge({ tabList: () => ({ tabs: [...livePages.values()] }), - getRemoteTabFaviconUrl: () => null + getRemoteTabFaviconUrl: () => null, + getPageNavigationState: () => null } as never) electronMocks.BrowserWindow.fromId.mockImplementation((id: number) => id === 1 ? { isDestroyed: () => false } : null diff --git a/src/main/runtime/browser/headless/orca-runtime-serve-browser-restore.test.ts b/src/main/runtime/browser/headless/orca-runtime-serve-browser-restore.test.ts index 2c2399366d0..3aea2bfc463 100644 --- a/src/main/runtime/browser/headless/orca-runtime-serve-browser-restore.test.ts +++ b/src/main/runtime/browser/headless/orca-runtime-serve-browser-restore.test.ts @@ -43,6 +43,7 @@ function restartedServe(session: WorkspaceSessionState = savedServeTabs()) { })) })), getRemoteTabFaviconUrl: () => null, + getPageNavigationState: () => null, getRegisteredTabs: vi.fn(() => registered()), snapshot: vi.fn(async () => ({ snapshot: '' })), getActivePageId: vi.fn(() => livePages[0] ?? null), diff --git a/src/main/runtime/session-lifecycle/projection/build-headless-mobile-session-browser-tabs.ts b/src/main/runtime/session-lifecycle/projection/build-headless-mobile-session-browser-tabs.ts index 82df9196a57..5be62aad56f 100644 --- a/src/main/runtime/session-lifecycle/projection/build-headless-mobile-session-browser-tabs.ts +++ b/src/main/runtime/session-lifecycle/projection/build-headless-mobile-session-browser-tabs.ts @@ -51,6 +51,7 @@ export abstract class OrcaRuntimeWithBuildHeadlessMobileSessionBrowserTabs exten ].filter((tab) => !rendererPageIds.has(tab.browserPageId)) : [] const publishedServerTabs = serverTabs.map((tab) => { + const navigation = this.agentBrowserBridge?.getPageNavigationState(tab.browserPageId) const persistedProps = this.getPersistedUnifiedSessionTabProps( worktreeId, this.serveBrowserTabs.unifiedTabId(worktreeId, tab.browserPageId) @@ -64,9 +65,9 @@ export abstract class OrcaRuntimeWithBuildHeadlessMobileSessionBrowserTabs exten browserPageId: tab.browserPageId, browserProfileId: tab.profileId ?? undefined, url: tab.url || 'about:blank', - loading: false, - canGoBack: false, - canGoForward: false, + loading: navigation?.loading ?? false, + canGoBack: navigation?.canGoBack ?? false, + canGoForward: navigation?.canGoForward ?? false, faviconUrl: this.agentBrowserBridge?.getRemoteTabFaviconUrl(tab.browserPageId) ?? null, loadError: tab.loadError ?? undefined, certificateFailure: tab.certificateFailure ?? undefined, diff --git a/tests/e2e/session-tabs/mobile-session-legacy-navigation.unit.test.ts b/tests/e2e/session-tabs/mobile-session-legacy-navigation.unit.test.ts index 1eb38585496..d585b24dde5 100644 --- a/tests/e2e/session-tabs/mobile-session-legacy-navigation.unit.test.ts +++ b/tests/e2e/session-tabs/mobile-session-legacy-navigation.unit.test.ts @@ -54,6 +54,7 @@ describe('legacy host navigation through inventory reconstruction', () => { })) }), getRemoteTabFaviconUrl: () => null, + getPageNavigationState: () => null, tabSwitch: vi.fn(async () => ({ switched: 1, browserPageId: 'page-b' })), getRegisteredTabs: () => new Map([ From 86fea2e81cca344277ada8246bcc6004502d94e5 Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 10:29:58 +0000 Subject: [PATCH 070/178] fix(browser): show streamed pages' JavaScript dialogs on the desktop (#41960) The desktop's screencast subscription dropped the stream's dialog and dialogClosed events, so an alert, confirm or prompt on a Server (streamed) page froze it with nothing to answer. Ask with the local browser's dialog, answer through browser.dialogAccept/dialogDismiss, and close it when the host closes the dialog or the stream stops. --- .../remote-browser-javascript-dialog.ts | 81 +++++++++++++++++++ .../remote-browser-page-session.ts | 2 +- .../remote-browser-screencast-subscription.ts | 33 +++++++- .../remote-browser-stream-errors.ts | 5 ++ .../remote-browser-stream-lifecycle-deps.ts | 2 + ...e-browser-stream-lifecycle-test-harness.ts | 23 +++++- .../remote-browser-stream-lifecycle.test.ts | 42 ++++++++++ .../remote-browser-stream-lifecycle.ts | 4 +- .../use-remote-browser-page-input.ts | 11 ++- .../use-remote-browser-page-keyboard.ts | 8 +- .../use-remote-browser-page-lifecycle.ts | 7 +- 11 files changed, 208 insertions(+), 10 deletions(-) create mode 100644 src/renderer/src/components/browser-pane/stream-remote/remote-browser-javascript-dialog.ts diff --git a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-javascript-dialog.ts b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-javascript-dialog.ts new file mode 100644 index 00000000000..ac97374e354 --- /dev/null +++ b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-javascript-dialog.ts @@ -0,0 +1,81 @@ +import type { RuntimeClientTarget } from '../../../../../shared/runtime-client-target' +import type { + BrowserJavaScriptDialog, + BrowserJavaScriptDialogAnswer, + BrowserJavaScriptDialogPrompt +} from '../../../../../shared/browser/prompts/browser-javascript-dialog' +import type { BrowserScreencastDialogResult } from '../../../../../shared/browser/runtime/runtime-browser-contracts' +import type { RemoteBrowserRpcCall } from './remote-browser-page-session' + +export type RemoteBrowserJavaScriptDialogAsk = ( + dialog: BrowserJavaScriptDialogPrompt, + signal: AbortSignal +) => Promise + +export type RemoteBrowserDialogPage = { + target: RuntimeClientTarget + worktree: string + pageId: string +} + +function toJavaScriptDialog(event: BrowserScreencastDialogResult): BrowserJavaScriptDialog | null { + const type = event.dialogType + // The host holds only these; asking about any other kind would offer an answer it refuses. + if (type !== 'alert' && type !== 'confirm' && type !== 'prompt') { + return null + } + return { type, message: event.message, defaultPromptText: '' } +} + +/** Asks the person about the streamed page's open dialog and answers it on the host. */ +export class RemoteBrowserJavaScriptDialog { + private open: AbortController | null = null + + constructor( + private readonly deps: { + askJavaScriptDialog: RemoteBrowserJavaScriptDialogAsk + callRpc: RemoteBrowserRpcCall + } + ) {} + + show(event: BrowserScreencastDialogResult, page: RemoteBrowserDialogPage): void { + const dialog = toJavaScriptDialog(event) + // The host repeats the dialog it still holds when a turned-away nested one closes. + if (!dialog || this.open) { + return + } + const open = new AbortController() + this.open = open + // The stream reports no frame origin, so the title can't name one. + void this.deps.askJavaScriptDialog({ ...dialog, origin: null }, open.signal).then((answer) => { + if (!open.signal.aborted) { + void this.answerOnHost(page, answer, open.signal).catch(() => {}) + } + }) + } + + /** The host closed it (answered here, by an agent or a phone, or by the page), or the stream ended. */ + close(): void { + this.open?.abort() + this.open = null + } + + // A failed answer leaves the dialog to the host, which dismisses it when this stream stops. + private answerOnHost( + { target, worktree, pageId }: RemoteBrowserDialogPage, + answer: BrowserJavaScriptDialogAnswer | null, + // Closing it elsewhere first must not let this answer reach the page's next dialog. + signal: AbortSignal + ): Promise { + const params = { worktree, page: pageId } + const options = { timeoutMs: 15_000, suppressFeatureInteraction: true, signal } + return answer?.accept + ? this.deps.callRpc( + target, + 'browser.dialogAccept', + { ...params, text: answer.promptText }, + options + ) + : this.deps.callRpc(target, 'browser.dialogDismiss', params, options) + } +} diff --git a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-session.ts b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-session.ts index 41d62402a4c..d4e9b579c57 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-session.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-session.ts @@ -13,7 +13,7 @@ export type RemoteBrowserRpcCall = ( target: RuntimeClientTarget, method: string, params?: unknown, - options?: { timeoutMs?: number; suppressFeatureInteraction?: boolean } + options?: { timeoutMs?: number; suppressFeatureInteraction?: boolean; signal?: AbortSignal } ) => Promise export type RemoteBrowserPageHandle = { diff --git a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-screencast-subscription.ts b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-screencast-subscription.ts index 1611e8172dd..af6c78f38de 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-screencast-subscription.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-screencast-subscription.ts @@ -6,6 +6,10 @@ import type { import { withBrowserPaneUiRuntimeRpcSource } from '../../../../../shared/runtime-rpc-feature-interaction-source' import { e2eConfig } from '@/app-shell/startup/e2e-config' import { isRemoteBrowserPageMissingCode } from './remote-browser-stream-errors' +import type { + RemoteBrowserDialogPage, + RemoteBrowserJavaScriptDialog +} from './remote-browser-javascript-dialog' import type { RemoteBrowserViewportSize } from './remote-browser-stream-tokens' export type RemoteBrowserScreencastSubscribe = ( @@ -54,6 +58,8 @@ export type RemoteBrowserScreencastEvents = { onPageMissing: () => void onFrame: (bytes: Uint8Array) => void onClosed: () => void + /** This stream's page dialog; a stopped stream's host dismisses it, so it closes with the stream. */ + javaScriptDialog: RemoteBrowserJavaScriptDialog } export async function openRemoteBrowserScreencastStream( @@ -62,7 +68,12 @@ export async function openRemoteBrowserScreencastStream( events: RemoteBrowserScreencastEvents ): Promise<{ unsubscribe: () => void }> { recordE2eScreencastSubscribe() - return subscribe( + const dialogPage: RemoteBrowserDialogPage = { + target: { kind: 'environment', environmentId: request.environmentId }, + worktree: request.worktree, + pageId: request.pageId + } + const subscription = await subscribe( { selector: request.environmentId, method: 'browser.screencast', @@ -81,7 +92,7 @@ export async function openRemoteBrowserScreencastStream( timeoutMs: 15_000 }, { - onResponse: (response) => dispatchScreencastResponse(response, events), + onResponse: (response) => dispatchScreencastResponse(response, events, dialogPage), onBinary: (bytes) => events.onFrame(bytes), onError: (error) => { if (!events.isCurrent()) { @@ -93,14 +104,24 @@ export async function openRemoteBrowserScreencastStream( } events.onTransportError(error.message) }, - onClose: () => events.onClosed() + onClose: () => { + events.javaScriptDialog.close() + events.onClosed() + } } ) + return { + unsubscribe: () => { + events.javaScriptDialog.close() + subscription.unsubscribe() + } + } } function dispatchScreencastResponse( response: RuntimeRpcResponse, - events: RemoteBrowserScreencastEvents + events: RemoteBrowserScreencastEvents, + dialogPage: RemoteBrowserDialogPage ): void { if (!events.isCurrent()) { return @@ -121,6 +142,10 @@ function dispatchScreencastResponse( events.onReady(event) } else if (event.type === 'end') { events.onEnded() + } else if (event.type === 'dialog') { + events.javaScriptDialog.show(event, dialogPage) + } else if (event.type === 'dialogClosed') { + events.javaScriptDialog.close() } else if (event.type === 'error') { events.onFailed(event.message) } diff --git a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-errors.ts b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-errors.ts index 8e51b28308e..1df9dda1d1a 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-errors.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-errors.ts @@ -49,6 +49,11 @@ export function isRemoteBrowserPageMissingError(error: unknown): boolean { return isRemoteBrowserPageMissingCode(readErrorCode(error)) } +// The input landed and opened a page dialog, which the stream shows; its agent instructions are not for a person. +export function isRemoteBrowserInputOpenedDialogError(error: unknown): boolean { + return readErrorCode(error) === 'browser_dialog_opened' +} + // Stop automatic retry only for proven permanent failures; unclassified failures retain bounded recovery. export function isPermanentRemoteBrowserStreamFailure(error: unknown): boolean { const code = readErrorCode(error) diff --git a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle-deps.ts b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle-deps.ts index 85309a0e85f..5b3fea412c6 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle-deps.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle-deps.ts @@ -1,3 +1,4 @@ +import type { RemoteBrowserJavaScriptDialogAsk } from './remote-browser-javascript-dialog' import type { RemoteBrowserPageSessionDeps } from './remote-browser-page-session' import type { RemoteBrowserScreencastSubscribe } from './remote-browser-screencast-subscription' import type { RemoteBrowserStreamStatus } from './remote-browser-stream-status' @@ -18,4 +19,5 @@ export type RemoteBrowserStreamLifecycleDeps = Omit void clearFrame: () => void handleFrameBytes: (token: RemoteBrowserStreamToken, bytes: Uint8Array) => void + askJavaScriptDialog: RemoteBrowserJavaScriptDialogAsk } diff --git a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle-test-harness.ts b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle-test-harness.ts index bcbec3ba39b..c2052eb4fa9 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle-test-harness.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle-test-harness.ts @@ -3,6 +3,10 @@ import { RemoteBrowserStreamLifecycle } from './remote-browser-stream-lifecycle' import type { RemoteBrowserPageHandle, RemoteBrowserRpcCall } from './remote-browser-page-session' import type { RemoteBrowserScreencastSubscribe } from './remote-browser-screencast-subscription' import type { RemoteBrowserViewportSize } from './remote-browser-stream-tokens' +import type { + BrowserJavaScriptDialogAnswer, + BrowserJavaScriptDialogPrompt +} from '../../../../../shared/browser/prompts/browser-javascript-dialog' import { canReconnectRemoteBrowserStream, isRemoteBrowserStreamBusy, @@ -40,6 +44,14 @@ export type FakeScreencastStream = { emitResponseFailure: (code: string, message: string) => void emitTransportError: (code: string, message: string) => void emitClose: () => void + emitDialog: (dialogType: string, message: string) => void + emitDialogClosed: () => void +} + +export type DialogAsk = { + dialog: BrowserJavaScriptDialogPrompt + signal: AbortSignal + answer: (answer: BrowserJavaScriptDialogAnswer | null) => void } export function rpcError(code: string, message: string): Error { @@ -59,6 +71,8 @@ export function createHarness() { const closedPages: (string | null)[] = [] const streams: FakeScreencastStream[] = [] const rpcLog: string[] = [] + const rpcParams = new Map() + const dialogAsks: DialogAsk[] = [] let capabilities: string[] = ['browser.screencast.v1'] let storedHandle: RemoteBrowserPageHandle | null = null @@ -75,6 +89,7 @@ export function createHarness() { // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the fake answers each method with the reply shape its caller reads. const callRpc = (async (_target: unknown, method: string, params?: { page?: string }) => { rpcLog.push(method) + rpcParams.set(method, params) if (method === 'status.get') { if (statusGate) { const gate = statusGate @@ -152,7 +167,9 @@ export function createHarness() { _meta: { runtimeId: 'runtime-1' } }), emitTransportError: (code, message) => callbacks.onError?.({ code, message }), - emitClose: () => callbacks.onClose?.() + emitClose: () => callbacks.onClose?.(), + emitDialog: (dialogType, message) => respond({ type: 'dialog', dialogType, message }), + emitDialogClosed: () => respond({ type: 'dialogClosed' }) } streams.push(stream) return { @@ -193,6 +210,8 @@ export function createHarness() { }, readViewportSize: () => viewportSize, syncViewport: async () => {}, + askJavaScriptDialog: (dialog, signal) => + new Promise((answer) => dialogAsks.push({ dialog, signal, answer })), getDeviceScaleFactor: () => 1, setStatus: (status) => statusLog.push(status), clearFrame: () => {}, @@ -207,6 +226,8 @@ export function createHarness() { closedPages, streams, rpcLog, + dialogAsks, + rpcParams: (method: string): unknown => rpcParams.get(method), get subscribeAttempts(): number { return subscribeAttempts }, diff --git a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle.test.ts b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle.test.ts index 3265d28661d..499a965110b 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle.test.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle.test.ts @@ -15,6 +15,48 @@ describe('RemoteBrowserStreamLifecycle', () => { vi.useRealTimers() }) + it("asks once about the streamed page's dialog and answers it on the host", async () => { + const harness = createHarness() + await openStreamAndConfirmReady(harness) + const stream = harness.streams[0] + + stream.emitDialog('confirm', 'Proceed?') + // The host repeats the dialog it still holds when a turned-away nested one closes. + stream.emitDialog('confirm', 'Proceed?') + expect(harness.dialogAsks).toHaveLength(1) + expect(harness.dialogAsks[0].dialog).toEqual({ + type: 'confirm', + message: 'Proceed?', + defaultPromptText: '', + origin: null + }) + + harness.dialogAsks[0].answer({ accept: true, promptText: '' }) + await settle() + expect(harness.rpcParams('browser.dialogAccept')).toEqual({ + worktree: 'worktree:wt-1', + page: 'page-1', + text: '' + }) + }) + + it('drops the ask when the host closes the dialog some other way', async () => { + const harness = createHarness() + await openStreamAndConfirmReady(harness) + const stream = harness.streams[0] + + stream.emitDialog('alert', 'Saved') + stream.emitDialogClosed() + expect(harness.dialogAsks[0].signal.aborted).toBe(true) + + // The next dialog gets its own ask. + stream.emitDialog('alert', 'Again') + expect(harness.dialogAsks).toHaveLength(2) + harness.dialogAsks[1].answer(null) + await settle() + expect(harness.rpcLog).toContain('browser.dialogDismiss') + }) + // Parked unmount has no effect cleanup left; parking must already have unsubscribed. it('leaves nothing subscribed when the pane unmounts while its stream is parked', async () => { const harness = createHarness() diff --git a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle.ts b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle.ts index 623c7728272..fb3c40ce406 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle.ts @@ -1,6 +1,7 @@ import type { RuntimeClientTarget } from '../../../../../shared/runtime-client-target' import type { RuntimeStatus } from '../../../../../shared/runtime-types' import { RemoteBrowserPageSession } from './remote-browser-page-session' +import { RemoteBrowserJavaScriptDialog } from './remote-browser-javascript-dialog' import { openRemoteBrowserScreencastStream } from './remote-browser-screencast-subscription' import { RemoteBrowserStreamRestartScheduler } from './remote-browser-stream-restart-scheduler' import { RemoteBrowserStreamLiveness } from './remote-browser-stream-liveness' @@ -273,7 +274,8 @@ export class RemoteBrowserStreamLifecycle { }, onPageMissing: () => deps.closeMissingRemotePage(pageId), onFrame: (bytes) => deps.handleFrameBytes(token, bytes), - onClosed: () => this.handleStreamClosed(token, true) + onClosed: () => this.handleStreamClosed(token, true), + javaScriptDialog: new RemoteBrowserJavaScriptDialog(deps) } ) return { token, unsubscribe: subscription.unsubscribe } diff --git a/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-input.ts b/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-input.ts index e01a458bc49..3431469c0a5 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-input.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-input.ts @@ -1,6 +1,9 @@ import { useCallback, useRef } from 'react' import { callRuntimeRpc } from '@/runtime/rpc/calls/runtime-rpc-call' -import { isRemoteBrowserPageMissingError } from './remote-browser-stream-errors' +import { + isRemoteBrowserInputOpenedDialogError, + isRemoteBrowserPageMissingError +} from './remote-browser-stream-errors' import { useRemoteBrowserPageKeyboard } from './use-remote-browser-page-keyboard' import type { RemoteBrowserStreamLifecycle } from './remote-browser-stream-lifecycle' import type { @@ -175,6 +178,9 @@ export function useRemoteBrowserPageInput({ closeMissingRemotePage(pageId) return } + if (isRemoteBrowserInputOpenedDialogError(error)) { + return + } setPaneNotice({ kind: 'consequence', text: error instanceof Error ? error.message : 'Remote mouse input failed.' @@ -226,6 +232,9 @@ export function useRemoteBrowserPageInput({ closeMissingRemotePage(pageId) return } + if (isRemoteBrowserInputOpenedDialogError(error)) { + return + } setPaneNotice({ kind: 'consequence', text: error instanceof Error ? error.message : 'Remote mouse input failed.' diff --git a/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-keyboard.ts b/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-keyboard.ts index c20574aa963..536f0fdf723 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-keyboard.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-keyboard.ts @@ -14,7 +14,10 @@ import { getRemoteBrowserKeyboardShortcut, getRemoteBrowserKeypressKey } from './remote-browser-keyboard' -import { isRemoteBrowserPageMissingError } from './remote-browser-stream-errors' +import { + isRemoteBrowserInputOpenedDialogError, + isRemoteBrowserPageMissingError +} from './remote-browser-stream-errors' import type { RemoteBrowserStreamLifecycle } from './remote-browser-stream-lifecycle' import type { RemoteBrowserOperationToken } from './remote-browser-stream-tokens' import { @@ -178,6 +181,9 @@ export function useRemoteBrowserPageKeyboard({ closeMissingRemotePage(pageId) return } + if (isRemoteBrowserInputOpenedDialogError(error)) { + return + } setPaneNotice({ kind: 'consequence', text: error instanceof Error ? error.message : 'Remote keyboard input failed.' diff --git a/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-lifecycle.ts b/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-lifecycle.ts index 5870a9ccb9b..0f4f6a42ca5 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-lifecycle.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-lifecycle.ts @@ -23,6 +23,8 @@ import { type RemoteBrowserStreamBridge } from './remote-browser-page-input-model' import { subscribeRuntimeEnvironment } from '@/runtime/rpc/environment-revisions/runtime-environment-pairing-refresh' +import { useConfirmationDialog } from '@/components/confirmation-dialog-context' +import { javaScriptDialogPrompt } from '../browser-javascript-dialog-prompt' export function useRemoteBrowserPageLifecycle({ browserTab, @@ -68,6 +70,8 @@ export function useRemoteBrowserPageLifecycle({ const lifecycleRef = useRef(null) const closeBrowserPage = useAppStore((s) => s.closeBrowserPage) const closeBrowserTab = useAppStore((s) => s.closeBrowserTab) + // Stable for the provider's life, so the once-built lifecycle may hold it. + const confirm = useConfirmationDialog() useLayoutEffect(() => { currentBrowserTabIdRef.current = browserTab.id @@ -113,7 +117,8 @@ export function useRemoteBrowserPageLifecycle({ streamBridgeRef.current.closeMissingRemotePage(remotePageId), waitForViewportSize: () => streamBridgeRef.current.waitForViewportSize(), readViewportSize: () => remoteViewportSizeRef.current, - syncViewport: (pageId) => streamBridgeRef.current.syncViewport(pageId) + syncViewport: (pageId) => streamBridgeRef.current.syncViewport(pageId), + askJavaScriptDialog: (dialog, signal) => javaScriptDialogPrompt.ask(confirm, dialog, signal) }) } const lifecycle = lifecycleRef.current From 85873ef846af8735dcb6e5e2561b717864944478 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 02:32:58 -0800 Subject: [PATCH 071/178] test: remove Git staging and status replays (#41966) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 1 - .../drive-checkout-suspect-paths.test.ts | 2 - .../ignores/huge-folder-ignore.test.ts | 9 - .../mutations/stage-worktree-changes.test.ts | 19 -- .../staging-discard-index-safety.test.ts | 15 -- .../mutations/status-discard-symlink.test.ts | 16 -- .../status/porcelain-v1-records.test.ts | 31 ---- ...us-branch-line-total-exec-contract.test.ts | 163 +----------------- .../status-branch-line-total-real-git.test.ts | 32 ---- ...s-branch-line-total-untracked-base.test.ts | 37 ---- .../status/status-cquoted-paths.test.ts | 45 ----- .../status-line-stats-host-paths.test.ts | 41 ----- ...status-line-stats-renames-real-git.test.ts | 2 - .../status/status-shared-symlinks.test.ts | 57 ------ 14 files changed, 1 insertion(+), 469 deletions(-) delete mode 100644 src/main/git/source-control/status/status-cquoted-paths.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 9aa6ee2513c..282a02f98c0 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -1732,7 +1732,6 @@ "src/main/git/source-control/status/status-branch-line-total-exec-contract.test.ts": 1033, "src/main/git/source-control/status/status-branch-line-total-real-git.test.ts": 785, "src/main/git/source-control/status/status-branch-line-total-untracked-base.test.ts": 700, - "src/main/git/source-control/status/status-cquoted-paths.test.ts": 348, "src/main/git/source-control/diffs/status-diff.test.ts": 327, "src/main/git/source-control/status-discard-and-bulk-staging.test.ts": 289, "src/main/git/source-control/mutations/status-discard-symlink.test.ts": 456, diff --git a/src/main/git/source-control/drive-checkout/drive-checkout-suspect-paths.test.ts b/src/main/git/source-control/drive-checkout/drive-checkout-suspect-paths.test.ts index a0c1427b75b..1ce9ae395a5 100644 --- a/src/main/git/source-control/drive-checkout/drive-checkout-suspect-paths.test.ts +++ b/src/main/git/source-control/drive-checkout/drive-checkout-suspect-paths.test.ts @@ -25,7 +25,6 @@ describe('DriveCheckoutSuspectPaths', () => { const reinstalled = { rootPath: REPO } tracker.recordEvents(reinstalled, [event('new.ts')]) expect([...(tracker.beginPoll(reinstalled)?.keys() ?? [])]).toEqual(['new.ts']) - expect(tracker.lastDegradeReason).toBe('watcher-restart') }) it('keeps rechecked-dirty paths through overflow, interruption, restart, and a lost watcher', () => { @@ -33,7 +32,6 @@ describe('DriveCheckoutSuspectPaths', () => { tracker.recordEvents(root, [event('hidden.ts')]) tracker.confirmDirty('hidden.ts', tracker.beginPoll(root)?.get('hidden.ts') ?? 0) tracker.recordEvents(root, [event('a'), event('b'), event('c')]) - expect(tracker.lastDegradeReason).toBe('over-limit') tracker.recordInterruption(root) const reinstalled = { rootPath: REPO } expect([...(tracker.beginPoll(reinstalled)?.keys() ?? [])]).toEqual(['hidden.ts']) diff --git a/src/main/git/source-control/ignores/huge-folder-ignore.test.ts b/src/main/git/source-control/ignores/huge-folder-ignore.test.ts index d4e09cd356b..1f053dc2415 100644 --- a/src/main/git/source-control/ignores/huge-folder-ignore.test.ts +++ b/src/main/git/source-control/ignores/huge-folder-ignore.test.ts @@ -25,13 +25,6 @@ describe('appendFolderToGitignore', () => { await fs.rm(dir, { recursive: true, force: true }) }) - it('creates .gitignore with the folder pattern when absent', async () => { - const wrote = await appendFolderToGitignore(dir, 'node_modules') - expect(wrote).toBe(true) - const content = await fs.readFile(path.join(dir, '.gitignore'), 'utf-8') - expect(content).toContain('node_modules/') - }) - it('appends with a leading newline when the file lacks a trailing one', async () => { await fs.writeFile(path.join(dir, '.gitignore'), '*.log') const wrote = await appendFolderToGitignore(dir, 'dist') @@ -44,7 +37,5 @@ describe('appendFolderToGitignore', () => { await expect(appendFolderToGitignore(dir, 'node_modules\n/etc/passwd')).rejects.toThrow( /Refusing to add/ ) - await expect(appendFolderToGitignore(dir, '../escape')).rejects.toThrow(/Refusing to add/) - await expect(appendFolderToGitignore(dir, 'arbitrary')).rejects.toThrow(/Refusing to add/) }) }) diff --git a/src/main/git/source-control/mutations/stage-worktree-changes.test.ts b/src/main/git/source-control/mutations/stage-worktree-changes.test.ts index cf678ddb4a8..72110bd59d8 100644 --- a/src/main/git/source-control/mutations/stage-worktree-changes.test.ts +++ b/src/main/git/source-control/mutations/stage-worktree-changes.test.ts @@ -85,15 +85,6 @@ describe('stageWorktreeChanges', () => { expect(names(repo, ['diff', '--cached', '--name-only'])).toHaveLength(OVER_CAP + 1) }) - it('stages only tracked changes for the tracked scope', async () => { - const repo = await createOverCapRepo() - - await stageWorktreeChanges(repo, 'tracked') - - expect(names(repo, ['diff', '--name-only'])).toEqual([]) - expect(names(repo, ['ls-files', '--others', '--exclude-standard'])).toEqual(['zz-new.txt']) - }) - it('leaves unresolved conflicts unstaged', async () => { const repo = await createConflictRepo() @@ -177,15 +168,5 @@ describe('stageWorktreeChanges', () => { ]) }) }) - - it('still stages a regular file the user put at a shared name', async () => { - const repo = await createRepo() - git(repo, ['commit', '-q', '--allow-empty', '-m', 'initial']) - await writeFile(path.join(repo, 'node_modules'), 'not a link\n') - - await stageWorktreeChanges(repo, 'all', { sharedLinkPaths: ['node_modules'] }) - - expect(names(repo, ['diff', '--cached', '--name-only'])).toEqual(['node_modules']) - }) }) }) diff --git a/src/main/git/source-control/mutations/staging-discard-index-safety.test.ts b/src/main/git/source-control/mutations/staging-discard-index-safety.test.ts index e966767ec53..95b8a7245dc 100644 --- a/src/main/git/source-control/mutations/staging-discard-index-safety.test.ts +++ b/src/main/git/source-control/mutations/staging-discard-index-safety.test.ts @@ -76,21 +76,6 @@ describe('staging and discard preserve index authority', () => { expect(await readFile(path.join(repo, 'selected.txt'), 'utf8')).toBe('selected\n') }) - it('discards a staged rename after unstaging both paths', async () => { - await writeFile(path.join(repo, 'old.txt'), 'rename content\n') - gitCommit(repo, 'initial') - await git(['mv', 'old.txt', 'new.txt']) - - await mutate({ bulk: true }, 'unstage', ['new.txt', 'old.txt']) - await mutate({ bulk: true }, 'discard', ['new.txt', 'old.txt']) - - expect(await git(['status', '--porcelain'])).toBe('') - expect(await readFile(path.join(repo, 'old.txt'), 'utf8')).toBe('rename content\n') - await expect(readFile(path.join(repo, 'new.txt'), 'utf8')).rejects.toMatchObject({ - code: 'ENOENT' - }) - }) - it.each(modes)('rejects an unresolved conflict on bulk=$bulk', async (mode) => { await writeFile(path.join(repo, 'file.txt'), 'base\n') gitCommit(repo, 'initial') diff --git a/src/main/git/source-control/mutations/status-discard-symlink.test.ts b/src/main/git/source-control/mutations/status-discard-symlink.test.ts index 611ccf73398..5eebbeac9b7 100644 --- a/src/main/git/source-control/mutations/status-discard-symlink.test.ts +++ b/src/main/git/source-control/mutations/status-discard-symlink.test.ts @@ -129,22 +129,6 @@ describe('discardChanges symlink safety', () => { await expect(readFile(path.join(repo, globMatchedFile), 'utf8')).resolves.toBe('keep modified') }) - it('treats bulk untracked discard paths with Git glob characters as literal paths', async () => { - const { repo } = await createRepoWithOutsideDirectory() - await writeFile(path.join(repo, '.gitignore'), 'ignored.log\n') - execFileSync('git', ['add', '.gitignore'], { cwd: repo }) - execFileSync('git', ['commit', '-q', '-m', 'ignore log fixture'], { cwd: repo }) - await writeFile(path.join(repo, globNamedFile), 'selected') - await writeFile(path.join(repo, globMatchedFile), 'unrelated') - await writeFile(path.join(repo, 'ignored.log'), 'ignored') - - await bulkDiscardChanges(repo, [globNamedFile]) - - await expect(access(path.join(repo, globNamedFile))).rejects.toThrow() - await expect(access(path.join(repo, globMatchedFile))).resolves.toBeUndefined() - await expect(access(path.join(repo, 'ignored.log'))).resolves.toBeUndefined() - }) - it('removes untracked nested git repos selected for discard', async () => { const { repo } = await createRepoWithOutsideDirectory() const nestedRepo = path.join(repo, 'nested') diff --git a/src/main/git/source-control/status/porcelain-v1-records.test.ts b/src/main/git/source-control/status/porcelain-v1-records.test.ts index 3d6a8188873..f0d1cf82468 100644 --- a/src/main/git/source-control/status/porcelain-v1-records.test.ts +++ b/src/main/git/source-control/status/porcelain-v1-records.test.ts @@ -26,37 +26,6 @@ afterEach(() => { }) describe('parsePorcelainV1Records', () => { - it('keeps paths containing spaces and quotes intact', () => { - const repo = createRepo() - writeFileSync(join(repo, 'a file "quoted".txt'), 'x') - - const records = parsePorcelainV1Records(git(['status', '--porcelain', '-z'], repo)) - - expect(records).toEqual([{ xy: '??', path: 'a file "quoted".txt' }]) - }) - - it('keeps non-ASCII paths raw rather than C-quoted', () => { - const repo = createRepo() - writeFileSync(join(repo, '日本語.txt'), 'x') - - expect(parsePorcelainV1Records(git(['status', '--porcelain', '-z'], repo))).toEqual([ - { xy: '??', path: '日本語.txt' } - ]) - }) - - // Rename origins are extra NUL fields; treating one as a record could invent an untracked status. - it('consumes the origin path of a rename instead of emitting it as a record', () => { - const repo = createRepo() - writeFileSync(join(repo, 'original.txt'), 'content\n') - git(['add', '-A'], repo) - git(['commit', '-qm', 'init'], repo) - git(['mv', 'original.txt', 'renamed.txt'], repo) - - const records = parsePorcelainV1Records(git(['status', '--porcelain', '-z'], repo)) - - expect(records).toEqual([{ xy: 'R ', path: 'renamed.txt' }]) - }) - it('reports a rename alongside a later untracked entry', () => { const repo = createRepo() writeFileSync(join(repo, 'original.txt'), 'content\n') diff --git a/src/main/git/source-control/status/status-branch-line-total-exec-contract.test.ts b/src/main/git/source-control/status/status-branch-line-total-exec-contract.test.ts index 0e7fe6e6207..4811d84928f 100644 --- a/src/main/git/source-control/status/status-branch-line-total-exec-contract.test.ts +++ b/src/main/git/source-control/status/status-branch-line-total-exec-contract.test.ts @@ -1,71 +1,15 @@ -import type * as GitExecFileModule from '../../command-runner/git-exec-file' -import type * as GitStreamStdoutModule from '../../command-runner/git-stream-stdout' import { execFileSync } from 'node:child_process' import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import * as path from 'node:path' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { invalidateGitBranchLineTotalInFlight } from '../../../../shared/git/status/git-branch-line-total' -import type * as BranchLineTotal from '../../../../shared/git/status/git-branch-line-total' - -// Spy on real Git argv to validate opt-in and coalescing without stubbing results. -const { gitExecCalls, execHooks, coalescerJoins } = vi.hoisted(() => ({ - gitExecCalls: [] as string[][], - execHooks: { beforeExec: undefined as undefined | ((args: string[]) => Promise | void) }, - coalescerJoins: { count: 0, onJoin: undefined as undefined | (() => void) } -})) - -vi.mock('../../../../shared/git/status/git-branch-line-total', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - computeGitBranchLineTotal: ( - input: Parameters[0] - ) => { - // Lease acquisition is synchronous, so a join is observable immediately after this call. - const total = actual.computeGitBranchLineTotal(input) - coalescerJoins.count += 1 - coalescerJoins.onJoin?.() - return total - } - } -}) - -vi.mock('../../command-runner/git-exec-file', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - gitExecFileAsync: async (...args: Parameters) => { - gitExecCalls.push(args[0]) - await execHooks.beforeExec?.(args[0]) - return actual.gitExecFileAsync(...args) - }, - gitExecFileAsyncBuffer: async (...args: Parameters) => { - gitExecCalls.push(args[0]) - return actual.gitExecFileAsyncBuffer(...args) - } - } -}) - -vi.mock('../../command-runner/git-stream-stdout', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - gitStreamStdout: async (...args: Parameters) => { - gitExecCalls.push(args[0]) - return actual.gitStreamStdout(...args) - } - } -}) - import { clearEffectiveUpstreamStatusCacheForTests } from '../upstream/effective-upstream-status-cache' import { clearSubmodulePathsCacheForTests } from '../submodules/submodule-paths' import { getStatus } from './status-read' import { invalidateGitReadCaches } from '../git-read-cache-invalidation' -const BOGUS_MERGE_BASE = 'deadbeef'.repeat(5) -const NO_LINES = { added: 0, removed: 0 } const tempRoots: string[] = [] function git(repo: string, args: string[]): string { @@ -97,26 +41,6 @@ async function write(repo: string, relativePath: string, contents: string): Prom await writeFile(target, contents) } -/** The `mergeBase → worktree` diff, distinguished from the per-area numstats by its rev + `--`. */ -function rangedDiffCalls(): string[][] { - return gitExecCalls.filter((args) => args.includes('--numstat') && args.at(-1) === '--') -} - -/** Resolves once `count` status passes have entered the branch-total coalescer. */ -function waitForCoalescerJoins(count: number): Promise { - if (coalescerJoins.count >= count) { - return Promise.resolve() - } - return new Promise((resolve) => { - coalescerJoins.onJoin = () => { - if (coalescerJoins.count >= count) { - coalescerJoins.onJoin = undefined - resolve() - } - } - }) -} - function resetGitReadCaches(): void { clearEffectiveUpstreamStatusCacheForTests() clearSubmodulePathsCacheForTests() @@ -125,16 +49,10 @@ function resetGitReadCaches(): void { } beforeEach(() => { - gitExecCalls.length = 0 - execHooks.beforeExec = undefined - coalescerJoins.count = 0 - coalescerJoins.onJoin = undefined resetGitReadCaches() }) afterEach(async () => { - execHooks.beforeExec = undefined - coalescerJoins.onJoin = undefined resetGitReadCaches() await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true }))) }) @@ -151,84 +69,5 @@ describe('branch line total completeness', () => { expect(result.didHitLimit).toBe(true) expect(result.branchLineTotal).toBeUndefined() expect(result.branchLineTotalUnavailable).toBeUndefined() - expect(rangedDiffCalls()).toEqual([]) - }) - - it('invalidates the total when the ranged numstat fails, rather than reporting zero', async () => { - const { repo } = await createFixtureRepo() - - const result = await getStatus(repo, { branchLineTotalMergeBase: BOGUS_MERGE_BASE }) - - expect(result.entries.length).toBeGreaterThan(0) - expect(result.branchLineTotal).toBeUndefined() - expect(result.branchLineTotalUnavailable).toBe(BOGUS_MERGE_BASE) - expect(rangedDiffCalls()).toHaveLength(1) - }) - - it('rejects with an AbortError when the pass is cancelled mid-diff', async () => { - const { repo, mergeBase } = await createFixtureRepo() - const controller = new AbortController() - execHooks.beforeExec = (args) => { - if (args.includes('--numstat') && args.at(-1) === '--') { - controller.abort() - } - } - - await expect( - getStatus(repo, { branchLineTotalMergeBase: mergeBase, signal: controller.signal }) - ).rejects.toMatchObject({ name: 'AbortError' }) - expect(rangedDiffCalls()).toHaveLength(1) - }) -}) - -describe('branch line total exec budget', () => { - it('shares one ranged diff across status passes the status lease cannot merge', async () => { - const { repo, mergeBase } = await createFixtureRepo() - // Different limits require separate status leases; hold the diff until both join its shared lease. - execHooks.beforeExec = async (args) => { - if (args.includes('--numstat') && args.at(-1) === '--') { - await waitForCoalescerJoins(2) - } - } - - const [first, second] = await Promise.all([ - getStatus(repo, { branchLineTotalMergeBase: mergeBase, limit: 0 }), - getStatus(repo, { branchLineTotalMergeBase: mergeBase, limit: 4096 }) - ]) - - expect(first.branchLineTotal).toEqual({ - added: 1, - removed: 0, - mergeBase, - test: NO_LINES, - generated: NO_LINES - }) - expect(second.branchLineTotal).toEqual(first.branchLineTotal) - expect(gitExecCalls.filter((args) => args.includes('status'))).toHaveLength(2) - expect(rangedDiffCalls()).toHaveLength(1) - }) - - it('recomputes rather than reusing a total measured against another merge base', async () => { - const { repo, mergeBase } = await createFixtureRepo() - git(repo, ['add', '-A']) - git(repo, ['commit', '-q', '-m', 'second']) - const laterMergeBase = git(repo, ['rev-parse', 'HEAD']) - - await getStatus(repo, { branchLineTotalMergeBase: mergeBase, reuseLineStats: true }) - gitExecCalls.length = 0 - invalidateGitBranchLineTotalInFlight() - const result = await getStatus(repo, { - branchLineTotalMergeBase: laterMergeBase, - reuseLineStats: true - }) - - expect(result.branchLineTotal).toEqual({ - added: 0, - removed: 0, - mergeBase: laterMergeBase, - test: NO_LINES, - generated: NO_LINES - }) - expect(rangedDiffCalls()).toHaveLength(1) }) }) diff --git a/src/main/git/source-control/status/status-branch-line-total-real-git.test.ts b/src/main/git/source-control/status/status-branch-line-total-real-git.test.ts index c0cf4420fb0..855f4e69e1c 100644 --- a/src/main/git/source-control/status/status-branch-line-total-real-git.test.ts +++ b/src/main/git/source-control/status/status-branch-line-total-real-git.test.ts @@ -42,20 +42,6 @@ function commitAll(repo: string, message: string): string { const NO_LINES = { added: 0, removed: 0 } -function rangedDiffTotal(repo: string, mergeBase: string): { added: number; removed: number } { - let added = 0 - let removed = 0 - for (const line of git(repo, ['diff', '--numstat', '-M', mergeBase, '--']).split(/\r?\n/)) { - if (!line) { - continue - } - const [rawAdded, rawRemoved] = line.split('\t') - added += rawAdded === '-' ? 0 : Number.parseInt(rawAdded ?? '0', 10) - removed += rawRemoved === '-' ? 0 : Number.parseInt(rawRemoved ?? '0', 10) - } - return { added, removed } -} - beforeEach(() => { invalidateGitReadCaches() invalidateGitBranchLineTotalInFlight() @@ -79,7 +65,6 @@ describe('branch line total against a real repository', () => { const result = await getStatus(repo, { branchLineTotalMergeBase: mergeBase }) - expect(rangedDiffTotal(repo, mergeBase)).toEqual({ added: 1, removed: 0 }) expect(result.branchLineTotal).toEqual({ added: 1, removed: 0, @@ -128,21 +113,4 @@ describe('branch line total against a real repository', () => { generated: NO_LINES }) }) - - it('omits the total when a deleted base file is recreated as untracked', async () => { - const repo = await createFixtureRepo() - await write(repo, 'gone.txt', 'a\nb\nc\n') - const mergeBase = commitAll(repo, 'base') - git(repo, ['rm', '-q', 'gone.txt']) - commitAll(repo, 'delete gone.txt') - await write(repo, 'gone.txt', 'n1\nn2\n') - - const result = await getStatus(repo, { branchLineTotalMergeBase: mergeBase }) - - expect(result.entries.map((entry) => [entry.area, entry.path])).toEqual([ - ['untracked', 'gone.txt'] - ]) - expect(rangedDiffTotal(repo, mergeBase)).toEqual({ added: 0, removed: 3 }) - expect(result.branchLineTotal).toBeUndefined() - }) }) diff --git a/src/main/git/source-control/status/status-branch-line-total-untracked-base.test.ts b/src/main/git/source-control/status/status-branch-line-total-untracked-base.test.ts index 3afb62b5687..4d6a32e016b 100644 --- a/src/main/git/source-control/status/status-branch-line-total-untracked-base.test.ts +++ b/src/main/git/source-control/status/status-branch-line-total-untracked-base.test.ts @@ -3,7 +3,6 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, expect, it } from 'vitest' -import { getBranchCompare } from '../comparisons/branch-compare' import { getStatus } from './status-read' import { invalidateGitReadCaches } from '../git-read-cache-invalidation' @@ -36,42 +35,6 @@ afterEach(async () => { await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true }))) }) -it.each(['one\ntwo\n', 'one\nchanged\n'])( - 'withholds a merge-base total for a deleted tracked file recreated as untracked until staged: %j', - async (contents) => { - const repo = await createRepo() - await writeFile(join(repo, 'new.txt'), 'one\ntwo\n') - await git(repo, 'add', 'new.txt') - await git(repo, 'commit', '-qm', 'add upstream file') - await git(repo, 'checkout', '-B', 'feature', 'base') - await git(repo, 'rm', 'new.txt') - await git(repo, 'commit', '-qm', 'delete tracked file on feature') - const compare = await getBranchCompare(repo, 'base') - const comparisonBase = compare.summary.mergeBase - if (!comparisonBase) { - throw new Error('Missing comparison base') - } - const options = { branchLineTotalMergeBase: comparisonBase } - expect((await getStatus(repo, options)).branchLineTotal).toMatchObject({ added: 0, removed: 2 }) - - await writeFile(join(repo, 'new.txt'), contents) - const unavailable = await getStatus(repo, options) - expect(unavailable.branchLineTotal).toBeUndefined() - expect(unavailable.branchLineTotalUnavailable).toBe(comparisonBase) - const reused = await getStatus(repo, { ...options, reuseLineStats: true }) - expect(reused.branchLineTotal).toBeUndefined() - expect(reused.branchLineTotalUnavailable).toBe(comparisonBase) - - await git(repo, 'add', 'new.txt') - invalidateGitReadCaches() - const expected = contents === 'one\ntwo\n' ? 0 : 1 - expect((await getStatus(repo, options)).branchLineTotal).toMatchObject({ - added: expected, - removed: expected - }) - } -) - it('detects untracked overlaps with the source of a rename, including unusual filenames', async () => { const repo = await createRepo() const oldPath = process.platform === 'win32' ? 'old name.txt' : 'old\tname.txt' diff --git a/src/main/git/source-control/status/status-cquoted-paths.test.ts b/src/main/git/source-control/status/status-cquoted-paths.test.ts deleted file mode 100644 index ac047cc18bf..00000000000 --- a/src/main/git/source-control/status/status-cquoted-paths.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { execFileSync } from 'node:child_process' -import { mkdtemp, rm, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import * as path from 'node:path' -import { afterEach, describe, expect, it } from 'vitest' -import { getStatus } from './status-read' -import { stageFile } from '../mutations/staging' - -const tempRoots: string[] = [] - -async function createRepo(): Promise { - const repo = await mkdtemp(path.join(tmpdir(), 'orca-status-cquoted-')) - tempRoots.push(repo) - execFileSync('git', ['init', '-q'], { cwd: repo }) - execFileSync('git', ['config', 'user.email', 'test@example.com'], { cwd: repo }) - execFileSync('git', ['config', 'user.name', 'Test User'], { cwd: repo }) - return repo -} - -function gitNames(repo: string, args: string[]): string[] { - const stdout = execFileSync('git', args, { cwd: repo, encoding: 'utf8' }) - return stdout.split('\0').filter(Boolean) -} - -afterEach(async () => { - await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true }))) -}) - -describe('git status C-quoted paths', () => { - it('returns the real path for untracked UTF-8 filenames', async () => { - const repo = await createRepo() - const filePath = '日本語-file.txt' - await writeFile(path.join(repo, filePath), 'new file\n') - - const status = await getStatus(repo) - - expect(status.entries).toEqual([ - { path: filePath, status: 'untracked', area: 'untracked', added: 1 } - ]) - - await stageFile(repo, status.entries[0].path) - - expect(gitNames(repo, ['diff', '--cached', '--name-only', '-z'])).toEqual([filePath]) - }) -}) diff --git a/src/main/git/source-control/status/status-line-stats-host-paths.test.ts b/src/main/git/source-control/status/status-line-stats-host-paths.test.ts index f2287dd53fa..e9be6d37fdd 100644 --- a/src/main/git/source-control/status/status-line-stats-host-paths.test.ts +++ b/src/main/git/source-control/status/status-line-stats-host-paths.test.ts @@ -4,7 +4,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type * as FsPromises from 'node:fs/promises' import type * as BoundedFileReader from '../../../../shared/file-content/node-bounded-file-reader' import type { GitStatusEntry } from '../../../../shared/git/status/git-status-types' -import { invalidateGitBranchLineTotalInFlight } from '../../../../shared/git/status/git-branch-line-total' const { lstatPaths, untrackedFiles } = vi.hoisted(() => ({ lstatPaths: [] as string[], @@ -47,30 +46,10 @@ vi.mock('../../../../shared/file-content/node-bounded-file-reader', async (impor } }) -const { gitExecFileAsyncMock } = vi.hoisted(() => ({ gitExecFileAsyncMock: vi.fn() })) - -vi.mock('../../command-runner/git-exec-file', async (importOriginal) => ({ - ...(await importOriginal>()), - gitExecFileAsync: gitExecFileAsyncMock, - gitExecFileAsyncBuffer: vi.fn() -})) - -vi.mock('../../command-runner/git-environment/git-process-env', async (importOriginal) => ({ - ...(await importOriginal>()), - gitOptionalLocksDisabledEnv: (env: NodeJS.ProcessEnv = process.env) => env -})) - -vi.mock('../../command-runner/git-stream-stdout', async (importOriginal) => ({ - ...(await importOriginal>()), - gitStreamStdout: vi.fn() -})) - import { attachLineStats } from './status-line-stats' -import { createBranchLineTotalInput } from './status-branch-line-total-input' const GUEST_WORKTREE = '/home/me/repo' const UNC_WORKTREE = String.raw`\\wsl.localhost\Ubuntu\home\me\repo` -const MERGE_BASE = 'a'.repeat(40) function untrackedEntry(entryPath: string): GitStatusEntry { return { path: entryPath, area: 'untracked', status: 'added' } @@ -80,9 +59,6 @@ describe('untracked line stats on a WSL worktree read by a Windows host', () => beforeEach(() => { lstatPaths.length = 0 untrackedFiles.clear() - gitExecFileAsyncMock.mockReset() - gitExecFileAsyncMock.mockResolvedValue({ stdout: '', stderr: '' }) - invalidateGitBranchLineTotalInFlight() vi.spyOn(process, 'platform', 'get').mockReturnValue('win32') }) @@ -90,21 +66,6 @@ describe('untracked line stats on a WSL worktree read by a Windows host', () => vi.restoreAllMocks() }) - it('invalidates an overlapping untracked branch total through the WSL filesystem spelling', async () => { - const target = path.join(UNC_WORKTREE, 'fresh.txt') - untrackedFiles.set(target, 'one\ntwo\n') - gitExecFileAsyncMock.mockResolvedValue({ stdout: '0\t2\tfresh.txt\0', stderr: '' }) - const input = createBranchLineTotalInput( - GUEST_WORKTREE, - [untrackedEntry('fresh.txt')], - { wslDistro: 'Ubuntu', branchLineTotalMergeBase: MERGE_BASE }, - true - ) - - expect(await input?.compute()).toBeNull() - expect(lstatPaths).toEqual([target]) - }) - it('re-spells the guest form only, leaving every host-openable spelling byte-identical', async () => { const cases: { worktree: string; expected: string }[] = [ { worktree: GUEST_WORKTREE, expected: UNC_WORKTREE }, @@ -113,7 +74,6 @@ describe('untracked line stats on a WSL worktree read by a Windows host', () => worktree: '//wsl.localhost/Ubuntu/home/me/repo', expected: '//wsl.localhost/Ubuntu/home/me/repo' }, - { worktree: '//wsl$/Ubuntu/home/me/repo', expected: '//wsl$/Ubuntu/home/me/repo' }, { worktree: '/home/me/my repo ', expected: '/home/me/my repo ' } ] @@ -126,7 +86,6 @@ describe('untracked line stats on a WSL worktree read by a Windows host', () => await attachLineStats(worktree, entries, { wslDistro: 'Ubuntu' }) - expect(lstatPaths, worktree).toEqual([target]) expect(entries[0].added, worktree).toBe(2) } }) diff --git a/src/main/git/source-control/status/status-line-stats-renames-real-git.test.ts b/src/main/git/source-control/status/status-line-stats-renames-real-git.test.ts index 578f7b29c7c..358d77ceac3 100644 --- a/src/main/git/source-control/status/status-line-stats-renames-real-git.test.ts +++ b/src/main/git/source-control/status/status-line-stats-renames-real-git.test.ts @@ -38,8 +38,6 @@ afterEach(async () => { describe.each(['staged', 'unstaged'] as const)('%s rename line counts', (area) => { it.each([ - { diffRenames: 'true', statusRenames: undefined, renamed: true }, - { diffRenames: 'false', statusRenames: undefined, renamed: false }, { diffRenames: 'true', statusRenames: 'false', renamed: false }, { diffRenames: 'false', statusRenames: 'true', renamed: true } ])('matches status with $diffRenames diff / $statusRenames status', async (config) => { diff --git a/src/main/git/source-control/status/status-shared-symlinks.test.ts b/src/main/git/source-control/status/status-shared-symlinks.test.ts index 6bf5283d84d..14644afadd8 100644 --- a/src/main/git/source-control/status/status-shared-symlinks.test.ts +++ b/src/main/git/source-control/status/status-shared-symlinks.test.ts @@ -37,29 +37,6 @@ describe('getStatus shared symlink exclusion', () => { rmSync(root, { recursive: true, force: true }) }) - it('Git reports the shared symlink as untracked, without a trailing slash', async () => { - const status = await getStatus(worktree) - - expect(status.entries).toEqual([ - expect.objectContaining({ path: 'node_modules', area: 'untracked' }) - ]) - }) - - it('drops the shared symlink when it is declared shared', async () => { - const status = await getStatus(worktree, { sharedLinkPaths: ['node_modules'] }) - - expect(status.entries).toEqual([]) - }) - - it.each(['./node_modules', 'node_modules/'])( - 'drops the shared symlink when the setting spells it %s', - async (sharedPath) => { - const status = await getStatus(worktree, { sharedLinkPaths: [sharedPath] }) - - expect(status.entries).toEqual([]) - } - ) - it('still reports a genuine untracked file alongside a shared symlink', async () => { writeFileSync(join(worktree, 'scratch.txt'), 'unsaved work\n') @@ -70,29 +47,6 @@ describe('getStatus shared symlink exclusion', () => { ]) }) - it('still reports a modified tracked file alongside a shared symlink', async () => { - writeFileSync(join(worktree, 'README.md'), '# edited\n') - - const status = await getStatus(worktree, { sharedLinkPaths: ['node_modules'] }) - - expect(status.entries).toEqual([ - expect.objectContaining({ path: 'README.md', area: 'unstaged' }) - ]) - }) - - it('keeps a regular directory the user created at a configured shared name', async () => { - mkdirSync(join(worktree, 'vendor')) - writeFileSync(join(worktree, 'vendor', 'real.txt'), 'user work\n') - - const status = await getStatus(worktree, { - sharedLinkPaths: ['node_modules', 'vendor'] - }) - - expect(status.entries).toEqual([ - expect.objectContaining({ path: 'vendor/real.txt', area: 'untracked' }) - ]) - }) - it('keeps a regular file the user created at a configured shared name', async () => { writeFileSync(join(worktree, 'notes'), 'user work\n') @@ -116,17 +70,6 @@ describe('getStatus shared symlink exclusion', () => { ]) }) - it('drops shared symlinks whose names have a space or non-ASCII characters', async () => { - const names = ['my shared dir', 'ライブラリ'] - for (const name of names) { - symlinkSync(join(primary, 'node_modules'), join(worktree, name), 'dir') - } - - const status = await getStatus(worktree, { sharedLinkPaths: ['node_modules', ...names] }) - - expect(status.entries).toEqual([]) - }) - it('keeps a symlink at a path that was never declared shared', async () => { symlinkSync(join(primary, 'node_modules'), join(worktree, 'vendor'), 'dir') From 1704173140ed36c7ac091d3ada75742748fea817 Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 10:34:29 +0000 Subject: [PATCH 072/178] fix(browser): don't offer to open a streamed page's server loopback URL on this machine (#41967) The streamed pane's context menu opened the page or link URL as-is in the desktop's default browser, so a server's localhost page opened this machine's localhost. Use resolveRemoteFailureExternalUrl, as the streamed error page already does, and leave the items out when it refuses. --- .../remote-browser-page-context-menu.tsx | 69 ++++++++++--------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-context-menu.tsx b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-context-menu.tsx index 0c35b2407ea..ee5b265cdcb 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-context-menu.tsx +++ b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-context-menu.tsx @@ -3,8 +3,8 @@ import { createPortal } from 'react-dom' import { callRuntimeRpc } from '@/runtime/rpc/calls/runtime-rpc-call' import { useTranslate } from '@/i18n/use-translate' import { - normalizeExternalBrowserUrl, - redactKagiSessionToken + redactKagiSessionToken, + resolveRemoteFailureExternalUrl } from '../../../../../shared/browser-url' import { isRemoteBrowserPageMissingError } from './remote-browser-stream-errors' import type { RemoteBrowserStreamLifecycle } from './remote-browser-stream-lifecycle' @@ -171,6 +171,12 @@ export function RemoteBrowserPageContextMenu({ el.style.top = `${Math.max(0, renderY) + offsetY}px` }, [contextMenu]) + // The server's loopback address would open this machine's, not the page's. + const linkExternalUrl = contextMenu.linkUrl + ? resolveRemoteFailureExternalUrl(contextMenu.linkUrl) + : null + const pageExternalUrl = resolveRemoteFailureExternalUrl(contextMenu.pageUrl) + return createPortal( <>
@@ -193,22 +199,21 @@ export function RemoteBrowserPageContextMenu({ 'Open Link In Orca Browser' )} - + {linkExternalUrl ? ( + + ) : null} + {pageExternalUrl ? ( + + ) : null} +` + +const CALLBACK_HTML = `Callback` + +async function startFixture(): Promise<{ server: Server; origin: string }> { + const server = createServer((request, response) => { + const path = new URL(request.url ?? '/', 'http://127.0.0.1').pathname + response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }) + if (path === '/opener') { + response.end(OPENER_HTML) + } else if (path === '/authorize') { + response.end(``) + } else if (path === '/callback') { + response.end(CALLBACK_HTML) + } else { + response.end('Destination') + } + }) + await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)) + const address = server.address() + if (!address || typeof address === 'string') { + throw new Error('fixture server has no port') + } + return { server, origin: `http://127.0.0.1:${address.port}` } +} + +function readPagePlacements(page: Page, worktreeId: string): Promise> { + return page.evaluate((worktreeId) => { + const state = window.__store!.getState() + const placements: Record = {} + for (const workspace of state.browserTabsByWorktree[worktreeId] ?? []) { + for (const browserPage of state.browserPagesByWorkspace[workspace.id] ?? []) { + const kind = state.remoteBrowserPageHandlesByPageId[browserPage.id]?.placement?.kind + placements[new URL(browserPage.url).pathname] = kind ?? 'none' + } + } + return placements + }, worktreeId) +} + +async function showPage(page: Page, worktreeId: string, path: string): Promise { + await page.evaluate( + ({ worktreeId, path }) => { + const state = window.__store!.getState() + for (const workspace of state.browserTabsByWorktree[worktreeId] ?? []) { + for (const browserPage of state.browserPagesByWorkspace[workspace.id] ?? []) { + if (new URL(browserPage.url).pathname === path) { + state.focusBrowserTabInWorktree(worktreeId, browserPage.id, { surfacePane: true }) + } + } + } + }, + { worktreeId, path } + ) +} + +function guestEval(client: PairedElectronClient, url: string, code: string): Promise { + return client.app.evaluate( + ({ webContents }, { url, code }) => + webContents + .getAllWebContents() + .find((contents) => contents.getType() === 'webview' && contents.getURL() === url) + ?.executeJavaScript(code) ?? null, + { url, code } + ) +} + +// A real mouse click, so main sees the user's gesture as it would on a desktop. +async function clickInGuest( + client: PairedElectronClient, + url: string, + selector: string +): Promise { + await expect + .poll(() => guestEval(client, url, 'innerWidth > 320 && innerHeight > 200'), { + timeout: 30_000 + }) + .toBe(true) + await client.app.evaluate( + async ({ webContents }, { url, selector }) => { + const guest = webContents + .getAllWebContents() + .find((contents) => contents.getType() === 'webview' && contents.getURL() === url)! + const { x, y }: { x: number; y: number } = await guest.executeJavaScript( + `(() => { const r = document.querySelector(${JSON.stringify(selector)}).getBoundingClientRect(); return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) } })()` + ) + guest.sendInputEvent({ type: 'mouseDown', x, y, button: 'left', clickCount: 1 }) + guest.sendInputEvent({ type: 'mouseUp', x, y, button: 'left', clickCount: 1 }) + }, + { url, selector } + ) +} + +test('a This-device page opens new tabs on this device and keeps OAuth popups working', async ({ + testRepoPath +}, testInfo) => { + // Boots a headless host and a paired client, then waits on two tab creations and a popup round trip. + test.setTimeout(300_000) + const { server, origin } = await startFixture() + const host = await launchHeadlessPairedRuntimeHost() + let client: PairedElectronClient | null = null + try { + await host.client.call('repo.add', { path: testRepoPath, kind: 'git' }) + client = await launchPairedElectronClient(host.offer, testInfo, 'This-device popups') + const page = client.page + const worktreeId = await waitForPairedClientWorktree(page) + await page.evaluate( + ({ environmentId, worktreeId }) => + window.__store!.getState().setActiveWorktree(worktreeId, `runtime:${environmentId}`), + { environmentId: client.environmentId, worktreeId } + ) + const openerUrl = `${origin}/opener` + await page.evaluate(async (url) => { + const state = window.__store!.getState() + state.setBrowserDefaultUrl(url) + await state.openNewBrowserTabInActiveWorkspace( + state.activeGroupIdByWorktree[state.activeWorktreeId!]! + ) + }, openerUrl) + await expect + .poll(() => readPagePlacements(page, worktreeId), { timeout: 60_000 }) + .toEqual({ '/opener': 'client' }) + await expect + .poll(() => guestEval(client!, openerUrl, 'document.readyState'), { timeout: 60_000 }) + .toBe('complete') + + await showPage(page, worktreeId, '/opener') + await clickInGuest(client, openerUrl, '#blank') + await expect + .poll(() => readPagePlacements(page, worktreeId), { + timeout: 30_000, + message: 'the target=_blank link did not open a This-device tab' + }) + .toEqual({ '/opener': 'client', '/destination': 'client' }) + await expect + .poll(() => readHostBrowserPageUrls(host.client, `path:${testRepoPath}`), { timeout: 30_000 }) + .toContain(`${origin}/destination`) + + await showPage(page, worktreeId, '/opener') + await clickInGuest(client, openerUrl, '#oauth') + await expect + .poll(() => guestEval(client!, openerUrl, 'window.__oauthMessages.join()'), { + timeout: 30_000, + message: 'the OAuth popup never reached its opener' + }) + .toBe('code=abc') + await expect + .poll(() => guestEval(client!, openerUrl, 'window.__oauthPopup.closed'), { timeout: 10_000 }) + .toBe(true) + } finally { + await client?.dispose() + await host.dispose() + server.close() + } +}) From 52b48ebc0982e31b1737e863592954e0f3ec1a6f Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 10:50:14 +0000 Subject: [PATCH 075/178] fix(browser): carry large This-device results and typed errors back to the server (#41971) --- .../browser-host-command-result-staging.ts | 80 ++++++++++++++ .../browser-host-command-result-submission.ts | 34 +++--- .../paired-runtime-browser-host-lease.ts | 5 +- .../paired-runtime-browser-client-host.ts | 29 ++++- .../browser-client-page-command-failure.ts | 15 ++- .../browser-host-command-ledger.test.ts | 39 +++++++ .../commands/browser-host-command-ledger.ts | 15 ++- .../browser-host-command-result-staging.ts | 101 ++++++++++++++++++ .../commands/browser-host-command-state.ts | 10 +- .../leases/browser-host-lease-registry.ts | 7 +- .../runtime-browser-client-automation.ts | 6 +- .../methods/browser/browser-client-host.ts | 76 ++++++++----- .../startup/main-process-ready-runtime.ts | 3 +- .../browser-client-automation-protocol.ts | 31 +++++- .../browser-client-host-protocol.ts | 15 +++ .../rpc-params-catalog.generated.ts | 2 + 16 files changed, 411 insertions(+), 57 deletions(-) create mode 100644 src/main/browser/client-hosts/commands/browser-host-command-result-staging.ts create mode 100644 src/main/runtime/browser-host/commands/browser-host-command-result-staging.ts diff --git a/src/main/browser/client-hosts/commands/browser-host-command-result-staging.ts b/src/main/browser/client-hosts/commands/browser-host-command-result-staging.ts new file mode 100644 index 00000000000..b13539501c4 --- /dev/null +++ b/src/main/browser/client-hosts/commands/browser-host-command-result-staging.ts @@ -0,0 +1,80 @@ +import type { BrowserClientHostCommandEvent } from '../../../../shared/browser/client-hosting/browser-client-host-protocol' +import { + BROWSER_CLIENT_AUTOMATION_RESULT_CHUNK_MAX_BYTES, + BROWSER_CLIENT_AUTOMATION_RESULT_MAX_BYTES, + BROWSER_CLIENT_AUTOMATION_STAGED_RESULT_MAX_BYTES, + truncateBrowserClientAutomationErrorMessage, + type BrowserClientAutomationResult as BrowserClientHostCommandResult +} from '../../../../shared/browser/client-hosting/browser-client-automation-protocol' +import { RemoteRuntimeClientError } from '../../../../shared/remote-runtime/remote-runtime-client-error' +import { browserHostCommandResultIdentity } from './browser-host-command-result-submission' + +const BROWSER_HOST_COMMAND_RESULT_TOO_LARGE_CODE = 'browser_host_command_result_too_large' + +/** Sends an automation value too big for one result frame to the host ahead of its settlement. */ +export async function stageBrowserHostCommandResult( + command: BrowserClientHostCommandEvent, + result: BrowserClientHostCommandResult, + sendChunk: (params: unknown) => Promise, + signal: AbortSignal +): Promise { + if ( + result.status !== 'completed' || + result.value === undefined || + command.command.type !== 'automation' + ) { + return result + } + const bytes = Buffer.from(JSON.stringify(result.value), 'utf8') + const hostStages = command.command.stagedResults === true + if ( + bytes.byteLength <= BROWSER_CLIENT_AUTOMATION_RESULT_CHUNK_MAX_BYTES || + (!hostStages && bytes.byteLength <= BROWSER_CLIENT_AUTOMATION_RESULT_MAX_BYTES) + ) { + return result + } + if (!hostStages || bytes.byteLength > BROWSER_CLIENT_AUTOMATION_STAGED_RESULT_MAX_BYTES) { + const limit = hostStages + ? BROWSER_CLIENT_AUTOMATION_STAGED_RESULT_MAX_BYTES + : BROWSER_CLIENT_AUTOMATION_RESULT_MAX_BYTES + return failed( + BROWSER_HOST_COMMAND_RESULT_TOO_LARGE_CODE, + `The result (${formatMegabytes(bytes.byteLength)}) is larger than the ${formatMegabytes(limit)} this server accepts from a page on this device. Use --format jpeg, a selector, or a smaller viewport${hostStages ? '' : ', or update the server'}.` + ) + } + const identity = browserHostCommandResultIdentity(command) + try { + for ( + let offset = 0; + offset < bytes.byteLength; + offset += BROWSER_CLIENT_AUTOMATION_RESULT_CHUNK_MAX_BYTES + ) { + if (signal.aborted) { + return failed('browser_client_page_command_aborted') + } + const chunk = bytes.subarray( + offset, + offset + BROWSER_CLIENT_AUTOMATION_RESULT_CHUNK_MAX_BYTES + ) + await sendChunk({ ...identity, offset, contentBase64: chunk.toString('base64') }) + } + } catch (error) { + // Keep the host or transport code (e.g. too large, unavailable) so the agent sees why. + return failed( + error instanceof RemoteRuntimeClientError + ? error.code + : 'browser_host_command_result_staging_failed', + error instanceof Error ? error.message : String(error) + ) + } + return { status: 'completed', stagedValueBytes: bytes.byteLength } +} + +function failed(errorCode: string, message?: string): BrowserClientHostCommandResult { + const errorMessage = message ? truncateBrowserClientAutomationErrorMessage(message) : undefined + return { status: 'failed', errorCode, ...(errorMessage ? { errorMessage } : {}) } +} + +function formatMegabytes(bytes: number): string { + return `${(bytes / (1024 * 1024)).toFixed(1)} MB` +} diff --git a/src/main/browser/client-hosts/commands/browser-host-command-result-submission.ts b/src/main/browser/client-hosts/commands/browser-host-command-result-submission.ts index 3364bba5f94..a75d8cfe90b 100644 --- a/src/main/browser/client-hosts/commands/browser-host-command-result-submission.ts +++ b/src/main/browser/client-hosts/commands/browser-host-command-result-submission.ts @@ -18,21 +18,7 @@ export async function submitBrowserHostCommandResult( const result = BrowserClientHostCommandResult.parse(candidate) const response = await sendRequest( 'browser.clientHost.commandResult', - { - pageCommandProtocolVersion: command.pageCommandProtocolVersion, - ...(command.pageReconciliationProtocolVersion - ? { pageReconciliationProtocolVersion: command.pageReconciliationProtocolVersion } - : {}), - authorityRuntimeId: command.authorityRuntimeId, - authorityEpoch: command.authorityEpoch, - browserHostClientId: command.browserHostClientId, - browserHostGeneration: command.browserHostGeneration, - browserPageId: command.browserPageId, - pageHostGeneration: command.pageHostGeneration, - commandSequence: command.commandSequence, - commandId: command.commandId, - result - }, + { ...browserHostCommandResultIdentity(command), result }, timeoutMs ) if (!response.ok) { @@ -45,3 +31,21 @@ export async function submitBrowserHostCommandResult( throw new Error('Invalid browser host command result acknowledgement') } } + +/** The command identity a result, or a chunk of one, is filed under. */ +export function browserHostCommandResultIdentity(command: BrowserClientHostCommandEvent) { + return { + pageCommandProtocolVersion: command.pageCommandProtocolVersion, + ...(command.pageReconciliationProtocolVersion + ? { pageReconciliationProtocolVersion: command.pageReconciliationProtocolVersion } + : {}), + authorityRuntimeId: command.authorityRuntimeId, + authorityEpoch: command.authorityEpoch, + browserHostClientId: command.browserHostClientId, + browserHostGeneration: command.browserHostGeneration, + browserPageId: command.browserPageId, + pageHostGeneration: command.pageHostGeneration, + commandSequence: command.commandSequence, + commandId: command.commandId + } +} diff --git a/src/main/browser/client-hosts/leases/paired-runtime-browser-host-lease.ts b/src/main/browser/client-hosts/leases/paired-runtime-browser-host-lease.ts index 4d8dd54a953..852cc235165 100644 --- a/src/main/browser/client-hosts/leases/paired-runtime-browser-host-lease.ts +++ b/src/main/browser/client-hosts/leases/paired-runtime-browser-host-lease.ts @@ -6,7 +6,6 @@ import { BrowserClientAutomationResult as BrowserClientHostCommandResult, type BrowserClientAutomationResult as BrowserClientHostCommandResultType } from '../../../../shared/browser/client-hosting/browser-client-automation-protocol' -import { BROWSER_CLIENT_PAGE_METADATA_METHOD } from '../../../../shared/browser/client-hosting/browser-client-page-metadata-protocol' import { assertBrowserClientHostAttachOptions } from '../lifecycle/browser-client-host-attach-request' import { BrowserHostReconnectDelay, @@ -79,12 +78,12 @@ export class PairedRuntimeBrowserHostLease { } /** Unversioned, unlike the file channel: an older runtime answers with a reportable error. */ - sendPageMetadataRequest(params: unknown, timeoutMs: number) { + sendHostRequest(method: string, params: unknown, timeoutMs: number) { // close drops the connection synchronously. return requireBrowserHostLeaseSendRequest( this.connection?.sendRequest, 'Remote runtime browser host lease is unavailable.' - )(BROWSER_CLIENT_PAGE_METADATA_METHOD, params, timeoutMs) + )(method, params, timeoutMs) } sendFileChannelRequest(method: string, params: unknown, timeoutMs: number) { diff --git a/src/main/browser/client-hosts/lifecycle/paired-runtime-browser-client-host.ts b/src/main/browser/client-hosts/lifecycle/paired-runtime-browser-client-host.ts index d26053a63dc..937f2045a25 100644 --- a/src/main/browser/client-hosts/lifecycle/paired-runtime-browser-client-host.ts +++ b/src/main/browser/client-hosts/lifecycle/paired-runtime-browser-client-host.ts @@ -3,7 +3,12 @@ import type { BrowserClientHostCommandEvent, BrowserClientHostLeaseAuthority } from '../../../../shared/browser/client-hosting/browser-client-host-protocol' -import type { BrowserClientAutomationResult as BrowserClientHostCommandResult } from '../../../../shared/browser/client-hosting/browser-client-automation-protocol' +import { + BROWSER_CLIENT_HOST_COMMAND_RESULT_CHUNK_METHOD, + type BrowserClientAutomationResult as BrowserClientHostCommandResult +} from '../../../../shared/browser/client-hosting/browser-client-automation-protocol' +import { BROWSER_CLIENT_PAGE_METADATA_METHOD } from '../../../../shared/browser/client-hosting/browser-client-page-metadata-protocol' +import { RemoteRuntimeClientError } from '../../../../shared/remote-runtime/remote-runtime-client-error' import type { PairedRuntimeConnection } from '../../../../shared/remote-runtime/pairing/paired-runtime-connection' import type { RemoteRuntimeSubscriptionOptions } from '../../../../shared/remote-runtime/requests/remote-runtime-subscription-contract' import type { RuntimeRpcResponse } from '../../../../shared/runtime-rpc-envelope' @@ -14,6 +19,7 @@ import type { DispatcherOptions } from '../commands/browser-client-host-command-state' import { PairedRuntimeBrowserHostLease } from '../leases/paired-runtime-browser-host-lease' +import { stageBrowserHostCommandResult } from '../commands/browser-host-command-result-staging' type DispatcherLimits = Omit @@ -124,7 +130,7 @@ export class PairedRuntimeBrowserClientHost { params: unknown, timeoutMs: number ): Promise> { - return this.lease.sendPageMetadataRequest(params, timeoutMs) + return this.lease.sendHostRequest(BROWSER_CLIENT_PAGE_METADATA_METHOD, params, timeoutMs) } refreshPageInventory(): Promise { @@ -150,11 +156,28 @@ export class PairedRuntimeBrowserClientHost { this.options.onAuthority?.(authority) this.dispatcher = new BrowserClientHostCommandDispatcher({ authority, - handler: this.options.handler, + handler: async (command, signal) => + stageBrowserHostCommandResult( + command, + await this.options.handler(command, signal), + (params) => this.sendCommandResultChunk(params), + signal + ), ...this.options.dispatcher }) } + private async sendCommandResultChunk(params: unknown): Promise { + const response = await this.lease.sendHostRequest( + BROWSER_CLIENT_HOST_COMMAND_RESULT_CHUNK_METHOD, + params, + this.options.timeoutMs ?? 15_000 + ) + if (!response.ok) { + throw new RemoteRuntimeClientError(response.error.code, response.error.message) + } + } + private dispatch( command: BrowserClientHostCommandEvent ): Promise { diff --git a/src/main/browser/client-pages/execution/browser-client-page-command-failure.ts b/src/main/browser/client-pages/execution/browser-client-page-command-failure.ts index 1d73355598f..e168e9f75d0 100644 --- a/src/main/browser/client-pages/execution/browser-client-page-command-failure.ts +++ b/src/main/browser/client-pages/execution/browser-client-page-command-failure.ts @@ -1,15 +1,21 @@ -import type { BrowserClientAutomationResult as BrowserClientHostCommandResult } from '../../../../shared/browser/client-hosting/browser-client-automation-protocol' +import { + truncateBrowserClientAutomationErrorMessage, + type BrowserClientAutomationResult as BrowserClientHostCommandResult +} from '../../../../shared/browser/client-hosting/browser-client-automation-protocol' import type { BrowserExpectedUrlMismatch } from '../../../../shared/browser/client-hosting/browser-expected-url' export class BrowserClientPageCommandError extends Error { readonly expectedUrlMismatch?: BrowserExpectedUrlMismatch + /** The browser command's own message, so the host can show it with the code. */ + readonly detail?: string constructor( message: string, - options?: ErrorOptions & { expectedUrlMismatch?: BrowserExpectedUrlMismatch } + options?: ErrorOptions & { expectedUrlMismatch?: BrowserExpectedUrlMismatch; detail?: string } ) { super(message, options) this.expectedUrlMismatch = options?.expectedUrlMismatch + this.detail = options?.detail } } @@ -28,9 +34,14 @@ export function browserClientPageCommandFailure( ): BrowserClientHostCommandResult { const mismatch = error instanceof BrowserClientPageCommandError ? error.expectedUrlMismatch : undefined + const errorMessage = + error instanceof BrowserClientPageCommandError && error.detail + ? truncateBrowserClientAutomationErrorMessage(error.detail) + : undefined return { status: 'failed', errorCode: browserClientPageCommandFailureCode(error, signal), + ...(errorMessage ? { errorMessage } : {}), ...(mismatch ? { expectedUrlMismatch: mismatch } : {}) } } diff --git a/src/main/runtime/browser-host/commands/browser-host-command-ledger.test.ts b/src/main/runtime/browser-host/commands/browser-host-command-ledger.test.ts index 15a5e0933b9..1a7553cce37 100644 --- a/src/main/runtime/browser-host/commands/browser-host-command-ledger.test.ts +++ b/src/main/runtime/browser-host/commands/browser-host-command-ledger.test.ts @@ -31,6 +31,45 @@ describe('BrowserHostCommandLedger', () => { 'browser_host_command_result_page_stale' ) }) + + it('settles a chunked automation result as the value its chunks carried', async () => { + const ledger = new BrowserHostCommandLedger({ authority }) + ledger.attach(vi.fn()) + const created = ledger.issue({ + browserPageId: 'page-a', + pageHostGeneration: 1, + command: { type: 'createPage', browserProfileId: 'default', executionHostKey: 'host-key-a' } + }) + ledger.settle(resultParams(created.event, { status: 'completed' })) + const screenshot = ledger.issue({ + browserPageId: 'page-a', + pageHostGeneration: 1, + command: { + type: 'automation', + method: 'browser.screenshot', + params: {}, + stagedResults: true + } + }) + const { result: _result, ...identity } = resultParams(screenshot.event, { status: 'completed' }) + const json = Buffer.from('{"data":"iVBORw0KGgo=","format":"png"}') + ledger.stageResultChunk({ + ...identity, + offset: 0, + contentBase64: json.subarray(0, 20).toString('base64') + }) + ledger.stageResultChunk({ + ...identity, + offset: 20, + contentBase64: json.subarray(20).toString('base64') + }) + ledger.settle({ ...identity, result: { status: 'completed', stagedValueBytes: 38 } }) + + await expect(screenshot.result).resolves.toEqual({ + status: 'completed', + value: { data: 'iVBORw0KGgo=', format: 'png' } + }) + }) }) function resultParams( diff --git a/src/main/runtime/browser-host/commands/browser-host-command-ledger.ts b/src/main/runtime/browser-host/commands/browser-host-command-ledger.ts index 264abe87859..1c24827be15 100644 --- a/src/main/runtime/browser-host/commands/browser-host-command-ledger.ts +++ b/src/main/runtime/browser-host/commands/browser-host-command-ledger.ts @@ -15,6 +15,7 @@ import { type BrowserHostCommandInput, type BrowserHostCommandLedgerOptions, type BrowserHostCommandPageState, + type BrowserHostCommandResultChunkParams, type BrowserHostCommandResultParams, positiveBrowserHostCommandLimit, recordBrowserHostCommandOrder, @@ -29,6 +30,7 @@ import { replaySettledBrowserHostCommand } from './browser-host-command-result-replay' import { BrowserHostCommandResultCache } from './browser-host-command-result-cache' +import { BrowserHostCommandResultStaging } from './browser-host-command-result-staging' export class BrowserHostCommandLedger { private readonly authority: BrowserClientHostLeaseAuthority @@ -38,6 +40,7 @@ export class BrowserHostCommandLedger { private readonly maxPages: number private readonly pages = new Map() private readonly resultCache: BrowserHostCommandResultCache + private readonly staging = new BrowserHostCommandResultStaging() private delivery: ((event: BrowserClientHostCommandEvent) => void) | undefined private outstandingCommands = 0 private activePages = 0 @@ -157,6 +160,14 @@ export class BrowserHostCommandLedger { return hasOutstandingBrowserHostReconciliation(this.pages) } + stageResultChunk(params: BrowserHostCommandResultChunkParams): void { + if (this.closed) { + throw new Error('browser_host_command_ledger_closed') + } + assertBrowserHostCommandResultAuthority(this.authority, params) + this.staging.append(this.pages.get(params.browserPageId), params) + } + settle(params: BrowserHostCommandResultParams): boolean { if (this.closed) { throw new Error('browser_host_command_ledger_closed') @@ -186,7 +197,7 @@ export class BrowserHostCommandLedger { if (!record || record.event.commandId !== params.commandId || record.settled) { throw new Error('browser_host_command_result_conflict') } - const result = Object.freeze({ ...params.result }) + const result = Object.freeze({ ...this.staging.take(record, params.result) }) record.settled = result record.resolve(result) page.nextSettlementSequence += 1 @@ -230,6 +241,7 @@ export class BrowserHostCommandLedger { } this.pages.clear() this.resultCache.clear() + this.staging.clear() this.outstandingCommands = 0 this.activePages = 0 } @@ -243,6 +255,7 @@ export class BrowserHostCommandLedger { throw new Error('browser_host_command_page_stale') } for (const record of page.records.values()) { + this.staging.release(record) if (!record.settled) { record.reject(new Error('browser_host_command_outcome_unknown')) } diff --git a/src/main/runtime/browser-host/commands/browser-host-command-result-staging.ts b/src/main/runtime/browser-host/commands/browser-host-command-result-staging.ts new file mode 100644 index 00000000000..810fb5e2f9b --- /dev/null +++ b/src/main/runtime/browser-host/commands/browser-host-command-result-staging.ts @@ -0,0 +1,101 @@ +import { + BROWSER_CLIENT_AUTOMATION_STAGED_RESULT_MAX_BYTES, + type BrowserClientAutomationResult as BrowserClientHostCommandResult +} from '../../../../shared/browser/client-hosting/browser-client-automation-protocol' +import { BrowserError } from '../../../browser/browser-error' +import type { + BrowserHostCommandPageState, + BrowserHostCommandRecord, + BrowserHostCommandResultChunkParams as ChunkParams +} from './browser-host-command-state' + +// Several pages can stage at once; bound the lease's total, not just each result. +const MAX_STAGED_BYTES_PER_LEASE = 2 * BROWSER_CLIENT_AUTOMATION_STAGED_RESULT_MAX_BYTES + +type StagedResult = { chunks: Buffer[]; bytes: number } + +/** Holds chunked automation results until the desktop settles their command. */ +export class BrowserHostCommandResultStaging { + private readonly staged = new Map() + private bytes = 0 + + append( + page: BrowserHostCommandPageState | undefined, + { pageHostGeneration, commandSequence, commandId, offset, contentBase64 }: ChunkParams + ): void { + const record = + page?.generation === pageHostGeneration ? page.records.get(commandSequence) : undefined + if ( + !record || + record.event.commandId !== commandId || + record.settled || + record.event.command.type !== 'automation' || + record.event.command.stagedResults !== true + ) { + throw new Error('browser_host_command_result_chunk_conflict') + } + if (offset === 0) { + this.release(record) + } + const staged = this.staged.get(record) ?? { chunks: [], bytes: 0 } + if (offset !== staged.bytes) { + this.release(record) + throw new Error('browser_host_command_result_chunk_out_of_order') + } + const chunk = Buffer.from(contentBase64, 'base64') + if (chunk.byteLength === 0) { + this.release(record) + throw new Error('browser_host_command_result_chunk_invalid') + } + if ( + staged.bytes + chunk.byteLength > BROWSER_CLIENT_AUTOMATION_STAGED_RESULT_MAX_BYTES || + this.bytes + chunk.byteLength > MAX_STAGED_BYTES_PER_LEASE + ) { + this.release(record) + throw new BrowserError( + 'browser_host_command_result_too_large', + 'The server is already holding too many large browser results; retry, or use --format jpeg, a selector, or a smaller viewport.' + ) + } + staged.chunks.push(chunk) + staged.bytes += chunk.byteLength + this.bytes += chunk.byteLength + this.staged.set(record, staged) + } + + /** Swaps a staged marker for the value its chunks carried, and frees them either way. */ + take( + record: BrowserHostCommandRecord, + result: BrowserClientHostCommandResult + ): BrowserClientHostCommandResult { + const staged = this.staged.get(record) + this.release(record) + if (result.status !== 'completed' || result.stagedValueBytes === undefined) { + return result + } + if (staged?.bytes !== result.stagedValueBytes) { + return { status: 'failed', errorCode: 'browser_host_command_result_incomplete' } + } + try { + return { + status: 'completed', + value: JSON.parse(Buffer.concat(staged.chunks).toString('utf8')) + } + } catch { + return { status: 'failed', errorCode: 'browser_host_command_result_invalid' } + } + } + + release(record: BrowserHostCommandRecord): void { + const staged = this.staged.get(record) + if (staged) { + this.bytes -= staged.bytes + this.staged.delete(record) + } + } + + clear(): void { + this.staged.clear() + this.bytes = 0 + } +} diff --git a/src/main/runtime/browser-host/commands/browser-host-command-state.ts b/src/main/runtime/browser-host/commands/browser-host-command-state.ts index c29f9179166..d190f05e3f2 100644 --- a/src/main/runtime/browser-host/commands/browser-host-command-state.ts +++ b/src/main/runtime/browser-host/commands/browser-host-command-state.ts @@ -30,6 +30,14 @@ export type BrowserHostCommandResultParams = Omit< result: BrowserClientHostCommandResult } +export type BrowserHostCommandResultChunkParams = Omit< + BrowserClientHostCommandEvent, + 'type' | 'command' +> & { + offset: number + contentBase64: string +} + export type BrowserHostCommandRecord = { event: BrowserClientHostCommandEvent resultAdmission: BrowserHostCommandResultAdmission @@ -163,7 +171,7 @@ export function sameBrowserHostCommandResult( export function assertBrowserHostCommandResultAuthority( authority: BrowserClientHostLeaseAuthority, - params: BrowserHostCommandResultParams + params: Omit ): void { if ( params.pageCommandProtocolVersion !== authority.pageCommandProtocolVersion || diff --git a/src/main/runtime/browser-host/leases/browser-host-lease-registry.ts b/src/main/runtime/browser-host/leases/browser-host-lease-registry.ts index a8d5ebd1723..e1373a6ce3f 100644 --- a/src/main/runtime/browser-host/leases/browser-host-lease-registry.ts +++ b/src/main/runtime/browser-host/leases/browser-host-lease-registry.ts @@ -263,14 +263,17 @@ export class BrowserHostLeaseRegistry { identity: BrowserHostCommandResultIdentity, params: BrowserHostCommandResultParams ): boolean { - const state = this.requireLeaseState(identity) - const ledger = requireBrowserHostCommandResultLedger(state, identity) + const ledger = this.requireCommandResultLedger(identity) if (!ledger.isUnplacedPageResult(params)) { this.requireClientPage(params) } return ledger.settle(params) } + requireCommandResultLedger(identity: BrowserHostCommandResultIdentity) { + return requireBrowserHostCommandResultLedger(this.requireLeaseState(identity), identity) + } + getPlacement(browserPageId: string): RuntimeBrowserPlacement | undefined { return this.pagePlacements.getPlacement(browserPageId) } diff --git a/src/main/runtime/browser/client-pages/runtime-browser-client-automation.ts b/src/main/runtime/browser/client-pages/runtime-browser-client-automation.ts index bd824e02877..89f9f4c0e9f 100644 --- a/src/main/runtime/browser/client-pages/runtime-browser-client-automation.ts +++ b/src/main/runtime/browser/client-pages/runtime-browser-client-automation.ts @@ -1,4 +1,5 @@ import { BrowserClientAutomationMethod } from '../../../../shared/browser/client-hosting/browser-client-automation-protocol' +import { BrowserError } from '../../../browser/browser-error' import { browserExpectedUrlMismatchError } from '../../../browser/cdp/input/browser-expected-page-url' import type { BrowserHostLeaseRegistry } from '../../browser-host/leases/browser-host-lease-registry' import type { RuntimeBrowserPageRegistry } from './runtime-browser-page-registry' @@ -36,14 +37,15 @@ export async function routeRuntimeBrowserClientAutomation(options: { method: method.data, params: options.params, // A person's link or relayed input must not light the desktop page as agent-driven. - agent: isAgentRpcCaller(options.caller) + agent: isAgentRpcCaller(options.caller), + stagedResults: true } ) const result = await issued.result if (result.status === 'failed') { throw result.expectedUrlMismatch ? browserExpectedUrlMismatchError(result.expectedUrlMismatch) - : new Error(result.errorCode) + : new BrowserError(result.errorCode, result.errorMessage ?? result.errorCode) } return { handled: true, result: result.value } } diff --git a/src/main/runtime/rpc/methods/browser/browser-client-host.ts b/src/main/runtime/rpc/methods/browser/browser-client-host.ts index 87b96c1a590..7d5d3d9d409 100644 --- a/src/main/runtime/rpc/methods/browser/browser-client-host.ts +++ b/src/main/runtime/rpc/methods/browser/browser-client-host.ts @@ -11,8 +11,10 @@ import { import { BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE, BrowserClientHostAttachParams, + BrowserClientHostCommandResultChunkParams, BrowserClientHostCommandResultParams } from '../../../../../shared/browser/client-hosting/browser-client-host-protocol' +import { BROWSER_CLIENT_HOST_COMMAND_RESULT_CHUNK_METHOD } from '../../../../../shared/browser/client-hosting/browser-client-automation-protocol' import { BrowserError } from '../../../../browser/browser-error' import { getBrowserHostLeaseRegistry } from '../../../browser-host/leases/browser-host-lease-registry-instance' import { getRuntimeBrowserPageRegistry } from '../../../browser/client-pages/runtime-browser-page-registry' @@ -20,7 +22,7 @@ import { adoptRuntimeBrowserClientPagesFromInventory } from '../../../browser/cl import { recoverUnavailableRuntimeBrowserClientPages } from '../../../browser/client-pages/runtime-browser-client-page-recovery' import { releaseRuntimeBrowserClientPageRecord } from '../../../browser/client-pages/runtime-browser-client-page-release' import { getBrowserHostRequestRegistry } from '../../../browser-host/leases/browser-host-request-registry' -import { defineMethod, defineStreamingMethod } from '../../core' +import { defineMethod, defineStreamingMethod, type RpcContext } from '../../core' export const BROWSER_CLIENT_HOST_METHODS = [ defineStreamingMethod({ @@ -169,35 +171,25 @@ export const BROWSER_CLIENT_HOST_METHODS = [ name: 'browser.clientHost.commandResult', permission: 'workspace', params: BrowserClientHostCommandResultParams, - handler: ( - params, - { runtime, pairedDeviceId, connectionId, clientKind, clientCapabilities } - ) => { - if (clientKind !== 'runtime' || !pairedDeviceId || !connectionId) { - throw new Error('authenticated_browser_client_host_required') - } - if (!clientCapabilities?.includes(BROWSER_CLIENT_HOST_RUNTIME_CAPABILITY)) { - throw new Error('browser_client_host_capability_required') - } - if (params.authorityRuntimeId !== runtime.getRuntimeId()) { - throw new BrowserError( - BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE, - BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE - ) - } - const accepted = getBrowserHostLeaseRegistry(runtime).settleClientPageCommand( - { - authorityEpoch: params.authorityEpoch, - browserHostClientId: params.browserHostClientId, - browserHostGeneration: params.browserHostGeneration, - pairedDeviceId, - connectionId - }, + handler: (params, context) => { + const accepted = getBrowserHostLeaseRegistry(context.runtime).settleClientPageCommand( + requireCommandResultIdentity(params, context), params ) return { accepted } } }), + defineMethod({ + name: BROWSER_CLIENT_HOST_COMMAND_RESULT_CHUNK_METHOD, + permission: 'workspace', + params: BrowserClientHostCommandResultChunkParams, + handler: (params, context) => { + getBrowserHostLeaseRegistry(context.runtime) + .requireCommandResultLedger(requireCommandResultIdentity(params, context)) + .stageResultChunk(params) + return { accepted: true } + } + }), defineMethod({ name: 'browser.clientHost.requestResult', permission: 'workspace', @@ -256,3 +248,37 @@ export const BROWSER_CLIENT_HOST_METHODS = [ } }) ] + +function requireCommandResultIdentity( + params: { + authorityRuntimeId: string + authorityEpoch: string + browserHostClientId: string + browserHostGeneration: number + }, + context: Pick< + RpcContext, + 'runtime' | 'pairedDeviceId' | 'connectionId' | 'clientKind' | 'clientCapabilities' + > +) { + const { runtime, pairedDeviceId, connectionId, clientKind, clientCapabilities } = context + if (clientKind !== 'runtime' || !pairedDeviceId || !connectionId) { + throw new Error('authenticated_browser_client_host_required') + } + if (!clientCapabilities?.includes(BROWSER_CLIENT_HOST_RUNTIME_CAPABILITY)) { + throw new Error('browser_client_host_capability_required') + } + if (params.authorityRuntimeId !== runtime.getRuntimeId()) { + throw new BrowserError( + BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE, + BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE + ) + } + return { + authorityEpoch: params.authorityEpoch, + browserHostClientId: params.browserHostClientId, + browserHostGeneration: params.browserHostGeneration, + pairedDeviceId, + connectionId + } +} diff --git a/src/main/startup/main-process-ready-runtime.ts b/src/main/startup/main-process-ready-runtime.ts index 4be3bb1912d..591411a8cb3 100644 --- a/src/main/startup/main-process-ready-runtime.ts +++ b/src/main/startup/main-process-ready-runtime.ts @@ -93,7 +93,8 @@ export async function initializeReadyRuntimeServices(): Promise { if (!response.ok) { const mismatch = BrowserExpectedUrlMismatch.safeParse(response.error.data) throw new BrowserClientPageCommandError(response.error.code, { - expectedUrlMismatch: mismatch.success ? mismatch.data : undefined + expectedUrlMismatch: mismatch.success ? mismatch.data : undefined, + detail: response.error.message }) } return response.result diff --git a/src/shared/browser/client-hosting/browser-client-automation-protocol.ts b/src/shared/browser/client-hosting/browser-client-automation-protocol.ts index 0dddb155b88..3d001333311 100644 --- a/src/shared/browser/client-hosting/browser-client-automation-protocol.ts +++ b/src/shared/browser/client-hosting/browser-client-automation-protocol.ts @@ -5,7 +5,14 @@ export const BROWSER_CLIENT_AUTOMATION_HOST_CAPABILITY = 'automation-v1' as cons // Older hosts re-parse params and would drop expectUrl, so commands carrying it need this. export const BROWSER_CLIENT_EXPECT_URL_HOST_CAPABILITY = 'expect-url-v1' as const export const BROWSER_CLIENT_AUTOMATION_PARAMS_MAX_BYTES = 256 * 1024 +// Why: sized to a host's 1 MiB inbound frame, which sealing inflates by 4/3. export const BROWSER_CLIENT_AUTOMATION_RESULT_MAX_BYTES = 768 * 1024 +// Larger results travel ahead of their settlement in chunks; base64 keeps each chunk inside one frame. +export const BROWSER_CLIENT_AUTOMATION_RESULT_CHUNK_MAX_BYTES = 512 * 1024 +export const BROWSER_CLIENT_AUTOMATION_STAGED_RESULT_MAX_BYTES = 64 * 1024 * 1024 +export const BROWSER_CLIENT_HOST_COMMAND_RESULT_CHUNK_METHOD = + 'browser.clientHost.commandResultChunk' as const +const BROWSER_CLIENT_AUTOMATION_ERROR_MESSAGE_MAX_LENGTH = 4096 export const BROWSER_CLIENT_AUTOMATION_METHODS = [ 'browser.snapshot', @@ -105,23 +112,43 @@ export const BrowserClientAutomationCommand = z.object({ method: BrowserClientAutomationMethod, params: BrowserClientAutomationParams, /** The host's caller was an agent's CLI; older hosts omit it and every command counts as one. */ - agent: z.boolean().optional() + agent: z.boolean().optional(), + /** The host assembles results sent as chunks; older hosts omit it and refuse results over one frame. */ + stagedResults: z.literal(true).optional() }) export type BrowserClientAutomationCommand = z.infer export const BrowserClientAutomationResult = z.discriminatedUnion('status', [ z.object({ status: z.literal('completed'), - value: BrowserClientAutomationResultValue.optional() + value: BrowserClientAutomationResultValue.optional(), + /** The value's JSON byte length, already sent as chunks; replaces `value`. */ + stagedValueBytes: z + .number() + .int() + .positive() + .max(BROWSER_CLIENT_AUTOMATION_STAGED_RESULT_MAX_BYTES) + .optional() }), z.object({ status: z.literal('failed'), errorCode: z.string().min(1).max(256), + errorMessage: z + .string() + .min(1) + .max(BROWSER_CLIENT_AUTOMATION_ERROR_MESSAGE_MAX_LENGTH) + .optional(), expectedUrlMismatch: BrowserExpectedUrlMismatch.optional() }) ]) export type BrowserClientAutomationResult = z.infer +export function truncateBrowserClientAutomationErrorMessage(message: string): string | undefined { + return message.length > 0 + ? message.slice(0, BROWSER_CLIENT_AUTOMATION_ERROR_MESSAGE_MAX_LENGTH) + : undefined +} + function enforceJsonByteBudget( value: unknown, maxBytes: number, diff --git a/src/shared/browser/client-hosting/browser-client-host-protocol.ts b/src/shared/browser/client-hosting/browser-client-host-protocol.ts index fcc27afaa90..bf54dce1863 100644 --- a/src/shared/browser/client-hosting/browser-client-host-protocol.ts +++ b/src/shared/browser/client-hosting/browser-client-host-protocol.ts @@ -5,6 +5,8 @@ import { import { z } from 'zod' import { BrowserNetworkExecutionHost } from './browser-network-execution-host' import { + BROWSER_CLIENT_AUTOMATION_RESULT_CHUNK_MAX_BYTES, + BROWSER_CLIENT_AUTOMATION_STAGED_RESULT_MAX_BYTES, BrowserClientAutomationCommand, BrowserClientAutomationResult as BrowserClientHostCommandResult } from './browser-client-automation-protocol' @@ -296,6 +298,19 @@ export const BrowserClientHostCommandResultParams = BrowserClientPageCommandAuth result: BrowserClientHostCommandResult }) +export const BrowserClientHostCommandResultChunkParams = BrowserClientPageCommandAuthority.extend({ + offset: z.number().int().nonnegative().max(BROWSER_CLIENT_AUTOMATION_STAGED_RESULT_MAX_BYTES), + contentBase64: z + .string() + .min(1) + .max(Math.ceil(BROWSER_CLIENT_AUTOMATION_RESULT_CHUNK_MAX_BYTES / 3) * 4) + .regex(/^[A-Za-z0-9+/]*={0,2}$/) +}) + +export type BrowserClientHostCommandResultChunkParams = z.infer< + typeof BrowserClientHostCommandResultChunkParams +> + export const BrowserClientHostCommandResultAck = z.object({ accepted: z.boolean() }) diff --git a/src/shared/rpc-contract/rpc-params-catalog.generated.ts b/src/shared/rpc-contract/rpc-params-catalog.generated.ts index 0eb5af9d568..6d7ac6d4928 100644 --- a/src/shared/rpc-contract/rpc-params-catalog.generated.ts +++ b/src/shared/rpc-contract/rpc-params-catalog.generated.ts @@ -16,6 +16,7 @@ import { } from '../browser/client-hosting/browser-client-file-channel-protocol' import { BrowserClientHostAttachParams, + BrowserClientHostCommandResultChunkParams, BrowserClientHostCommandResultParams, BrowserNetworkTunnelAttachParams } from '../browser/client-hosting/browser-client-host-protocol' @@ -926,6 +927,7 @@ export const RPC_PARAMS_BY_METHOD = { 'browser.click': Element, 'browser.clientHost.attach': BrowserClientHostAttachParams, 'browser.clientHost.commandResult': BrowserClientHostCommandResultParams, + 'browser.clientHost.commandResultChunk': BrowserClientHostCommandResultChunkParams, 'browser.clientHost.fileChannel.abort': BrowserClientFileChannelAbortParams, 'browser.clientHost.fileChannel.read': BrowserClientFileChannelReadParams, 'browser.clientHost.fileChannel.write': BrowserClientFileChannelWriteParams, From 404ab9fccfac51eae6db56248648c11f11066d7c Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 02:50:19 -0800 Subject: [PATCH 076/178] test: remove upstream projections and duplicate submodule guard (#41974) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 1 - .../submodules/status-submodule.test.ts | 33 ------------- .../upstream/status-upstream-ref.test.ts | 43 ----------------- .../source-control/upstream/upstream.test.ts | 34 ------------- .../git-config-snapshot-runner.test.ts | 24 ---------- .../git-resolution-config-snapshot.test.ts | 20 +------- .../diff/git-diff-transport-budget.test.ts | 48 +------------------ 7 files changed, 3 insertions(+), 200 deletions(-) delete mode 100644 src/main/git/source-control/upstream/upstream.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 282a02f98c0..fcbafaeab0f 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -1745,7 +1745,6 @@ "src/main/git/source-control/submodules/status-submodule.test.ts": 306, "src/main/git/source-control/upstream/status-upstream-ref.test.ts": 52, "src/main/git/source-control/upstream/upstream-deferred-fork-remote-real.test.ts": 319, - "src/main/git/source-control/upstream/upstream.test.ts": 406, "src/main/git/worktrees/creation/worktree-add-local-base-refresh.test.ts": 362, "src/main/git/worktrees/creation/worktree-add-pending-removal.test.ts": 346, "src/main/git/worktrees/creation/worktree-add-remote-branch-upstream-real-git.test.ts": 786, diff --git a/src/main/git/source-control/submodules/status-submodule.test.ts b/src/main/git/source-control/submodules/status-submodule.test.ts index 8dd28c57de6..47d29365545 100644 --- a/src/main/git/source-control/submodules/status-submodule.test.ts +++ b/src/main/git/source-control/submodules/status-submodule.test.ts @@ -67,8 +67,6 @@ import { clearSubmodulePathsCacheForTests } from './submodule-paths' import { getDiff } from '../diffs/file-diff' describe('submodule diff routing', () => { - const OLD_OID = 'a'.repeat(40) - const NEW_OID = 'b'.repeat(40) const PARENT = path.resolve('/repo-sm') beforeEach(() => { @@ -80,24 +78,6 @@ describe('submodule diff routing', () => { statMock.mockReset() existsSyncMock.mockReset() statMock.mockResolvedValue({ isFile: () => true, size: 12 }) - gitExecFileAsyncMock.mockImplementation((args: string[], options?: { cwd?: string }) => { - if (args[0] === 'config' && args.includes('.gitmodules')) { - // Declare the submodule only in its parent so recursion terminates. - return Promise.resolve({ - stdout: options?.cwd === PARENT ? 'submodule.flutter_mine.path\nflutter_mine\0' : '' - }) - } - if (args[0] === 'ls-files') { - return Promise.resolve({ stdout: `160000 ${OLD_OID} 0\tflutter_mine\n` }) - } - if (args[0] === 'ls-tree') { - return Promise.resolve({ stdout: `160000 commit ${OLD_OID}\tflutter_mine\n` }) - } - if (args[0] === 'rev-parse') { - return Promise.resolve({ stdout: `${NEW_OID}\n` }) - } - return Promise.resolve({ stdout: '' }) - }) }) it('rejects inner submodule diffs whose .gitmodules path escapes the worktree', async () => { @@ -112,17 +92,4 @@ describe('submodule diff routing', () => { await expect(getDiff(PARENT, '../evil/secret.txt', false)).rejects.toThrow('Access denied') }) - - it('rejects gitlink pointer diffs whose .gitmodules path escapes the worktree', async () => { - gitExecFileAsyncMock.mockImplementation((args: string[], options?: { cwd?: string }) => { - if (args[0] === 'config' && args.includes('.gitmodules')) { - return Promise.resolve({ - stdout: options?.cwd === PARENT ? 'submodule.evil.path\n../evil\0' : '' - }) - } - return Promise.resolve({ stdout: '' }) - }) - - await expect(getDiff(PARENT, '../evil', false)).rejects.toThrow('Access denied') - }) }) diff --git a/src/main/git/source-control/upstream/status-upstream-ref.test.ts b/src/main/git/source-control/upstream/status-upstream-ref.test.ts index 85071b37cc0..5b4b05dcc34 100644 --- a/src/main/git/source-control/upstream/status-upstream-ref.test.ts +++ b/src/main/git/source-control/upstream/status-upstream-ref.test.ts @@ -4,49 +4,6 @@ import { resolveGitStatusUpstreamRef } from './status-upstream-ref' const signal = (): AbortSignal => new AbortController().signal describe('resolveGitStatusUpstreamRef', () => { - it('preserves the exact namespace for a slash-containing remote name', async () => { - const execGit = vi.fn().mockResolvedValue({ - stdout: - 'refs/heads/feature/main\0refs/remotes/team/fork/feature/main\0team/fork/feature/main\n' - }) - - await expect( - resolveGitStatusUpstreamRef( - execGit, - '/repo', - 'refs/heads/feature/main', - 'team/fork/feature/main', - signal() - ) - ).resolves.toBe('refs/remotes/team/fork/feature/main') - expect(execGit).toHaveBeenCalledWith( - [ - 'for-each-ref', - '--format=%(refname)%00%(upstream)%00%(upstream:short)', - '--count=1', - 'refs/heads/feature/main' - ], - '/repo', - expect.any(AbortSignal) - ) - }) - - it('returns a slash-named local upstream for the binding validator to reject', async () => { - const execGit = vi.fn().mockResolvedValue({ - stdout: 'refs/heads/feature/topic\0refs/heads/feature/base\0feature/base\n' - }) - - await expect( - resolveGitStatusUpstreamRef( - execGit, - '/repo', - 'refs/heads/feature/topic', - 'feature/base', - signal() - ) - ).resolves.toBe('refs/heads/feature/base') - }) - it('resolves an accepted effective upstream override explicitly', async () => { const execGit = vi .fn() diff --git a/src/main/git/source-control/upstream/upstream.test.ts b/src/main/git/source-control/upstream/upstream.test.ts deleted file mode 100644 index 2f7ec01d798..00000000000 --- a/src/main/git/source-control/upstream/upstream.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' - -const { gitExecFileAsyncMock } = vi.hoisted(() => ({ - gitExecFileAsyncMock: vi.fn() -})) - -vi.mock('../../command-runner/git-exec-file', async (importOriginal) => ({ - ...(await importOriginal>()), - gitExecFileAsync: gitExecFileAsyncMock -})) - -import { getUpstreamStatus, invalidateGitUpstreamStatusReads } from './upstream' - -describe('getUpstreamStatus', () => { - beforeEach(() => { - gitExecFileAsyncMock.mockReset() - invalidateGitUpstreamStatusReads() - }) - - it('does not hide git failures while checking an explicit publish target', async () => { - gitExecFileAsyncMock.mockResolvedValueOnce({ stdout: '', stderr: '' }).mockRejectedValueOnce( - Object.assign(new Error('fatal: not a git repository'), { - stderr: 'fatal: not a git repository' - }) - ) - - await expect( - getUpstreamStatus('/repo', { - remoteName: 'fork', - branchName: 'feature/fix' - }) - ).rejects.toThrow('fatal: not a git repository') - }) -}) diff --git a/src/shared/git/commands/git-config-snapshot-runner.test.ts b/src/shared/git/commands/git-config-snapshot-runner.test.ts index ef8ecf687a4..707f73353b2 100644 --- a/src/shared/git/commands/git-config-snapshot-runner.test.ts +++ b/src/shared/git/commands/git-config-snapshot-runner.test.ts @@ -10,17 +10,6 @@ function listSnapshot(records: string[]): string { type GitCommandRunner = Parameters[0] -function countCalls( - runGit: ReturnType>, - expectedArgs: string[] -): number { - return runGit.mock.calls.filter(([args]) => { - return ( - args.length === expectedArgs.length && args.every((arg, index) => arg === expectedArgs[index]) - ) - }).length -} - describe('createGitConfigSnapshotRunner', () => { it('case-folds the requested section and key', async () => { const runGit = vi.fn(async () => ({ @@ -76,7 +65,6 @@ describe('createGitConfigSnapshotRunner', () => { }) it('rejects an absent key from a loaded snapshot instead of returning empty success', async () => { - // Missing keys must reject like config --get, without respawning the command this snapshot coalesces. const runGit = vi.fn(async () => ({ stdout: listSnapshot(['branch.main.remote\norigin']) })) @@ -85,12 +73,6 @@ describe('createGitConfigSnapshotRunner', () => { await expect(runner(['config', '--get', 'branch.main.merge'])).rejects.toThrow( GitConfigSnapshotKeyNotFoundError ) - expect(countCalls(runGit, ['config', '--list', '-z'])).toBe(1) - expect( - runGit.mock.calls.filter(([args]) => { - return args[0] === 'config' && args[1] === '--get' - }) - ).toHaveLength(0) }) it('falls back to real config --get after snapshot fetch failure', async () => { @@ -108,11 +90,5 @@ describe('createGitConfigSnapshotRunner', () => { await expect(runner(['config', '--get', 'branch.main.remote'])).resolves.toEqual({ stdout: 'real:branch.main.remote' }) - await expect(runner(['config', '--get', 'branch.main.merge'])).resolves.toEqual({ - stdout: 'real:branch.main.merge' - }) - - expect(countCalls(runGit, ['config', '--list', '-z'])).toBe(1) - expect(runGit.mock.calls.filter(([args]) => args[1] === '--get')).toHaveLength(2) }) }) diff --git a/src/shared/git/compatibility/git-resolution-config-snapshot.test.ts b/src/shared/git/compatibility/git-resolution-config-snapshot.test.ts index fd433f1ddd3..61dae350b3d 100644 --- a/src/shared/git/compatibility/git-resolution-config-snapshot.test.ts +++ b/src/shared/git/compatibility/git-resolution-config-snapshot.test.ts @@ -1,27 +1,14 @@ import { describe, expect, it, vi } from 'vitest' import { resolveConfiguredGitPushTarget } from '../remotes/git-push-target-resolution' -function createRunner(config = new Map(), snapshotFails = false) { +function createRunner(config = new Map()) { return vi.fn(async (args: string[]) => { if (args[0] === 'symbolic-ref') { return { stdout: 'refs/heads/feature\n' } } if (args[0] === 'config' && args[1] === '--list') { - if (snapshotFails) { - throw new Error('snapshot unavailable') - } return { stdout: Array.from(config, ([key, value]) => `${key}\n${value}\0`).join('') } } - if (args[0] === 'config' && args[1] === '--get') { - const value = config.get((args[2] ?? '').toLowerCase()) - if (value === undefined) { - throw new Error('missing config key') - } - return { stdout: `${value}\n` } - } - if (args[0] === 'rev-parse') { - throw new Error(args.includes('HEAD@{u}') ? 'fatal: no upstream configured' : 'missing ref') - } throw new Error(`unexpected Git command: ${args.join(' ')}`) }) } @@ -43,10 +30,5 @@ describe('resolution config snapshots', () => { remote: 'other-fork', refspec: 'HEAD:feature' }) - - expect(runGit.mock.calls.filter(([args]) => args[0] === 'config')).toEqual([ - [['config', '--list', '-z']], - [['config', '--list', '-z']] - ]) }) }) diff --git a/src/shared/git/diff/git-diff-transport-budget.test.ts b/src/shared/git/diff/git-diff-transport-budget.test.ts index 6ff43690fa1..29c4b980be2 100644 --- a/src/shared/git/diff/git-diff-transport-budget.test.ts +++ b/src/shared/git/diff/git-diff-transport-budget.test.ts @@ -1,15 +1,8 @@ // JSON escaping can expand controls sixfold while NUL-only sniffing treats them as text; pin each budget branch to JSON.stringify. import { describe, expect, it } from 'vitest' import type { GitDiffResult } from './git-diff-compare-types' -import { REMOTE_RUNTIME_MAX_OUTBOUND_JSON_BYTES } from '../../remote-runtime/remote-runtime-memory-limits' -import { - assertGitDiffWithinTransportBudget, - gitDiffExceedsTransportBudget -} from './git-diff-transport-budget' -import { - REMOTE_RPC_MAX_CONTENT_BYTES, - remoteRpcContentBudget -} from '../../remote-rpc-content-budget' +import { assertGitDiffWithinTransportBudget } from './git-diff-transport-budget' +import { REMOTE_RPC_MAX_CONTENT_BYTES } from '../../remote-rpc-content-budget' const BUDGET = REMOTE_RPC_MAX_CONTENT_BYTES @@ -83,41 +76,4 @@ describe('gitDiffExceedsTransportBudget', () => { expect(referenceResultBytes(overBudget)).toBe(BUDGET + 1) expect(budgetError(overBudget).code).toBe('diff_too_large') }) - - it('rejects additive relay metadata beyond the result budget', () => { - const skewed = { - ...textDiff(''), - futureMetadata: 'x'.repeat(BUDGET) - } as unknown as GitDiffResult - - expect(gitDiffExceedsTransportBudget(skewed, BUDGET)).toBe(true) - expect(budgetError(skewed).code).toBe('diff_too_large') - }) -}) - -// Why: the invariant the cap rests on — a diff that exactly fills the content budget must still -// serialize inside the outbound JSON limit once wrapped in an RPC reply. A base64-only version of -// this passes trivially; the newline and control-char fixtures are the load-bearing ones. -describe('envelope ceiling', () => { - function replyBytes(result: GitDiffResult, requestId = 'req_0123456789abcdef'): number { - return Buffer.byteLength( - JSON.stringify({ - id: requestId, - ok: true, - result, - _meta: { runtimeId: '00000000-0000-4000-8000-000000000000' } - }), - 'utf8' - ) - } - - it('charges an escape-dense request id instead of overflowing the fixed reserve', () => { - const requestId = '\u0001'.repeat(8_192) - const budget = remoteRpcContentBudget(requestId) - - expect(budget).toBeLessThan(BUDGET) - expect(replyBytes(binaryDiffOfJsonBytes('a', budget), requestId)).toBeLessThanOrEqual( - REMOTE_RUNTIME_MAX_OUTBOUND_JSON_BYTES - ) - }) }) From 94e84e4044d00d6f863e7766f7520bbb37fcf72a Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 03:04:34 -0800 Subject: [PATCH 077/178] test: remove RPC work budgets and push lease replays (#41981) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../storage-lease.test.mjs | 19 +--------- .../src/delivery/durable-push-claim.test.ts | 18 +-------- .../src/delivery/durable-push-store.test.ts | 34 ----------------- src/shared/remote-rpc-content-budget.test.ts | 38 +------------------ 4 files changed, 3 insertions(+), 106 deletions(-) diff --git a/.github/actions/cloud-sql-rollout-lease/storage-lease.test.mjs b/.github/actions/cloud-sql-rollout-lease/storage-lease.test.mjs index 7463c8ee0b6..d70941ace81 100644 --- a/.github/actions/cloud-sql-rollout-lease/storage-lease.test.mjs +++ b/.github/actions/cloud-sql-rollout-lease/storage-lease.test.mjs @@ -11,18 +11,13 @@ const BUCKET = 'onorca-cloud-terraform-state' const OBJECT = 'terraform/state/cloud-sql-rollout/production.lock' const NOW = 1_756_000_000_000 -/** Model real GCS generation increments, ifGenerationMatch and 412 conflicts; faults inject failures. */ -function fakeStorage({ object = null, faults = [] } = {}) { +function fakeStorage({ object = null } = {}) { const state = { object, requests: [] } const fetcher = async (rawUrl, init = {}) => { const url = new URL(rawUrl) const method = init.method ?? 'GET' const record = { method, url, path: url.pathname, search: url.searchParams } state.requests.push(record) - const fault = faults.find((candidate) => candidate.when(record)) - if (fault) { - return json(fault.status, fault.body ?? {}) - } if (url.pathname.startsWith('/upload/')) { const want = url.searchParams.get('ifGenerationMatch') @@ -114,13 +109,6 @@ test('refuses a live lease held by another run and never writes', async () => { .catch((thrown) => thrown) assert.ok(error instanceof LeaseConflict) - assert.equal(error.holder.repository, 'stablyai/orca') - assert.equal(error.holder.run_url, 'https://github.com/stablyai/orca/actions/runs/9001') - assert.equal( - storage.state.requests.filter((request) => request.method !== 'GET').length, - 0, - 'a foreign live lease must not be written' - ) assert.equal(storage.state.object.generation, '1500') }) @@ -164,9 +152,6 @@ test('takes over an expired lease and warns naming the stale holder', async () = assert.equal(claim.state, 'takeover') assert.equal(warnings.length, 1) - assert.match(warnings[0], /stablyai\/orca/) - assert.match(warnings[0], /Deploy Relay Production Capacity/) - assert.match(warnings[0], /actions\/runs\/9001/) const upload = storage.state.requests.find((request) => request.path.startsWith('/upload/')) assert.equal(upload.search.get('ifGenerationMatch'), '1500') assert.equal(storage.state.object.body.holder_key, HOLDER.holderKey) @@ -180,8 +165,6 @@ test('releases with a generation match and leaves the object gone', async () => const released = await lease.release(HOLDER.holderKey) assert.deepEqual(released, { released: true, generation: claim.generation }) - const remove = storage.state.requests.find((request) => request.method === 'DELETE') - assert.equal(remove.search.get('ifGenerationMatch'), claim.generation) assert.equal(storage.state.object, null) }) diff --git a/cloud/apps/push/src/delivery/durable-push-claim.test.ts b/cloud/apps/push/src/delivery/durable-push-claim.test.ts index 93f65135504..86f20726234 100644 --- a/cloud/apps/push/src/delivery/durable-push-claim.test.ts +++ b/cloud/apps/push/src/delivery/durable-push-claim.test.ts @@ -1,6 +1,6 @@ import { afterEach, expect, it } from 'vitest' import type { CloudDatabase as PushDatabase } from '@orca-cloud/cloud-database/cloud-database' -import { DELIVERY_LEASE_MS, DurablePushStore } from './durable-push-store.js' +import { DurablePushStore } from './durable-push-store.js' import { cleanupDurablePushFixtures, @@ -139,20 +139,4 @@ it.skipIf(!durablePushTestDatabaseUrl)( } ) -// Only the TTL term keeps the candidate scan off an unpruned backlog; expires_at is not indexed first. -it('frees a device held by a crashed worker once its lease lapses', async () => { - const { db, store, clock, advance } = await fixture() - await store.accept('host', 'phone', notification(1)) - advance(1) - await store.accept('host', 'phone', notification(2)) - const crashed = (await new DurablePushStore(db, clock).claim())! - advance(DELIVERY_LEASE_MS - 1) - expect(await store.claim()).toBeNull() - advance(1) - const reclaimed = (await store.claim())! - expect(reclaimed.id).toBe(crashed.id) - expect(reclaimed.attempts).toBe(2) - await store.finish(reclaimed) - expect((await store.claim())?.notification.notificationSeq).toBe(2) -}) diff --git a/cloud/apps/push/src/delivery/durable-push-store.test.ts b/cloud/apps/push/src/delivery/durable-push-store.test.ts index 9be0e32ec8f..8caac39919d 100644 --- a/cloud/apps/push/src/delivery/durable-push-store.test.ts +++ b/cloud/apps/push/src/delivery/durable-push-store.test.ts @@ -161,37 +161,3 @@ it('does not resurrect an in-flight alert after a dismissal and transient provid expect(await store.pendingCount('phone')).toBe(0) }) -it('fences late renew and finish after an expired claim is dismissed', async () => { - const { db, store, advance } = await fixture() - const alert = notification(1) - await store.accept('host', 'phone', alert) - const stale = (await store.claim())! - advance(DELIVERY_LEASE_MS) - await store.accept('host', 'phone', { - ...notification(2, 'dismiss'), - notificationId: alert.notificationId - }) - const read = async () => - ( - await db.query( - 'SELECT state, payload_json, lease_until FROM push_delivery_batches WHERE batch_id = ?', - [stale.id] - ) - )[0] - expect(await read()).toBeUndefined() - await store.renew(stale) - await store.finish(stale, 1000) - await store.finish(stale) - expect(await read()).toBeUndefined() - const dismissal = (await store.claim())! - expect(dismissal.notification.kind).toBe('dismiss') - await store.finish(dismissal) - await store.accept('host', 'phone', notification(3)) - const fresh = (await store.claim())! - await store.finish(fresh, 1000) - advance(1000) - const retry = (await store.claim())! - expect(retry.id).toBe(fresh.id) - await store.finish(retry) - expect(await store.claim()).toBeNull() -}) diff --git a/src/shared/remote-rpc-content-budget.test.ts b/src/shared/remote-rpc-content-budget.test.ts index 69ff77d52c5..04f7a047ba8 100644 --- a/src/shared/remote-rpc-content-budget.test.ts +++ b/src/shared/remote-rpc-content-budget.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it } from 'vitest' import { REMOTE_RUNTIME_MAX_OUTBOUND_JSON_BYTES } from './remote-runtime/remote-runtime-memory-limits' import { REMOTE_RPC_MAX_CONTENT_BYTES, @@ -15,42 +15,6 @@ describe('remoteRpcContentBudget', () => { expect(remoteRpcResultExceedsContentBudget(result, bytes - 1, ['content'])).toBe(true) }) - it('reuses exact measurements for a shared result object', () => { - const result = { content: '\n'.repeat(64 * 1024), isBinary: false } - const bytes = Buffer.byteLength(JSON.stringify(result), 'utf8') - const charCodeAt = vi.spyOn(String.prototype, 'charCodeAt') - try { - expect(remoteRpcResultExceedsContentBudget(result, bytes, ['content'])).toBe(false) - const firstCalls = charCodeAt.mock.calls.length - expect(firstCalls).toBeGreaterThan(result.content.length) - - expect(remoteRpcResultExceedsContentBudget(result, bytes - 1, ['content'])).toBe(true) - expect(charCodeAt.mock.calls.length - firstCalls).toBeLessThan(100) - } finally { - charCodeAt.mockRestore() - } - }) - - it('reuses raw measurements for a shared oversized result', () => { - const result = { content: 'A'.repeat(4 * 1024 * 1024), isBinary: false } - const byteLength = vi.spyOn(Buffer, 'byteLength') - const contentMeasurementCalls = () => - byteLength.mock.calls.filter(([value]) => value === result.content).length - try { - expect(remoteRpcResultExceedsContentBudget(result, result.content.length, ['content'])).toBe( - true - ) - expect(contentMeasurementCalls()).toBe(1) - - expect( - remoteRpcResultExceedsContentBudget(result, result.content.length - 128, ['content']) - ).toBe(true) - expect(contentMeasurementCalls()).toBe(1) - } finally { - byteLength.mockRestore() - } - }) - it('re-evaluates a cached raw measurement under a larger budget', () => { const result = { content: 'A'.repeat(1_000), isBinary: false } const bytes = Buffer.byteLength(JSON.stringify(result), 'utf8') From f4d294dac25504fc4c1fc20b87cd26fec11d0574 Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Fri, 13 Nov 2026 11:18:51 +0000 Subject: [PATCH 078/178] fix(browser): an agent's new server tab takes the selection from the user's This-device page (#41953) Pageless agent commands prefer the workspace's active client page, and creating an offscreen or renderer tab never cleared it, so the next command after `orca tab create` acted on the user's page. Yield client selection wherever a create moves the bridge's active tab, as orcad already does. --- .../browser-client-page-queries.ts | 9 +++-- .../client-pages/orca-runtime-browser.test.ts | 38 +++++++++++++++++++ .../runtime-browser-page-registry.ts | 8 ++++ .../tabs/browser-tab-create-publication.ts | 7 ++++ .../browser/tabs/browser-tab-queries.ts | 5 +-- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/main/runtime/browser/client-pages/browser-client-page-queries.ts b/src/main/runtime/browser/client-pages/browser-client-page-queries.ts index 07b6c157b09..98509e9c4ca 100644 --- a/src/main/runtime/browser/client-pages/browser-client-page-queries.ts +++ b/src/main/runtime/browser/client-pages/browser-client-page-queries.ts @@ -158,11 +158,12 @@ export async function yieldClientPageSelection( worktree: string | undefined ): Promise { const pages = host.getRuntimeBrowserPageRegistry() + if (worktree) { + pages.yieldSelection((await host.resolveBrowserWorkspace(worktree)).id) + return + } pages.deactivateGlobal() - const workspaceIds = worktree - ? [(await host.resolveBrowserWorkspace(worktree)).id] - : [...new Set(pages.listPages().map((page) => page.workspaceId))] - for (const workspaceId of workspaceIds) { + for (const workspaceId of new Set(pages.listPages().map((page) => page.workspaceId))) { pages.deactivateWorkspace(workspaceId) } } diff --git a/src/main/runtime/browser/client-pages/orca-runtime-browser.test.ts b/src/main/runtime/browser/client-pages/orca-runtime-browser.test.ts index 35465319fc9..57e0e6f0245 100644 --- a/src/main/runtime/browser/client-pages/orca-runtime-browser.test.ts +++ b/src/main/runtime/browser/client-pages/orca-runtime-browser.test.ts @@ -191,4 +191,42 @@ describe('RuntimeBrowserCommands browser screencast', () => { expect(waitForTabRegistrationMock).toHaveBeenCalledWith('page-new') expect(bridge.setActiveTab).toHaveBeenCalledWith(101, 'wt-1') }) + // Pageless agent commands prefer an active client page; an agent's new server tab must take that over. + it('moves the workspace selection off a client page when a server tab is created', async () => { + const { RuntimeBrowserCommands } = await import('../commands/browser-commands') + const pages = new RuntimeBrowserPageRegistry() + pages.publishClientPage({ + browserPageId: 'user-page', + workspaceId: 'wt-1', + browserProfileId: 'default', + executionHostKey: 'local', + placement: { + kind: 'client', + browserHostClientId: 'desktop-1', + browserHostGeneration: 1, + pageHostGeneration: 1 + }, + url: 'https://user.example/', + loading: false, + active: true + }) + const bridge = { + getRegisteredTabs: vi.fn(() => new Map([['agent-page', 102]])), + setActiveTab: vi.fn() + } + const offscreen = { createTab: vi.fn(async () => ({ browserPageId: 'agent-page' })) } + const host = createHost({ + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: create reaches only these two bridge methods. + getAgentBrowserBridge: () => bridge as unknown as AgentBrowserBridge, + getRuntimeBrowserPageRegistry: () => pages, + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: create reaches only createTab. + getOffscreenBrowserBackend: () => offscreen as never + }) + const commands = new RuntimeBrowserCommands(host) + + await commands.browserTabCreate({ worktree: 'id:wt-1', url: 'https://agent.example/' }) + + expect(bridge.setActiveTab).toHaveBeenCalledWith(102, 'wt-1') + expect(pages.listPages('wt-1').find((page) => page.active)).toBeUndefined() + }) }) diff --git a/src/main/runtime/browser/client-pages/runtime-browser-page-registry.ts b/src/main/runtime/browser/client-pages/runtime-browser-page-registry.ts index 519ed9d37d9..a7fb5cb373d 100644 --- a/src/main/runtime/browser/client-pages/runtime-browser-page-registry.ts +++ b/src/main/runtime/browser/client-pages/runtime-browser-page-registry.ts @@ -199,6 +199,14 @@ export class RuntimeBrowserPageRegistry { this.globalActivePageId = null } + /** An engine tab took the selection; unscoped creates and switches move only the global pick. */ + yieldSelection(workspaceId: string | undefined): void { + this.deactivateGlobal() + if (workspaceId !== undefined) { + this.deactivateWorkspace(workspaceId) + } + } + deactivateWorkspace(workspaceId: string): void { for (const [browserPageId, page] of this.pages) { if (page.workspaceId === workspaceId && page.active) { diff --git a/src/main/runtime/browser/tabs/browser-tab-create-publication.ts b/src/main/runtime/browser/tabs/browser-tab-create-publication.ts index 08ba33d6c28..9d6401abb34 100644 --- a/src/main/runtime/browser/tabs/browser-tab-create-publication.ts +++ b/src/main/runtime/browser/tabs/browser-tab-create-publication.ts @@ -1,4 +1,5 @@ import type { AgentBrowserBridge } from '../../../browser/agent-browser/agent-browser-bridge' +import type { RuntimeBrowserPageRegistry } from '../client-pages/runtime-browser-page-registry' import { navigationTargetsHost, resolveRuntimeNavigationTarget, @@ -20,6 +21,7 @@ type BrowserTabCreatePublicationBridge = Pick< export type BrowserTabCreatePublicationHost = { getAgentBrowserBridge(): BrowserTabCreatePublicationBridge | null + getRuntimeBrowserPageRegistry(): Pick markHeadlessBrowserSessionTabActive?( worktreeId: string | undefined, browserPageId: string, @@ -194,6 +196,11 @@ export function publishCreatedBrowserSessionTab( ?.getRegisteredTabs(publication.worktreeId) .get(publication.browserPageId) if (bridge && webContentsId != null) { + // Pageless commands prefer an active client page, so the new engine tab must take over from it. + // Yield first so the bridge's own tabs-changed publication carries the cleared client row. + if (publication.focus.startsActive) { + host.getRuntimeBrowserPageRegistry().yieldSelection(publication.worktreeId) + } bridge.setActiveTab(webContentsId, publication.worktreeId) } } diff --git a/src/main/runtime/browser/tabs/browser-tab-queries.ts b/src/main/runtime/browser/tabs/browser-tab-queries.ts index 51f31bdbe76..5f6deea8b96 100644 --- a/src/main/runtime/browser/tabs/browser-tab-queries.ts +++ b/src/main/runtime/browser/tabs/browser-tab-queries.ts @@ -166,10 +166,7 @@ export class RuntimeBrowserTabQueries { ? (await this.host.resolveBrowserWorkspace(params.worktree)).id : undefined const result = await bridge.tabSwitch(undefined, worktreeId, selected.browserPageId) - this.host.getRuntimeBrowserPageRegistry().deactivateGlobal() - if (worktreeId) { - this.host.getRuntimeBrowserPageRegistry().deactivateWorkspace(worktreeId) - } + this.host.getRuntimeBrowserPageRegistry().yieldSelection(worktreeId) // Scope focus to the owning worktree; never switch the user's workspace. const focusWorktreeId = worktreeId ?? browserManager.getWorktreeIdForTab(result.browserPageId) ?? undefined From 7658aebc4d00f9c5abf4ae8aca8ddf4328dbd3d2 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 03:24:50 -0800 Subject: [PATCH 079/178] test: remove repeated readiness and telemetry schema cases (#41983) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 1 - ...agent-screen-readiness-transcripts.test.ts | 31 ++- .../screen-ruled-agent-transcript-suite.ts | 249 ------------------ .../nested-repo-telemetry-schema.test.ts | 97 ------- .../profile-state-telemetry-schema.spec.ts | 17 -- .../telemetry-payload-contracts.test.ts | 1 - 6 files changed, 15 insertions(+), 381 deletions(-) delete mode 100644 src/main/runtime/terminal/wait/screen-ruled-agent-transcript-suite.ts delete mode 100644 src/shared/nested-repo-telemetry-schema.test.ts delete mode 100644 src/shared/profile-state-telemetry-schema.spec.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index fcbafaeab0f..62734da3d05 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -6401,7 +6401,6 @@ "src/shared/native-chat/transcript/native-chat-unverifiable-turn-status.test.ts": 150, "src/shared/native-file-drop.test.ts": 59, "src/shared/ndjson-retained-chunk-memory.test.ts": 327, - "src/shared/nested-repo-telemetry-schema.test.ts": 196, "src/shared/nested-repo-telemetry.test.ts": 55, "src/shared/network-proxy.test.ts": 44, "src/shared/network/manual-address.test.ts": 68, diff --git a/src/main/runtime/terminal/wait/prime-agent-screen-readiness-transcripts.test.ts b/src/main/runtime/terminal/wait/prime-agent-screen-readiness-transcripts.test.ts index e3b7efea742..e44a6ebe0c5 100644 --- a/src/main/runtime/terminal/wait/prime-agent-screen-readiness-transcripts.test.ts +++ b/src/main/runtime/terminal/wait/prime-agent-screen-readiness-transcripts.test.ts @@ -1,13 +1,6 @@ -import { describe, vi } from 'vitest' -import './agent-transcript-replay-test-harness' -import { describeScreenRuledAgentTranscripts } from './screen-ruled-agent-transcript-suite' - -vi.mock('electron', () => ({ - BrowserWindow: { fromId: vi.fn(() => null) }, - webContents: { fromId: vi.fn(() => null) }, - ipcMain: { on: vi.fn(), removeListener: vi.fn() }, - app: { getPath: vi.fn(() => '/tmp') } -})) +import { describe, expect, it } from 'vitest' +import { finalReplayFrame } from './agent-transcript-replay-test-harness' +import { evaluateAgentStateRules } from '../../agent-state-rules/agent-state-rules-engine' // prime-agent 0.9.8 on macOS, OpenRouter, isolated HOME; 0.9.5 recorded for PR #22154 (see // each .meta.json); STA-8741. @@ -30,11 +23,17 @@ const NOT_READY = [ ] describe('Prime Agent readiness from captured bytes', () => { - describeScreenRuledAgentTranscripts({ - agent: 'prime-agent', - foregroundProcess: 'prime-agent', - ready: READY, - notReady: NOT_READY, - readyWithoutScreen: READY.map(({ name }) => name) + it.each(READY)('$name ($what) reads ready', async ({ name, cols, rows }) => { + const { ruledScreenLines } = await finalReplayFrame(name, cols, rows) + expect( + evaluateAgentStateRules('prime-agent', { readScreenLines: () => ruledScreenLines })?.state + ).toBe('idle') + }) + + it.each(NOT_READY)('$name ($what) does not read ready', async ({ name, cols, rows }) => { + const { ruledScreenLines } = await finalReplayFrame(name, cols, rows) + expect( + evaluateAgentStateRules('prime-agent', { readScreenLines: () => ruledScreenLines })?.state + ).not.toBe('idle') }) }) diff --git a/src/main/runtime/terminal/wait/screen-ruled-agent-transcript-suite.ts b/src/main/runtime/terminal/wait/screen-ruled-agent-transcript-suite.ts deleted file mode 100644 index 43f4d71c54d..00000000000 --- a/src/main/runtime/terminal/wait/screen-ruled-agent-transcript-suite.ts +++ /dev/null @@ -1,249 +0,0 @@ -// One replay suite for every agent whose readiness its live screen decides (terminal-wait-detection.ts). -import { describe, expect, it, vi } from 'vitest' -import { - createTranscriptPane, - TRANSCRIPT_PANE_PTY_ID, - waitForTranscriptIdle -} from '../../orca-runtime-tests/agent-transcript-pane-test-harness' -import { - finalReadProjection, - finalReplayFrame, - readRuntimeFixture, - type TranscriptReplayResize -} from './agent-transcript-replay-test-harness' -import { isQuietReadyScreenBody } from './terminal-wait-detection' -import { evaluateAgentStateRules } from '../../agent-state-rules/agent-state-rules-engine' -import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent' - -export type ScreenRuledFixture = { name: string; cols: number; rows: number; what: string } - -export type ScreenRuledAgentSuite = { - agent: TuiAgent - foregroundProcess: string - ready: readonly ScreenRuledFixture[] - notReady: readonly ScreenRuledFixture[] - /** Ready recordings the text rules or the quiet-process lane settle with no screen. */ - readyWithoutScreen: readonly string[] -} - -// Mismatched grids garble cursor-addressed chrome (#23475 review). -const MISMATCHED_GRIDS = [ - [80, 24], - [100, 30], - [60, 20], - [150, 50] -] as const - -// 8s covers 3s quiescence and the 2s poll; 5s refusal allows both plus at least one poll tick. -const READY_TIMEOUT_MS = 8_000 -const REFUSAL_TIMEOUT_MS = 5_000 -// Allow for multi-MB animated recordings taking seconds in onPtyData. -const PANE_SETUP_SLACK_MS = 30_000 - -function chunkCount(name: string): number { - return Math.ceil(readRuntimeFixture(name).length / 64) -} - -/** Whether the agent's own rules read `screenLines` as an idle composer. */ -export function readsIdleComposer(agent: TuiAgent, screenLines: readonly string[]): boolean { - return evaluateAgentStateRules(agent, { readScreenLines: () => screenLines })?.state === 'idle' -} - -export function describeScreenRuledAgentTranscripts(suite: ScreenRuledAgentSuite): void { - const { agent } = suite - const rule = (screenLines: readonly string[]): boolean => readsIdleComposer(agent, screenLines) - const [firstReady] = suite.ready - if (!firstReady) { - throw new Error(`${agent}: a suite needs a ready recording`) - } - - describe('the screen rule on the final recorded frame', () => { - it.each(suite.ready)('$name ($what) reads ready', async ({ name, cols, rows }) => { - const { ruledScreenLines } = await finalReplayFrame(name, cols, rows) - expect(rule(ruledScreenLines)).toBe(true) - }) - it.each(suite.notReady)('$name ($what) does not', async ({ name, cols, rows }) => { - const { ruledScreenLines } = await finalReplayFrame(name, cols, rows) - expect(rule(ruledScreenLines)).toBe(false) - }) - }) - - describe('fails safe on a garbled grid', () => { - it.each(suite.notReady)('$name never reads ready', async ({ name, cols, rows }) => { - const resize: TranscriptReplayResize = { - atChunk: Math.floor(chunkCount(name) / 2), - cols: 100, - rows: 30 - } - const frames = [ - ...(await Promise.all(MISMATCHED_GRIDS.map(([c, r]) => finalReplayFrame(name, c, r)))), - await finalReplayFrame(name, cols, rows, resize) - ] - for (const { ruledScreenLines, waitText } of frames) { - expect(isQuietReadyScreenBody(waitText, agent, () => ruledScreenLines)).toBe(false) - } - }) - }) - - describe('through the runtime', () => { - async function pane(fixture: ScreenRuledFixture) { - const created = await createTranscriptPane({ - paneTitle: 'Terminal', - foregroundProcess: suite.foregroundProcess, - launchAgent: agent, - data: readRuntimeFixture(fixture.name), - size: { cols: fixture.cols, rows: fixture.rows } - }) - // Drain emulator parsing: one multi-MB chunk can outlive the output quiet clock unlike a live PTY's silence. - await created.runtime.readTerminal(created.handle, { screen: true }) - return created - } - - it.each(suite.ready)( - '$name: a tui-idle wait settles once the pane is quiet', - async (fixture) => { - const { runtime, handle } = await pane(fixture) - await expect( - waitForTranscriptIdle({ runtime, handle }, READY_TIMEOUT_MS) - ).resolves.toMatchObject({ - condition: 'tui-idle', - satisfied: true - }) - }, - READY_TIMEOUT_MS + PANE_SETUP_SLACK_MS - ) - - it.each(suite.notReady)( - '$name: a tui-idle wait does not settle ready', - async (fixture) => { - const { runtime, handle } = await pane(fixture) - const result = await waitForTranscriptIdle({ runtime, handle }, REFUSAL_TIMEOUT_MS).catch( - (error: unknown) => ({ satisfied: false, error: String(error) }) - ) - expect(result.satisfied).toBe(false) - }, - REFUSAL_TIMEOUT_MS + PANE_SETUP_SLACK_MS - ) - - // Why 100x30: a grid no recording was painted for. The PTY reports its real size. - it.each(suite.ready)( - '$name: on a grid the PTY does not have, the lanes the screen would shut decide', - async (fixture) => { - const options = { - paneTitle: 'Terminal', - foregroundProcess: suite.foregroundProcess, - launchAgent: agent, - data: readRuntimeFixture(fixture.name), - size: { cols: 100, rows: 30 } - } - const { runtime, handle } = await createTranscriptPane(options) - await runtime.readTerminal(handle, { screen: true }) - options.size = { cols: fixture.cols, rows: fixture.rows } - const result = await waitForTranscriptIdle({ runtime, handle }, READY_TIMEOUT_MS).catch( - () => ({ satisfied: false }) - ) - expect(result.satisfied).toBe(suite.readyWithoutScreen.includes(fixture.name)) - }, - READY_TIMEOUT_MS + PANE_SETUP_SLACK_MS - ) - - // Reattach reflows without a TUI repaint. - it( - 'on a grid reflowed to the PTY size without a repaint, the lanes the screen would shut decide', - async () => { - const options = { - paneTitle: 'Terminal', - foregroundProcess: suite.foregroundProcess, - launchAgent: agent, - data: readRuntimeFixture(firstReady.name), - size: { cols: 100, rows: 30 } - } - const { runtime, handle } = await createTranscriptPane(options) - options.size = { cols: firstReady.cols, rows: firstReady.rows } - runtime.reflowHeadlessTerminalToPtyGrid( - TRANSCRIPT_PANE_PTY_ID, - firstReady.cols, - firstReady.rows - ) - // Echoing the already-reflowed size sends no SIGWINCH, so the TUI never repaints. - runtime.onExternalPtyResize(TRANSCRIPT_PANE_PTY_ID, firstReady.cols, firstReady.rows) - await runtime.readTerminal(handle, { screen: true }) - const result = await waitForTranscriptIdle({ runtime, handle }, READY_TIMEOUT_MS).catch( - () => ({ satisfied: false }) - ) - expect(result.satisfied).toBe(suite.readyWithoutScreen.includes(firstReady.name)) - }, - READY_TIMEOUT_MS + PANE_SETUP_SLACK_MS - ) - - // Why no bytes: a restored pane has no output clock, so the provider's screen is all it has. - // Why the read projection: it is what the probe's screen read returns, draft blanked. - async function probedVerdict(fixture: ScreenRuledFixture): Promise { - const { lines, draft } = await finalReadProjection(fixture.name, fixture.cols, fixture.rows) - const { runtime, handle } = await createTranscriptPane({ - paneTitle: 'Terminal', - foregroundProcess: suite.foregroundProcess, - launchAgent: agent, - data: '' - }) - const read = vi.spyOn(runtime, 'readTerminal').mockResolvedValue({ - handle, - status: 'running', - tail: lines, - ...(draft === undefined ? {} : { draft }), - truncated: false, - nextCursor: null, - source: 'screen' - }) - const result = await waitForTranscriptIdle({ runtime, handle }, 2_500).catch(() => ({ - satisfied: false - })) - // Presence precondition: the visible-screen probe actually ran. - expect(read).toHaveBeenCalled() - return result.satisfied === true - } - - it.each(suite.ready)( - '$name: a restored pane settles from a visible-screen read', - async (fixture) => { - expect(await probedVerdict(fixture)).toBe(true) - }, - 15_000 - ) - - it.each(suite.notReady)( - '$name: a restored pane does not settle from a visible-screen read', - async (fixture) => { - expect(await probedVerdict(fixture)).toBe(false) - }, - 15_000 - ) - - it('waits for quiet again after a ready pane repaints', async () => { - const { runtime, handle } = await pane(firstReady) - vi.useFakeTimers({ - toFake: ['Date', 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval'] - }) - try { - const settled = vi.fn() - const waiting = runtime.waitForTerminal(handle, { - condition: 'tui-idle', - timeoutMs: READY_TIMEOUT_MS - }) - void waiting.then(settled, () => {}) - await vi.advanceTimersByTimeAsync(2_000) - expect(settled).not.toHaveBeenCalled() - - runtime.onPtyData(TRANSCRIPT_PANE_PTY_ID, readRuntimeFixture(firstReady.name), Date.now()) - await runtime.readTerminal(handle, { screen: true }) - await vi.advanceTimersByTimeAsync(2_000) - expect(settled).not.toHaveBeenCalled() - - await vi.advanceTimersByTimeAsync(2_000) - await expect(waiting).resolves.toMatchObject({ condition: 'tui-idle', satisfied: true }) - } finally { - vi.useRealTimers() - } - }) - }) -} diff --git a/src/shared/nested-repo-telemetry-schema.test.ts b/src/shared/nested-repo-telemetry-schema.test.ts deleted file mode 100644 index 0caf6601dcc..00000000000 --- a/src/shared/nested-repo-telemetry-schema.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { eventSchemas } from './telemetry-events' - -const attemptId = '2fbac1e3-5094-45b4-80a6-90281e6e9e09' - -describe('nested repo import telemetry schemas', () => { - it('rejects raw paths, repo names, and raw errors via .strict()', () => { - expect( - eventSchemas.add_repo_nested_scan_result.safeParse({ - attempt_id: attemptId, - surface: 'onboarding', - runtime_kind: 'local', - result: 'review_shown', - selected_path_kind: 'non_git_folder', - found_count: 1, - found_count_bucket: '1', - truncated: false, - timed_out: false, - selected_path: '/Users/alice/work/platform' - }).success - ).toBe(false) - - expect( - eventSchemas.add_repo_nested_import_action.safeParse({ - attempt_id: attemptId, - surface: 'sidebar', - runtime_kind: 'local', - action: 'import_group', - found_count: 1, - found_count_bucket: '1', - selected_count: 1, - selected_count_bucket: '1', - all_selected: true, - repo_name: 'secret-service' - }).success - ).toBe(false) - - expect( - eventSchemas.add_repo_nested_import_result.safeParse({ - attempt_id: attemptId, - surface: 'sidebar', - runtime_kind: 'ssh', - mode: 'group', - outcome: 'failed', - found_count: 1, - found_count_bucket: '1', - selected_count: 1, - selected_count_bucket: '1', - imported_count: 0, - imported_count_bucket: '0', - already_known_count: 0, - already_known_count_bucket: '0', - failed_count: 1, - failed_count_bucket: '1', - all_selected: true, - error_message: 'failed at /Users/alice/work/platform' - }).success - ).toBe(false) - }) - - it('rejects mismatched exact counts and buckets', () => { - expect( - eventSchemas.add_repo_nested_import_action.safeParse({ - attempt_id: attemptId, - surface: 'sidebar', - runtime_kind: 'local', - action: 'import_group', - found_count: 1, - found_count_bucket: '16+', - selected_count: 1, - selected_count_bucket: '1', - all_selected: true - }).success - ).toBe(false) - - expect( - eventSchemas.add_repo_nested_import_result.safeParse({ - attempt_id: attemptId, - surface: 'sidebar', - runtime_kind: 'local', - mode: 'group', - outcome: 'success', - found_count: 2, - found_count_bucket: '2-3', - selected_count: 2, - selected_count_bucket: '2-3', - imported_count: 2, - imported_count_bucket: '16+', - already_known_count: 0, - already_known_count_bucket: '0', - failed_count: 0, - failed_count_bucket: '0', - all_selected: true - }).success - ).toBe(false) - }) -}) diff --git a/src/shared/profile-state-telemetry-schema.spec.ts b/src/shared/profile-state-telemetry-schema.spec.ts deleted file mode 100644 index 57718096e8d..00000000000 --- a/src/shared/profile-state-telemetry-schema.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { eventSchemas } from './telemetry-events' - -describe('profile state authority telemetry', () => { - it('rejects paths or other unbounded diagnostic fields', () => { - expect( - eventSchemas.profile_state_authority_selected.safeParse({ - backend: 'sqlite', - classification: 'sqlite-only', - authority_mode: 'sqlite-established', - runtime: 'desktop', - migrated: false, - database_path: '/private/profile.sqlite' - }).success - ).toBe(false) - }) -}) diff --git a/src/shared/telemetry-payload-contracts.test.ts b/src/shared/telemetry-payload-contracts.test.ts index 9a4cf8ea3e8..c83bd727095 100644 --- a/src/shared/telemetry-payload-contracts.test.ts +++ b/src/shared/telemetry-payload-contracts.test.ts @@ -1,4 +1,3 @@ import './telemetry-orca-cli-feature-tip.spec' import './telemetry-events-feature-education.spec' import './telemetry-feature-wall-events.spec' -import './profile-state-telemetry-schema.spec' From 4245b597659df71fe076a7296fe959b0ad9c69b5 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 13 Nov 2026 03:27:33 -0800 Subject: [PATCH 080/178] refactor: group agent hook providers by domain (#41968) * refactor: group agent hook providers by domain * chore: shrink retired hook test ratchet allowances --- config/flaky-test-pattern-baseline.txt | 3 +- .../scripts/ci/sharding/ci-shard-timings.json | 19 +- .../server/server-codex-question-skip.ts | 2 +- .../server/server-foreign-turn-hook.ts | 2 +- .../agent-hooks/server/server-hydration.ts | 4 +- .../server/server-ingest-remote.ts | 2 +- .../server/server-relay-sessions.ts | 2 +- .../server/server-status-application.ts | 2 +- .../server/server-status-inference.ts | 2 +- .../server/server-status-retries.ts | 4 +- .../server/server-status-update.ts | 8 +- src/main/agent-hooks/server/server-types.ts | 2 +- .../server-claude-helper-recovery.test.ts | 463 ------------------ ...er-claude-owed-notification-expiry.test.ts | 114 ----- .../tool-events/agent-tool-call-reader.ts | 2 +- .../wait/tui-idle-hook-lane-runtime.test.ts | 269 +--------- .../claude-tool-request-identity.ts | 2 +- .../grok-result-discovery.ts | 2 +- .../agent-hook-listener/listener-state.ts | 6 +- ...gent-hook-listener-codex-interrupt.test.ts | 2 +- .../claude-background-task-status.test.ts | 210 +------- .../normalization/normalize-hook-payload.ts | 8 +- .../pane-provider-session.ts | 4 +- .../agent-hook-listener/provider-dispatch.ts | 56 ++- .../provider-event-routing.ts | 30 +- .../providers/{ => amp}/amp-events.ts | 14 +- .../providers/{ => amp}/amp-tool-fields.ts | 6 +- .../antigravity-background-command.test.ts | 6 +- .../antigravity-event-rules.ts | 0 .../{ => antigravity}/antigravity-events.ts | 12 +- .../antigravity-tool-fields.ts | 8 +- .../claude-helper-kill.test-fixture.ts | 109 ----- .../providers/claude-helper-kill.test.ts | 79 --- .../claude-transcript-semantics.test.ts | 190 ------- .../claude-owed-notification-expiry-timers.ts | 12 +- .../claude-owed-task-notifications.ts | 4 +- .../claude-task-notification-delivery.ts | 8 +- .../providers/{ => claude}/claude-events.ts | 34 +- .../{ => claude}/claude-tool-fields.ts | 13 +- .../lifecycle}/claude-compact-completion.ts | 4 +- .../lifecycle}/claude-lifecycle-events.ts | 10 +- .../lifecycle}/claude-pane-hold-evidence.ts | 12 +- .../lifecycle}/claude-roster-state.ts | 20 +- .../claude-session-start-sources.ts | 0 .../lifecycle}/claude-status-build.ts | 12 +- .../lifecycle}/claude-teammate-spawn.ts | 6 +- .../lifecycle}/claude-tool-request-wait.ts | 10 +- .../transcripts}/claude-helper-kill-poller.ts | 16 +- .../claude-helper-transcript-evidence.ts | 4 +- .../transcripts}/claude-transcript-cursor.ts | 0 .../transcripts}/claude-transcript-decoder.ts | 4 +- .../claude-transcript-semantics.test.ts | 34 ++ ...ent-hook-listener-codex-main-agent.test.ts | 6 +- .../{ => codex}/codex-async-questions.ts | 6 +- .../{ => codex}/codex-ended-turn-event.ts | 6 +- .../{ => codex}/codex-ephemeral-threads.ts | 6 +- .../providers/{ => codex}/codex-events.ts | 24 +- .../{ => codex}/codex-question-skip.ts | 4 +- .../{ => codex}/codex-session-start.ts | 12 +- .../providers/{ => codex}/codex-state.ts | 18 +- .../{ => codex}/codex-tool-fields.ts | 6 +- .../{ => codex}/codex-transcript-poll.ts | 12 +- .../{ => command-code}/command-code-events.ts | 8 +- .../command-code-tool-fields.ts | 8 +- .../providers/{ => copilot}/copilot-events.ts | 10 +- .../{ => copilot}/copilot-tool-fields.ts | 8 +- .../providers/{ => cursor}/cursor-events.ts | 8 +- .../{ => cursor}/cursor-tool-fields.ts | 6 +- .../providers/{ => devin}/devin-events.ts | 8 +- .../providers/{ => droid}/droid-events.ts | 10 +- .../{ => droid}/droid-tool-fields.ts | 8 +- .../providers/{ => dsh}/dsh-events.test.ts | 4 +- .../providers/{ => dsh}/dsh-events.ts | 12 +- .../providers/{ => gemini}/gemini-events.ts | 8 +- .../{ => gemini}/gemini-tool-fields.ts | 4 +- .../{ => grok}/grok-background-tasks.ts | 12 +- .../providers/{ => grok}/grok-events.ts | 20 +- .../providers/{ => grok}/grok-tool-fields.ts | 12 +- .../{ => hermes}/hermes-events.test.ts | 2 +- .../providers/{ => hermes}/hermes-events.ts | 8 +- .../{ => hermes}/hermes-tool-fields.ts | 6 +- .../providers/jcode-turn-prompt.test.ts | 48 -- .../providers/{ => jcode}/jcode-events.ts | 10 +- .../jcode-helper-permission-wait.ts | 12 +- .../{ => jcode}/jcode-tool-fields.ts | 4 +- .../{ => jcode}/jcode-turn-prompt.ts | 6 +- .../providers/{ => kimi}/kimi-events.ts | 10 +- .../providers/{ => kiro}/kiro-events.ts | 8 +- .../providers/{ => kiro}/kiro-tool-fields.ts | 6 +- .../providers/muse-events.test.ts | 229 --------- .../providers/muse/muse-events.test.ts | 85 ++++ .../providers/{ => muse}/muse-events.ts | 18 +- .../opencode-family-events.ts | 12 +- .../opencode-family-tool-fields.ts | 8 +- .../{ => pi-family}/pi-family-events.ts | 14 +- .../{ => pi-family}/pi-family-tool-fields.ts | 15 +- .../providers/qoder-events.ts | 19 - .../{ => zcode}/zcode-events.test.ts | 4 +- .../providers/{ => zcode}/zcode-events.ts | 12 +- .../transcript-poll-policy.ts | 4 +- .../agent-hook-result-retry-scheduler.ts | 6 +- ...er-claude-owed-notification-expiry.test.ts | 84 ---- .../status/agent-hook-event-admission.ts | 2 +- .../agent-hook-interrupt-reconciliation.ts | 4 +- 104 files changed, 529 insertions(+), 2200 deletions(-) delete mode 100644 src/main/agent-hooks/server/testing/interruptions/server-claude-helper-recovery.test.ts delete mode 100644 src/main/agent-hooks/server/testing/task-wakeup/server-claude-owed-notification-expiry.test.ts rename src/shared/{ => agent-hook-listener/normalization}/claude-background-task-status.test.ts (54%) rename src/shared/agent-hook-listener/providers/{ => amp}/amp-events.ts (92%) rename src/shared/agent-hook-listener/providers/{ => amp}/amp-tool-fields.ts (90%) rename src/shared/agent-hook-listener/providers/{ => antigravity}/antigravity-background-command.test.ts (87%) rename src/shared/agent-hook-listener/providers/{ => antigravity}/antigravity-event-rules.ts (100%) rename src/shared/agent-hook-listener/providers/{ => antigravity}/antigravity-events.ts (93%) rename src/shared/agent-hook-listener/providers/{ => antigravity}/antigravity-tool-fields.ts (87%) delete mode 100644 src/shared/agent-hook-listener/providers/claude-helper-kill.test-fixture.ts delete mode 100644 src/shared/agent-hook-listener/providers/claude-helper-kill.test.ts delete mode 100644 src/shared/agent-hook-listener/providers/claude-transcript-semantics.test.ts rename src/shared/agent-hook-listener/providers/{ => claude/background-tasks}/claude-owed-notification-expiry-timers.ts (84%) rename src/shared/agent-hook-listener/providers/{ => claude/background-tasks}/claude-owed-task-notifications.ts (98%) rename src/shared/agent-hook-listener/providers/{ => claude/background-tasks}/claude-task-notification-delivery.ts (91%) rename src/shared/agent-hook-listener/providers/{ => claude}/claude-events.ts (92%) rename src/shared/agent-hook-listener/providers/{ => claude}/claude-tool-fields.ts (87%) rename src/shared/agent-hook-listener/providers/{ => claude/lifecycle}/claude-compact-completion.ts (95%) rename src/shared/agent-hook-listener/providers/{ => claude/lifecycle}/claude-lifecycle-events.ts (95%) rename src/shared/agent-hook-listener/providers/{ => claude/lifecycle}/claude-pane-hold-evidence.ts (88%) rename src/shared/agent-hook-listener/providers/{ => claude/lifecycle}/claude-roster-state.ts (93%) rename src/shared/agent-hook-listener/providers/{ => claude/lifecycle}/claude-session-start-sources.ts (100%) rename src/shared/agent-hook-listener/providers/{ => claude/lifecycle}/claude-status-build.ts (83%) rename src/shared/agent-hook-listener/providers/{ => claude/lifecycle}/claude-teammate-spawn.ts (88%) rename src/shared/agent-hook-listener/providers/{ => claude/lifecycle}/claude-tool-request-wait.ts (85%) rename src/shared/agent-hook-listener/providers/{ => claude/transcripts}/claude-helper-kill-poller.ts (92%) rename src/shared/agent-hook-listener/providers/{ => claude/transcripts}/claude-helper-transcript-evidence.ts (97%) rename src/shared/agent-hook-listener/providers/{ => claude/transcripts}/claude-transcript-cursor.ts (100%) rename src/shared/agent-hook-listener/providers/{ => claude/transcripts}/claude-transcript-decoder.ts (97%) create mode 100644 src/shared/agent-hook-listener/providers/claude/transcripts/claude-transcript-semantics.test.ts rename src/shared/agent-hook-listener/{normalization => providers/codex}/agent-hook-listener-codex-main-agent.test.ts (92%) rename src/shared/agent-hook-listener/providers/{ => codex}/codex-async-questions.ts (96%) rename src/shared/agent-hook-listener/providers/{ => codex}/codex-ended-turn-event.ts (79%) rename src/shared/agent-hook-listener/providers/{ => codex}/codex-ephemeral-threads.ts (94%) rename src/shared/agent-hook-listener/providers/{ => codex}/codex-events.ts (93%) rename src/shared/agent-hook-listener/providers/{ => codex}/codex-question-skip.ts (92%) rename src/shared/agent-hook-listener/providers/{ => codex}/codex-session-start.ts (95%) rename src/shared/agent-hook-listener/providers/{ => codex}/codex-state.ts (94%) rename src/shared/agent-hook-listener/providers/{ => codex}/codex-tool-fields.ts (88%) rename src/shared/agent-hook-listener/providers/{ => codex}/codex-transcript-poll.ts (87%) rename src/shared/agent-hook-listener/providers/{ => command-code}/command-code-events.ts (80%) rename src/shared/agent-hook-listener/providers/{ => command-code}/command-code-tool-fields.ts (86%) rename src/shared/agent-hook-listener/providers/{ => copilot}/copilot-events.ts (88%) rename src/shared/agent-hook-listener/providers/{ => copilot}/copilot-tool-fields.ts (97%) rename src/shared/agent-hook-listener/providers/{ => cursor}/cursor-events.ts (88%) rename src/shared/agent-hook-listener/providers/{ => cursor}/cursor-tool-fields.ts (95%) rename src/shared/agent-hook-listener/providers/{ => devin}/devin-events.ts (89%) rename src/shared/agent-hook-listener/providers/{ => droid}/droid-events.ts (90%) rename src/shared/agent-hook-listener/providers/{ => droid}/droid-tool-fields.ts (93%) rename src/shared/agent-hook-listener/providers/{ => dsh}/dsh-events.test.ts (93%) rename src/shared/agent-hook-listener/providers/{ => dsh}/dsh-events.ts (84%) rename src/shared/agent-hook-listener/providers/{ => gemini}/gemini-events.ts (83%) rename src/shared/agent-hook-listener/providers/{ => gemini}/gemini-tool-fields.ts (91%) rename src/shared/agent-hook-listener/providers/{ => grok}/grok-background-tasks.ts (89%) rename src/shared/agent-hook-listener/providers/{ => grok}/grok-events.ts (93%) rename src/shared/agent-hook-listener/providers/{ => grok}/grok-tool-fields.ts (92%) rename src/shared/agent-hook-listener/providers/{ => hermes}/hermes-events.test.ts (99%) rename src/shared/agent-hook-listener/providers/{ => hermes}/hermes-events.ts (93%) rename src/shared/agent-hook-listener/providers/{ => hermes}/hermes-tool-fields.ts (93%) delete mode 100644 src/shared/agent-hook-listener/providers/jcode-turn-prompt.test.ts rename src/shared/agent-hook-listener/providers/{ => jcode}/jcode-events.ts (89%) rename src/shared/agent-hook-listener/providers/{ => jcode}/jcode-helper-permission-wait.ts (90%) rename src/shared/agent-hook-listener/providers/{ => jcode}/jcode-tool-fields.ts (96%) rename src/shared/agent-hook-listener/providers/{ => jcode}/jcode-turn-prompt.ts (81%) rename src/shared/agent-hook-listener/providers/{ => kimi}/kimi-events.ts (90%) rename src/shared/agent-hook-listener/providers/{ => kiro}/kiro-events.ts (84%) rename src/shared/agent-hook-listener/providers/{ => kiro}/kiro-tool-fields.ts (84%) delete mode 100644 src/shared/agent-hook-listener/providers/muse-events.test.ts create mode 100644 src/shared/agent-hook-listener/providers/muse/muse-events.test.ts rename src/shared/agent-hook-listener/providers/{ => muse}/muse-events.ts (92%) rename src/shared/agent-hook-listener/providers/{ => opencode-family}/opencode-family-events.ts (85%) rename src/shared/agent-hook-listener/providers/{ => opencode-family}/opencode-family-tool-fields.ts (92%) rename src/shared/agent-hook-listener/providers/{ => pi-family}/pi-family-events.ts (93%) rename src/shared/agent-hook-listener/providers/{ => pi-family}/pi-family-tool-fields.ts (90%) delete mode 100644 src/shared/agent-hook-listener/providers/qoder-events.ts rename src/shared/agent-hook-listener/providers/{ => zcode}/zcode-events.test.ts (94%) rename src/shared/agent-hook-listener/providers/{ => zcode}/zcode-events.ts (87%) delete mode 100644 src/wsl-guest/agent-hooks/results/agent-hook-server-claude-owed-notification-expiry.test.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index 9bbf1ed4d8d..a965c78d283 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -490,7 +490,7 @@ 23 test-no-wall-clock:src/main/runtime/terminal/output/terminal-query-responder.test.ts 3 test-no-wall-clock:src/main/runtime/terminal/query/orca-runtime-codex-idle-title-chat-address.test.ts 1 test-no-wall-clock:src/main/runtime/terminal/recovery/terminal-pane-recovery-liveness-gate.test.ts -16 test-no-wall-clock:src/main/runtime/terminal/wait/tui-idle-hook-lane-runtime.test.ts +4 test-no-wall-clock:src/main/runtime/terminal/wait/tui-idle-hook-lane-runtime.test.ts 1 test-no-wall-clock:src/main/runtime/workspaces/process-listing/runtime-worktree-structured-agent-rows-liveness.test.ts 1 test-no-wall-clock:src/main/runtime/workspaces/removal/owned-session-teardown-policy.test.ts 6 test-no-wall-clock:src/main/skills/install/skill-bundle-process-termination.integration.test.ts @@ -612,7 +612,6 @@ 2 test-timeout-needs-reason:src/main/runtime/terminal/wait/launched-agent-crash-guard.live-shell.test.ts 2 test-timeout-needs-reason:src/main/runtime/terminal/wait/terminal-interactive-wait-visibility.test.ts 1 test-timeout-needs-reason:src/main/runtime/terminal/wait/terminal-wait-name-only-idle.test.ts -4 test-timeout-needs-reason:src/main/runtime/terminal/wait/tui-idle-hook-lane-runtime.test.ts 1 test-timeout-needs-reason:src/main/skills/install/skill-bundle-process-termination.integration.test.ts 1 test-timeout-needs-reason:src/main/sqlite/sync-database-concurrent-backup.test.ts 2 test-timeout-needs-reason:src/main/ssh/connection/ssh-connection-sftp-wire.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 62734da3d05..ca81603760a 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -511,8 +511,6 @@ "src/main/agent-hooks/server/testing/providers/antigravity/server-antigravity-resume-identity.test.ts": 820, "src/main/agent-hooks/server/testing/permissions/server-claude-child-permission-lifecycle.test.ts": 681, "src/main/agent-hooks/server/testing/interruptions/server-claude-helper-kill.test.ts": 10279, - "src/main/agent-hooks/server/testing/interruptions/server-claude-helper-recovery.test.ts": 34523, - "src/main/agent-hooks/server/testing/task-wakeup/server-claude-owed-notification-expiry.test.ts": 877, "src/main/agent-hooks/server/testing/identity/server-claude-session-continuation.test.ts": 675, "src/main/agent-hooks/server/testing/http/server-claude-statusline.test.ts": 679, "src/main/agent-hooks/server/testing/task-wakeup/server-claude-task-wakeup-lifecycle.test.ts": 5451, @@ -6027,7 +6025,7 @@ "src/shared/agent-hook-listener/normalization/agent-hook-listener-claude-elicitation.test.ts": 229, "src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-approval-ownership.test.ts": 258, "src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-interrupt.test.ts": 240, - "src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-main-agent.test.ts": 207, + "src/shared/agent-hook-listener/providers/codex/agent-hook-listener-codex-main-agent.test.ts": 207, "src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-subagent-names.test.ts": 260, "src/shared/agent-hook-listener/normalization/agent-hook-listener-command-code-transcript.test.ts": 330, "src/shared/agent-hook-listener/normalization/agent-hook-listener-grok-completion.test.ts": 255, @@ -6037,13 +6035,11 @@ "src/shared/agent-hook-listener/transport/agent-hook-listener-transport.test.ts": 326, "src/shared/agent-hook-listener/claude-session-continuation.test.ts": 114, "src/shared/agent-hook-listener/claude-settled-permission-requests.test.ts": 54, - "src/shared/agent-hook-listener/providers/claude-helper-kill.test.ts": 1288, - "src/shared/agent-hook-listener/providers/claude-transcript-semantics.test.ts": 163, - "src/shared/agent-hook-listener/providers/dsh-events.test.ts": 231, - "src/shared/agent-hook-listener/providers/hermes-events.test.ts": 143, - "src/shared/agent-hook-listener/providers/jcode-turn-prompt.test.ts": 122, - "src/shared/agent-hook-listener/providers/muse-events.test.ts": 246, - "src/shared/agent-hook-listener/providers/zcode-events.test.ts": 241, + "src/shared/agent-hook-listener/providers/claude/transcripts/claude-transcript-semantics.test.ts": 163, + "src/shared/agent-hook-listener/providers/dsh/dsh-events.test.ts": 231, + "src/shared/agent-hook-listener/providers/hermes/hermes-events.test.ts": 143, + "src/shared/agent-hook-listener/providers/muse/muse-events.test.ts": 246, + "src/shared/agent-hook-listener/providers/zcode/zcode-events.test.ts": 241, "src/shared/agent-hook-listener/reverse-file-region-scan.test.ts": 51, "src/shared/agent-hook-listener/transcript-reader.test.ts": 109, "src/shared/agent-hook-listener/transport/agent-hook-spool-drain.test.ts": 50, @@ -6165,7 +6161,7 @@ "src/shared/claimed-agent-pty-owner.test.ts": 72, "src/shared/claude-agent-view-launch.test.ts": 55, "src/shared/claude-background-run-isolation.test.ts": 43, - "src/shared/claude-background-task-status.test.ts": 315, + "src/shared/agent-hook-listener/normalization/claude-background-task-status.test.ts": 315, "src/shared/claude-model-list-probe.test.ts": 55, "src/shared/claude-shell-function.test.ts": 220, "src/shared/cloud-service-url.test.ts": 37, @@ -6843,7 +6839,6 @@ "src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.test.ts": 586, "src/wsl-guest/agent-hooks/server/agent-hook-opencode-tui-identity.test.ts": 551, "src/wsl-guest/agent-hooks/status/agent-hook-provider-normalization.test.ts": 489, - "src/wsl-guest/agent-hooks/results/agent-hook-server-claude-owed-notification-expiry.test.ts": 792, "src/wsl-guest/agent-hooks/status/agent-hook-server-claude-session-continuation.test.ts": 462, "src/wsl-guest/agent-hooks/status/agent-hook-server-codex-side-thread.test.ts": 786, "src/wsl-guest/agent-hooks/status/agent-hook-server-codex-subagent-transcript.test.ts": 1589, diff --git a/src/main/agent-hooks/server/server-codex-question-skip.ts b/src/main/agent-hooks/server/server-codex-question-skip.ts index e59e028baaf..0cb25cf6d89 100644 --- a/src/main/agent-hooks/server/server-codex-question-skip.ts +++ b/src/main/agent-hooks/server/server-codex-question-skip.ts @@ -1,4 +1,4 @@ -import { codexRowAfterQuestionSkip } from '../../../shared/agent-hook-listener/providers/codex-question-skip' +import { codexRowAfterQuestionSkip } from '../../../shared/agent-hook-listener/providers/codex/codex-question-skip' import { AGENT_STATUS_STALE_AFTER_MS } from '../../../shared/agent-status/contracts/agent-status-freshness' import type { AgentQuestionAnsweredInferenceRequest } from '../../../shared/agent-status/lifecycle/agent-question-answered-intent' import { CODEX_ASYNC_QUESTION_TOOL } from '../../../shared/codex-async-question-input' diff --git a/src/main/agent-hooks/server/server-foreign-turn-hook.ts b/src/main/agent-hooks/server/server-foreign-turn-hook.ts index 78bca1b8942..c204ec960f4 100644 --- a/src/main/agent-hooks/server/server-foreign-turn-hook.ts +++ b/src/main/agent-hooks/server/server-foreign-turn-hook.ts @@ -1,5 +1,5 @@ import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' -import { rowHoldsOtherCodexThread } from '../../../shared/agent-hook-listener/providers/codex-ephemeral-threads' +import { rowHoldsOtherCodexThread } from '../../../shared/agent-hook-listener/providers/codex/codex-ephemeral-threads' import { isStaleGrokTurnEnd } from './server-grok-status-rules' import type { EnrichedAgentHookEventPayload } from './server-types' diff --git a/src/main/agent-hooks/server/server-hydration.ts b/src/main/agent-hooks/server/server-hydration.ts index ebabcfb7ad3..781d5edf6c6 100644 --- a/src/main/agent-hooks/server/server-hydration.ts +++ b/src/main/agent-hooks/server/server-hydration.ts @@ -5,8 +5,8 @@ import { import { seedClaudeLeadTurnFromPersistedStatus, seedClaudeSubagentRosterFromSnapshots -} from '../../../shared/agent-hook-listener/providers/claude-roster-state' -import { seedCodexStateFromSnapshot } from '../../../shared/agent-hook-listener/providers/codex-state' +} from '../../../shared/agent-hook-listener/providers/claude/lifecycle/claude-roster-state' +import { seedCodexStateFromSnapshot } from '../../../shared/agent-hook-listener/providers/codex/codex-state' import { AGENT_STATUS_PERSISTED_HYDRATION_MODE } from '../../../shared/agent-status/transport/agent-status-legacy-adapter' import { AGENT_STATUS_HYDRATE_MAX_AGE_MS as HYDRATE_MAX_AGE_MS } from '../../../shared/agent-status/contracts/agent-status-freshness' import { LAST_STATUS_FILE_VERSION, STATUS_FLUSH_BUSY_TIMEOUT_MS } from './server-constants' diff --git a/src/main/agent-hooks/server/server-ingest-remote.ts b/src/main/agent-hooks/server/server-ingest-remote.ts index 675a68e5aa1..1c4feee87d3 100644 --- a/src/main/agent-hooks/server/server-ingest-remote.ts +++ b/src/main/agent-hooks/server/server-ingest-remote.ts @@ -16,7 +16,7 @@ import { isClaudeCompactCompletionConsumed, markClaudeCompactCompletionConsumed, resolveLegacyCompactTrigger -} from '../../../shared/agent-hook-listener/providers/claude-compact-completion' +} from '../../../shared/agent-hook-listener/providers/claude/lifecycle/claude-compact-completion' import { launchTokenHash } from '../../../shared/agent-hook-listener/transport/agent-hook-spool' import { parsePaneKey } from '../../../shared/workspace-layout/stable-pane-id' import type { IncomingAgentHookEvent } from './server-types' diff --git a/src/main/agent-hooks/server/server-relay-sessions.ts b/src/main/agent-hooks/server/server-relay-sessions.ts index 29daa5c2837..bb8374a8db7 100644 --- a/src/main/agent-hooks/server/server-relay-sessions.ts +++ b/src/main/agent-hooks/server/server-relay-sessions.ts @@ -1,6 +1,6 @@ import { hasShedSubagentsField } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' import { readAgentPaneRootBinding } from '../../../shared/agent-status/process/agent-process-presence' -import type { CodexRemoteRosterAuthority } from '../../../shared/agent-hook-listener/providers/codex-state' +import type { CodexRemoteRosterAuthority } from '../../../shared/agent-hook-listener/providers/codex/codex-state' import type { ParsedAgentStatusPayload } from '../../../shared/agent-status/contracts/agent-status-types' // One binding per pane a relay asked about; a link restart drops them all. diff --git a/src/main/agent-hooks/server/server-status-application.ts b/src/main/agent-hooks/server/server-status-application.ts index e7a625f10ed..6cf5f15661a 100644 --- a/src/main/agent-hooks/server/server-status-application.ts +++ b/src/main/agent-hooks/server/server-status-application.ts @@ -14,7 +14,7 @@ import type { } from '../../../shared/agent-status/contracts/agent-status-observation' import { AGENT_STATUS_2A_CURRENT_PRODUCER_MODE } from '../../../shared/agent-status/transport/agent-status-legacy-adapter' import { mainAgentState } from '../../../shared/agent-status/lifecycle/main-agent-status' -import { previousCodexTurnContext } from '../../../shared/agent-hook-listener/providers/codex-session-start' +import { previousCodexTurnContext } from '../../../shared/agent-hook-listener/providers/codex/codex-session-start' import { admitLegacyAgentStatus } from '../../../shared/agent-hook-listener/listener-state' import type { EnrichedAgentHookEventPayload } from './server-types' import { agentTypeToPromptSentAgentKind } from './server-status-identity' diff --git a/src/main/agent-hooks/server/server-status-inference.ts b/src/main/agent-hooks/server/server-status-inference.ts index c7352198056..ed31c3a116e 100644 --- a/src/main/agent-hooks/server/server-status-inference.ts +++ b/src/main/agent-hooks/server/server-status-inference.ts @@ -2,7 +2,7 @@ import type { RemoteAgentInterruptDispatch } from '../../../shared/agent-hook-li import { markClaudeLeadTurnDone, clearClaudeAnsweredQuestionWait -} from '../../../shared/agent-hook-listener/providers/claude-roster-state' +} from '../../../shared/agent-hook-listener/providers/claude/lifecycle/claude-roster-state' import { isAgentInterruptInputIntent, isNavigationEscapeIntent, diff --git a/src/main/agent-hooks/server/server-status-retries.ts b/src/main/agent-hooks/server/server-status-retries.ts index c23da129c52..8486042e4e9 100644 --- a/src/main/agent-hooks/server/server-status-retries.ts +++ b/src/main/agent-hooks/server/server-status-retries.ts @@ -1,5 +1,5 @@ -import { ClaudeHelperKillPoller } from '../../../shared/agent-hook-listener/providers/claude-helper-kill-poller' -import { pollCodexTranscriptStatus } from '../../../shared/agent-hook-listener/providers/codex-transcript-poll' +import { ClaudeHelperKillPoller } from '../../../shared/agent-hook-listener/providers/claude/transcripts/claude-helper-kill-poller' +import { pollCodexTranscriptStatus } from '../../../shared/agent-hook-listener/providers/codex/codex-transcript-poll' import { normalizeHookPayload } from '../../../shared/agent-hook-listener/normalization/normalize-hook-payload' import { hasPendingAgentResultText, diff --git a/src/main/agent-hooks/server/server-status-update.ts b/src/main/agent-hooks/server/server-status-update.ts index df37b55c600..8bd55299dfb 100644 --- a/src/main/agent-hooks/server/server-status-update.ts +++ b/src/main/agent-hooks/server/server-status-update.ts @@ -6,16 +6,16 @@ import { needsHookRootEvidence, readHookProcessVerdict } from '../../../shared/agent-hook-listener/presence/agent-hook-owner-presence' -import { markCodexLeadTurnInterrupted } from '../../../shared/agent-hook-listener/providers/codex-state' -import { reconcileCodexStatusEvent } from '../../../shared/agent-hook-listener/providers/codex-session-start' +import { markCodexLeadTurnInterrupted } from '../../../shared/agent-hook-listener/providers/codex/codex-state' +import { reconcileCodexStatusEvent } from '../../../shared/agent-hook-listener/providers/codex/codex-session-start' import { resolveAgentStatusIdentity, shouldSuppressInheritedTerminalStatus } from '../../../shared/agent-status/identity/agent-status-identity' import type { EnrichedAgentHookEventPayload, IncomingAgentHookEvent } from './server-types' import type { AgentStatusObservationOrigin } from '../../../shared/agent-status/contracts/agent-status-observation' -import { ClaudeOwedNotificationExpiryTimers } from '../../../shared/agent-hook-listener/providers/claude-owed-notification-expiry-timers' -import { setClaudeMainAgentTurnState } from '../../../shared/agent-hook-listener/providers/claude-roster-state' +import { ClaudeOwedNotificationExpiryTimers } from '../../../shared/agent-hook-listener/providers/claude/background-tasks/claude-owed-notification-expiry-timers' +import { setClaudeMainAgentTurnState } from '../../../shared/agent-hook-listener/providers/claude/lifecycle/claude-roster-state' import { attachClaudePermissionToolUseId, pairedClaudeNonAgentWork, diff --git a/src/main/agent-hooks/server/server-types.ts b/src/main/agent-hooks/server/server-types.ts index 4d69de23568..f538246b821 100644 --- a/src/main/agent-hooks/server/server-types.ts +++ b/src/main/agent-hooks/server/server-types.ts @@ -8,7 +8,7 @@ import type { AgentStatusState } from '../../../shared/agent-status/contracts/ag import type { AgentStatusObservation } from '../../../shared/agent-status/contracts/agent-status-observation' import type { AgentKind } from '../../../shared/telemetry-events' import type { LegacyPaneKeyAliasEntry } from '../../../shared/persisted-state-types' -import type { CodexRemoteRosterAuthority } from '../../../shared/agent-hook-listener/providers/codex-state' +import type { CodexRemoteRosterAuthority } from '../../../shared/agent-hook-listener/providers/codex/codex-state' import type { AgentHookSource } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' export type HookServerStartOptions = { diff --git a/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-recovery.test.ts b/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-recovery.test.ts deleted file mode 100644 index 1311a436db7..00000000000 --- a/src/main/agent-hooks/server/testing/interruptions/server-claude-helper-recovery.test.ts +++ /dev/null @@ -1,463 +0,0 @@ -import { appendFileSync } from 'node:fs' -import { appendFile, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { createServer } from 'node:http' -import { join } from 'node:path' -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { RelayAgentHookServer } from '../../../../../wsl-guest/agent-hooks/server/agent-hook-server' -import { AgentHookServer } from '../../agent-hook-server' -import { buildBody, PANE, postHookEvent } from '../server.test-fixtures' -import { ClaudeHelperKillPoller } from '../../../../../shared/agent-hook-listener/providers/claude-helper-kill-poller' -import '../../../../../shared/agent-hook-listener/providers/claude-transcript-decoder' -import '../../../../../shared/agent-hook-listener/transcripts/agent-transcript-poll-scheduler' - -// Complete captured lifecycle records, with runtime replay seeded by real persisted HTTP state. -vi.mock('../../../../telemetry/client', () => ({ track: vi.fn() })) -vi.mock('../../../../telemetry/cohort-classifier', () => ({ - getCohortAtEmit: () => ({ nth_repo_added: 2 }) -})) -const servers: { stop: () => void }[] = [] -const folders: string[] = [] -const realSetTimeout = globalThis.setTimeout -beforeEach(() => { - vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'performance'] }) -}) -afterEach(async () => { - for (const server of servers.splice(0)) { - server.stop() - } - vi.useRealTimers() - // Only exact test-created mkdtemp paths; no external state is reachable. - for (const folder of folders.splice(0)) { - await rm(folder, { recursive: true, force: true }) - } - vi.restoreAllMocks() -}) -const lines = (records: Record[]) => - records.map((r) => `${JSON.stringify(r)}\n`).join('') -// ast-grep-ignore: test-no-real-time-wait -- Real elapsed gaps separate hook R, provider K and restart S. -const pause = (ms: number) => new Promise((resolve) => realSetTimeout(resolve, ms)) -// Transcript polls run on the fake clock while transcript I/O and wall time stay real. -const settle = (assertion: () => void) => - vi.waitFor( - () => { - vi.advanceTimersByTime(250) - assertion() - }, - { timeout: 3000, interval: 10 } - ) -async function load(stream: string): Promise[]> { - return ( - await readFile( - new URL( - `../../../../../shared/__fixtures__/claude-helper-kill-2.1.310-${stream}.jsonl`, - import.meta.url - ), - 'utf8' - ) - ) - .trim() - .split('\n') - .map((line) => JSON.parse(line)) -} -const hooks = [ - { hook_event_name: 'UserPromptSubmit', prompt: 'work' }, - { hook_event_name: 'SubagentStart', agent_id: 'a123' }, - { - hook_event_name: 'Stop', - last_assistant_message: 'READY', - background_tasks: [ - { id: 'a123', type: 'subagent', status: 'running' }, - { id: 'shell', type: 'shell', status: 'running' } - ] - } -] - -async function setup( - kind: string, - when: string, - options: { - cutoff?: number - killAt?: number - throwOnce?: boolean - disabled?: boolean - beforeStart?: (transcript: string) => void - retryBind?: boolean - busyPort?: number - stopBeforeBind?: boolean - advanceBeforeBind?: () => void - retired?: boolean - } = {} -) { - const inputHooks = options.throwOnce ? [hooks[1]] : hooks - const parent = await load('parent'), - child = await load('child') - let oldKill = parent.find((r) => r.subtype === 'agents_killed')! - const folder = await mkdtemp(join(tmpdir(), 'pr35930-c5-bounded-')) - folders.push(folder) - const transcript = join(folder, 'session.jsonl') - await writeFile(transcript, lines(parent.filter((r) => r !== oldKill))) - await mkdir(join(folder, 'session', 'subagents'), { recursive: true }) - await writeFile(join(folder, 'session', 'subagents', 'agent-a123.jsonl'), lines(child)) - const body = (payload: Record) => - buildBody( - { session_id: 'session', transcript_path: transcript, ...payload }, - { launchToken: 'same-owner' } - ) - const endpointDir = - kind === 'desktop-spool' ? join(folder, 'agent-hooks', 'c5-bounded') : join(folder, 'endpoint') - const startOptions = { env: 'production', userDataPath: folder, endpointNamespace: 'c5-bounded' } - // Desktop restart is seeded by actual HTTP admission/persistence, not injected snapshot roster. - if (kind === 'desktop-spool') { - const previous = new AgentHookServer() - servers.push(previous) - await previous.start(startOptions) - for (const h of inputHooks) { - expect((await postHookEvent(previous, body(h))).status).toBe(204) - } - expect(previous.getStatusSnapshotForPane(PANE)[0]?.subagents?.[0]?.state).toBe('working') - previous.flushStatusPersistSync() - previous.stop() - } - if (kind.endsWith('spool')) { - await mkdir(join(endpointDir, 'spool'), { recursive: true }) - // Actual failed-hook emission while stopped: use the shell writer's second floor. - const receivedAt = options.cutoff ?? Math.floor(Date.now() / 1000) * 1000 - await writeFile( - join(endpointDir, 'spool', 'claude.jsonl'), - lines( - inputHooks.map((h) => ({ - ...body(h), - source: 'claude', - launchToken: 'same-owner', - receivedAt - })) - ) - ) - } - if (when === 'before-start') { - // ast-grep-ignore: test-no-real-time-wait -- Provider K must follow failed-hook R with real I/O. - await pause(5) - oldKill = { ...oldKill, timestamp: new Date(options.killAt ?? Date.now()).toISOString() } - await appendFile(transcript, lines([oldKill])) - // ast-grep-ignore: test-no-real-time-wait -- Restart S must follow provider K without mocking host time. - await pause(20) - } - options.beforeStart?.(transcript) - const desktop = new AgentHookServer() - servers.push(desktop) - if (options.retired) { - desktop.dropStatusEntriesByTabPrefix('tab-1') - } - let prebind = false, - threw = false - const beforeBind = () => { - if (when === 'accepted-before-bind' && !prebind) { - prebind = true - appendFileSync( - transcript, - lines([{ ...oldKill, timestamp: new Date(Date.now()).toISOString() }]) - ) - options.advanceBeforeBind?.() - if (options.stopBeforeBind) { - if (kind.startsWith('desktop')) { - desktop.stop() - } else { - relay.stop() - } - } - } - } - desktop.setListener((event) => { - if (options.throwOnce && !threw && event.hookEventName === 'SubagentStart') { - threw = true - appendFileSync( - transcript, - lines([{ ...oldKill, timestamp: new Date(Date.now()).toISOString() }]) - ) - throw new Error('test outward listener') - } - if (event.hookEventName !== 'Stop') { - return - } - if (kind === 'desktop-spool') { - if (!options.disabled) { - expect(desktop['server']).toBe(null) - } - beforeBind() - } - }) - const relay = new RelayAgentHookServer({ - endpointDir, - token: 'c5-bounded', - preferredPort: options.retryBind ? -1 : options.busyPort, - isPaneSurfaceRetired: () => options.retired === true, - forward: (e) => { - desktop.ingestRemote(e, 'connection') - if (kind === 'relay-spool' && e.hookEventName === 'Stop') { - beforeBind() - } - } - }) - servers.push(relay) - const start = () => - kind.startsWith('desktop') - ? desktop.start({ ...startOptions, statusHooksEnabled: !options.disabled }) - : relay.start({ publishEndpoint: false }) - if (options.retryBind || (options.throwOnce && kind === 'desktop-spool')) { - await expect(start()).rejects.toThrow() - const observer = kind.startsWith('desktop') - ? desktop['claudeHelperKills'] - : relay['retryScheduler']['claudeHelperKills'] - const retained = observer['observations'].get(PANE)! - expect(retained.parent.offset).toBe(0) - expect(observer['scheduler']['entries'].size).toBe(0) - if (options.retryBind) { - Reflect.set(relay, 'preferredPort', 0) - } - await start() - expect(observer['observations'].get(PANE)).toBe(retained) - } else { - await start() - } - const post = async (payload: Record, extra: Record = {}) => { - if (kind.startsWith('desktop')) { - expect((await postHookEvent(desktop, { ...body(payload), ...extra })).status).toBe(204) - } else { - const { port, token } = relay.getCoordinates() - expect( - ( - await fetch(`http://127.0.0.1:${port}/hook/claude`, { - method: 'POST', - headers: { 'Content-Type': 'application/json', 'X-Orca-Agent-Hook-Token': token }, - body: JSON.stringify({ ...body(payload), ...extra }) - }) - ).status - ).toBe(204) - } - } - if (!kind.endsWith('spool')) { - for (const h of inputHooks) { - await post(h) - } - } - const poller = kind.startsWith('desktop') - ? desktop['claudeHelperKills'] - : relay['retryScheduler']['claudeHelperKills'] - const entries = poller['observations'] - return { - desktop, - relay, - poller, - post, - transcript, - oldKill, - entries, - prebind, - threw, - row: () => desktop.getStatusSnapshotForPane(PANE)[0] - } -} - -it.each([ - ['relay-spool', 'before-start'], - ['relay-spool', 'accepted-before-bind'], - ['desktop-spool', 'before-start'], - ['desktop-spool', 'accepted-before-bind'] -])('delivers the first kill through %s %s, then wakes retained evidence', async (kind, when) => { - const clock = - when === 'accepted-before-bind' ? vi.spyOn(Date, 'now').mockReturnValue(Date.now()) : undefined - const acceptedAt = Date.now() - const p = await setup(kind, when, { - advanceBeforeBind: () => clock?.mockReturnValue(acceptedAt + 10) - }) - const before = p.row()! - await settle(() => expect(p.row()?.subagents ?? []).toEqual([])) - expect(p.row()?.workingMode).toBe('monitoring') - expect(p.row()?.mainAgent).toEqual(before.mainAgent) - expect(p.row()?.lastAssistantMessage).toBe('READY') - expect(p.row()?.turnEndedAt).toBe(before.turnEndedAt) - const owner = p.entries.get(PANE)! - const offset = owner.parent.offset - // A delayed hook is the legitimate parked wake. No second kill is appended. - await p.post(hooks[1]) - await settle(() => expect(p.row()?.subagents ?? []).toEqual([])) - expect(p.entries.get(PANE)).toBe(owner) - expect(owner.parent.offset).toBe(offset) - if (when === 'accepted-before-bind') { - expect(p.prebind).toBe(true) - } -}) - -it.each(['desktop-http', 'relay-http', 'desktop-spool', 'relay-spool'])( - 'retains accepted observation when %s fanout throws', - async (kind) => { - const p = await setup(kind, 'accepted-before-bind', { throwOnce: true }) - expect(p.threw).toBe(true) - expect(p.entries.size).toBe(1) - await settle(() => expect(p.row()?.subagents ?? []).toEqual([])) - expect(p.row()?.state).toBe('working') // Unknown root remains live; no parent hook was supplied. - } -) - -it.each(['relay-spool', 'desktop-spool'])( - 'keeps history and genuine within-second generations on %s', - async (kind) => { - const R = Math.floor(Date.now() / 1000) * 1000 - const p = await setup(kind, 'before-start', { cutoff: R, killAt: R - 1001 }) - await settle(() => expect(p.entries.get(PANE)?.parent.offset).toBeGreaterThan(0)) - expect(p.entries.get(PANE)?.killedAt).toBeUndefined() - expect(p.row()?.subagents?.[0]?.state).toBe('working') - const child = join(p.transcript.slice(0, -6), 'subagents', 'agent-a123.jsonl') - await appendFile( - child, - lines([ - { - type: 'user', - timestamp: new Date(R + 520).toISOString(), - message: { content: [{ type: 'text', text: 'Resume the work' }] } - } - ]) - ) - await appendFile( - p.transcript, - lines([{ ...p.oldKill, timestamp: new Date(R + 500).toISOString() }]) - ) - await settle(() => expect(p.entries.get(PANE)?.killedAt).toBe(R + 500)) - expect(p.row()?.subagents?.[0]?.state).toBe('working') - // R's precision admits K, but only provider generation decides whether this work ends. - await appendFile( - p.transcript, - lines([{ ...p.oldKill, timestamp: new Date(R + 600).toISOString() }]) - ) - await settle(() => expect(p.row()?.subagents ?? []).toEqual([])) - } -) - -it('initialization of an already-listening desktop activates parked runtime spool', async () => { - const p = await setup('desktop-spool', 'before-start', { disabled: true }) - expect(p.entries.size).toBe(0) - p.desktop.setStatusHooksEnabled(true) - await settle(() => { - expect(p.entries.size).toBe(1) - expect(p.row()?.subagents ?? []).toEqual([]) - expect(p.row()?.workingMode).toBe('monitoring') - }) -}) - -it('caps future spool cutoffs and keeps HTTP cutoff provenance private', async () => { - const S = Date.now() - const p = await setup('relay-spool', 'before-start', { cutoff: S + 60000 }) - expect(p.entries.get(PANE)?.observedAt).toBeLessThan(S + 60000) - await appendFile(p.transcript, lines([{ ...p.oldKill, timestamp: new Date().toISOString() }])) - await settle(() => expect(p.row()?.subagents ?? []).toEqual([])) - const q = await setup('desktop-http', 'after-start') - await q.post(hooks[2], { receivedAt: 0, hookReceivedAt: 0 }) - expect(q.entries.get(PANE)?.observedAt).toBeGreaterThan(S - 1000) - await appendFile( - q.transcript, - lines([{ ...q.oldKill, timestamp: new Date(S - 10000).toISOString() }]) - ) - await settle(() => expect(q.entries.get(PANE)?.parent.offset).toBeGreaterThan(0)) - expect(q.row()?.subagents?.[0]?.state).toBe('working') -}) - -it('a real failed bind parks without resources and explicit retry delivers its first fact', async () => { - const p = await setup('relay-spool', 'before-start', { retryBind: true }) - await settle(() => expect(p.row()?.subagents ?? []).toEqual([])) - expect(p.row()?.workingMode).toBe('monitoring') -}) - -it('a held local child permission cannot reset the admitted owner', async () => { - const p = await setup('desktop-http', 'after-start') - await p.post({ - hook_event_name: 'PermissionRequest', - agent_id: 'a123', - tool_name: 'Bash', - tool_use_id: 'approval', - tool_input: { command: 'false' } - }) - const owner = p.entries.get(PANE) - const capture = vi.spyOn(ClaudeHelperKillPoller.prototype, 'schedule') - await p.post({ - hook_event_name: 'PreToolUse', - tool_name: 'Bash', - tool_use_id: 'unrelated', - tool_input: { command: 'true' } - }) - expect(p.row()?.state).toBe('waiting') - expect(capture).not.toHaveBeenCalled() - expect(p.entries.get(PANE)).toBe(owner) - await appendFile(p.transcript, lines([{ ...p.oldKill, timestamp: new Date().toISOString() }])) - await settle(() => expect(p.row()?.subagents ?? []).toEqual([])) -}) - -it('preferred-port fallback activates the original admitted observation', async () => { - const busy = createServer() - await new Promise((resolve) => busy.listen(0, '127.0.0.1', resolve)) - try { - const address = busy.address() - if (!address || typeof address === 'string') { - throw new Error('missing listener address') - } - const p = await setup('relay-spool', 'before-start', { busyPort: address.port }) - expect(p.relay.usedPortFallback).toBe(true) - await settle(() => expect(p.row()?.subagents ?? []).toEqual([])) - expect(p.row()?.workingMode).toBe('monitoring') - } finally { - await new Promise((resolve) => busy.close(() => resolve())) - } -}) - -it.each(['desktop-http', 'relay-http'])( - 'cached SessionStart activation on %s keeps its boundary and first later kill', - async (kind) => { - const p = await setup(kind, 'after-start') - const S = Date.now() - const clock = vi.spyOn(Date, 'now').mockReturnValue(S) - await p.post({ hook_event_name: 'SessionStart', source: 'resume' }) - const owner = p.entries.get(PANE)! - expect(owner.observedAt).toBe(S) - clock.mockReturnValue(S + 10) - p.poller.activate() - p.poller.activate() - expect(p.entries.get(PANE)).toBe(owner) - expect(owner.observedAt).toBe(S) - await p.post(hooks[1]) - await appendFile( - p.transcript, - lines([{ ...p.oldKill, timestamp: new Date(S + 1).toISOString() }]) - ) - await settle(() => expect(p.row()?.subagents ?? []).toEqual([])) - } -) - -it('reentrant mirror and OSC writes cannot borrow local hook admission', async () => { - const p = await setup('desktop-http', 'after-start') - const other = PANE.replace('111111111111', '222222222222') - let reentered = false - p.desktop.subscribeStatusRowMutations(() => { - if (reentered) { - return - } - reentered = true - p.desktop.ingestRemote( - { - paneKey: other, - providerSession: { key: 'session_id', id: 'other', transcriptPath: p.transcript }, - payload: { agentType: 'claude', state: 'working' } - }, - 'other-host' - ) - p.desktop.ingestTerminalStatus({ - paneKey: other, - payload: { agentType: 'claude', state: 'working', prompt: '' } - }) - }) - await p.post({ hook_event_name: 'UserPromptSubmit', prompt: 'another real turn' }) - expect(reentered).toBe(true) - expect(p.entries.size).toBe(1) - expect(p.desktop.getStatusSnapshotForPane(other)[0]?.state).toBe('working') - await appendFile(p.transcript, lines([{ ...p.oldKill, timestamp: new Date().toISOString() }])) - await settle(() => expect(p.row()?.subagents ?? []).toEqual([])) -}) diff --git a/src/main/agent-hooks/server/testing/task-wakeup/server-claude-owed-notification-expiry.test.ts b/src/main/agent-hooks/server/testing/task-wakeup/server-claude-owed-notification-expiry.test.ts deleted file mode 100644 index 0c9aaed459f..00000000000 --- a/src/main/agent-hooks/server/testing/task-wakeup/server-claude-owed-notification-expiry.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -// No hook fires at lease expiry, so the server must release owed-notification holds itself. -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { CLAUDE_OWED_TASK_NOTIFICATION_LEASE_MS } from '../../../../../shared/agent-hook-listener/providers/claude-owed-task-notifications' -import { AgentHookServer, _internals } from '../../agent-hook-server' -import { buildBody, PANE, postHookEvent, RUNNING_SHELL } from '../server.test-fixtures' - -vi.mock('../../../../telemetry/client', () => ({ track: vi.fn() })) -vi.mock('../../../../telemetry/cohort-classifier', () => ({ - getCohortAtEmit: vi.fn(() => ({ nth_repo_added: 2 })) -})) -// The fixture's owner process is fictional; the pane's owning Claude is still running. -vi.mock('../../../../../shared/agent-status/process/agent-process-presence-probe', () => ({ - probeAgentProcessPresence: vi.fn(async () => 'live') -})) - -beforeEach(() => { - _internals.resetCachesForTests() - // Real loopback POSTs need fake time to advance. - vi.useFakeTimers({ - shouldAdvanceTime: true, - toFake: ['setTimeout', 'clearTimeout', 'Date', 'performance'] - }) -}) - -afterEach(() => { - vi.useRealTimers() -}) - -describe('Claude owed task notification expiry on the hook server', () => { - it('settles a pane whose launched shell vanished without a notification', async () => { - const server = new AgentHookServer() - await server.start({ env: 'production' }) - const states: string[] = [] - server.setListener((event) => states.push(event.payload.state)) - const post = (payload: Record) => - postHookEvent(server, buildBody({ session_id: 'session-1', ...payload })) - try { - await post({ hook_event_name: 'UserPromptSubmit', prompt: 'start the dev server' }) - await post({ - hook_event_name: 'PostToolUse', - tool_name: 'Bash', - tool_response: { backgroundTaskId: RUNNING_SHELL.id } - }) - await post({ hook_event_name: 'Stop', background_tasks: [RUNNING_SHELL] }) - await post({ hook_event_name: 'UserPromptSubmit', prompt: 'thanks' }) - await post({ hook_event_name: 'Stop', background_tasks: [] }) - expect(server.getStatusSnapshotForPane(PANE)[0]?.state).toBe('working') - - vi.advanceTimersByTime(CLAUDE_OWED_TASK_NOTIFICATION_LEASE_MS) - - expect(server.getStatusSnapshotForPane(PANE)[0]?.state).toBe('done') - expect(states.at(-1)).toBe('done') - } finally { - server.stop() - } - }) - - it('drops what was owed when the owning Claude process exits', async () => { - const server = await heldByAnOwedShell() - try { - await server.post({ hook_event_name: 'SessionEnd', reason: 'prompt_input_exit' }, OWNER) - vi.advanceTimersByTime(CLAUDE_OWED_TASK_NOTIFICATION_LEASE_MS) - - expect(server.row()).toMatchObject({ providerSessionOnly: true }) - } finally { - server.stop() - } - }) - - it('keeps what is owed when a nested Claude in the same pane exits', async () => { - const server = await heldByAnOwedShell() - try { - await server.post({ hook_event_name: 'SessionEnd', reason: 'prompt_input_exit' }, 5151) - expect(server.row()).toMatchObject({ state: 'working' }) - expect(server.row().providerSessionOnly).toBeUndefined() - - vi.advanceTimersByTime(CLAUDE_OWED_TASK_NOTIFICATION_LEASE_MS) - expect(server.row()).toMatchObject({ state: 'done' }) - } finally { - server.stop() - } - }) -}) - -const OWNER = 4242 - -async function heldByAnOwedShell() { - const server = new AgentHookServer() - await server.start({ env: 'production' }) - const agentProcess = (pid: number) => - JSON.stringify({ pid, platform: 'darwin', startTime: 'Fri Oct 2 12:00:00 2026' }) - const post = (payload: Record, pid = OWNER) => - postHookEvent( - server, - buildBody({ session_id: 'session-1', ...payload }, { agentProcess: agentProcess(pid) }) - ) - await post({ hook_event_name: 'UserPromptSubmit', prompt: 'start the dev server' }) - await post({ - hook_event_name: 'PostToolUse', - tool_name: 'Bash', - tool_response: { backgroundTaskId: RUNNING_SHELL.id } - }) - await post({ hook_event_name: 'Stop', background_tasks: [RUNNING_SHELL] }) - await post({ hook_event_name: 'UserPromptSubmit', prompt: 'thanks' }) - await post({ hook_event_name: 'Stop', background_tasks: [] }) - const row = () => { - const entry = server.getStatusSnapshotForPane(PANE)[0] - if (!entry) { - throw new Error('the pane has no row') - } - return entry - } - return { post, row, stop: () => server.stop() } -} diff --git a/src/main/agent-hooks/tool-events/agent-tool-call-reader.ts b/src/main/agent-hooks/tool-events/agent-tool-call-reader.ts index 3527e45afc6..20d680f5a4c 100644 --- a/src/main/agent-hooks/tool-events/agent-tool-call-reader.ts +++ b/src/main/agent-hooks/tool-events/agent-tool-call-reader.ts @@ -8,7 +8,7 @@ import { normalizeCopilotEventName, readCopilotToolCall, resolveCopilotEventName -} from '../../../shared/agent-hook-listener/providers/copilot-tool-fields' +} from '../../../shared/agent-hook-listener/providers/copilot/copilot-tool-fields' import type { agentToolAfterHookPayloadSchema } from '../../../shared/plugins/events/plugin-hook-payloads' const BEFORE_EVENTS = new Set([ diff --git a/src/main/runtime/terminal/wait/tui-idle-hook-lane-runtime.test.ts b/src/main/runtime/terminal/wait/tui-idle-hook-lane-runtime.test.ts index fc14365be28..1a30a96daaf 100644 --- a/src/main/runtime/terminal/wait/tui-idle-hook-lane-runtime.test.ts +++ b/src/main/runtime/terminal/wait/tui-idle-hook-lane-runtime.test.ts @@ -1,13 +1,6 @@ -import { readFileSync } from 'node:fs' -import { join } from 'node:path' import { describe, expect, it, vi, afterEach } from 'vitest' -import { - createTranscriptPane, - TRANSCRIPT_PANE_PTY_ID, - waitForTranscriptIdle -} from '../../orca-runtime-tests/agent-transcript-pane-test-harness' -import { createHookListenerState } from '../../../../shared/agent-hook-listener/listener-state' -import { normalizeHermesEvent } from '../../../../shared/agent-hook-listener/providers/hermes-events' +import { createTranscriptPane } from '../../orca-runtime-tests/agent-transcript-pane-test-harness' + import type { AgentStatusIpcPayload } from '../../../../shared/agent-status/contracts/agent-status-ipc-payload' import { makePaneKey } from '../../../../shared/workspace-layout/stable-pane-id' import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent' @@ -20,182 +13,6 @@ vi.mock('electron', () => ({ app: { getPath: vi.fn(() => '/tmp') } })) -describe('Hermes captured hook readiness', () => { - const captured = readFileSync( - join(__dirname, '../../__fixtures__', 'hermes-tui-ready.txt'), - 'utf8' - ) - - /** Run managed Hermes payloads through the real normalizer so event-mapping regressions fail at this runtime boundary too. */ - const status = (eventName: string, payload: Record = {}): string => { - const parsed = normalizeHermesEvent(createHookListenerState(), eventName, '', 'pane-1', payload) - expect(parsed, `hermes plugin event ${eventName} must normalize to a status`).not.toBeNull() - return `\x1b]9999;${JSON.stringify(parsed)}\x07` - } - - const readyPane = async () => { - const pane = await createTranscriptPane({ - paneTitle: 'Hermes Agent', - foregroundProcess: 'hermes', - launchAgent: 'hermes', - size: { cols: 120, rows: 31 }, - data: captured - }) - await pane.runtime.readTerminal(pane.handle, { screen: true }) - return pane - } - - describe('Hermes TUI readiness from a captured PTY', () => { - it('settles tui-idle on a ready composer after the session-boundary hook', async () => { - const { runtime, handle } = await readyPane() - // What Orca's own managed plugin sends for `on_session_start`. - runtime.onPtyData( - TRANSCRIPT_PANE_PTY_ID, - status('on_session_start', { session_id: 's1' }), - Date.now() - ) - - const read = vi.spyOn(runtime, 'readTerminal') - await expect( - runtime.waitForTerminal(handle, { condition: 'tui-idle', timeoutMs: 2_000 }) - ).resolves.toMatchObject({ satisfied: true }) - // The retained screen decides without a provider screen round trip. - expect(read.mock.calls.filter(([, options]) => options?.screen === true)).toEqual([]) - }, 5_000) - - it('settles through a leaf handle whose retained tail never showed the ready screen', async () => { - const { runtime } = await readyPane() - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Access the real synced leaf and handle issuer for this route regression. - const internals = runtime as unknown as { - leaves: Map - issueHandle: (leaf: unknown) => string - } - const leaf = internals.leaves.values().next().value - expect(leaf).toBeDefined() - expect([...leaf!.tailBuffer, leaf!.tailPartialLine].join('\n')).not.toMatch(/\bready\b/i) - runtime.onPtyData( - TRANSCRIPT_PANE_PTY_ID, - status('on_session_start', { session_id: 's1' }), - Date.now() - ) - - await expect( - runtime.waitForTerminal(internals.issueHandle(leaf), { - condition: 'tui-idle', - timeoutMs: 2_000 - }) - ).resolves.toMatchObject({ satisfied: true }) - }, 5_000) - - it('does not settle while a turn the user started is still running', async () => { - const { runtime, handle } = await readyPane() - runtime.onPtyData( - TRANSCRIPT_PANE_PTY_ID, - status('on_session_start', { session_id: 's1' }), - Date.now() - ) - // `pre_llm_call` — the first event that means a turn actually began. - runtime.onPtyData( - TRANSCRIPT_PANE_PTY_ID, - status('pre_llm_call', { user_message: 'hi' }), - Date.now() - ) - - await expect( - runtime.waitForTerminal(handle, { condition: 'tui-idle', timeoutMs: 550 }) - ).rejects.toThrow('timeout') - }, 3_000) - - it('does not settle on a turn-end done row without a ready screen', async () => { - const { runtime, handle } = await createTranscriptPane({ - paneTitle: 'Hermes Agent', - foregroundProcess: 'hermes', - launchAgent: 'hermes', - data: '' - }) - // `post_llm_call` lands `done` without the boundary flag; the #6011 rule still applies. - runtime.onPtyData( - TRANSCRIPT_PANE_PTY_ID, - status('post_llm_call', { session_id: 's1' }), - Date.now() - ) - - await expect( - runtime.waitForTerminal(handle, { condition: 'tui-idle', timeoutMs: 550 }) - ).rejects.toThrow('timeout') - }, 3_000) - }) -}) - -describe('Claude indexed task wakeup readiness', () => { - const PANE = 'tab-1:11111111-1111-4111-8111-111111111111' - const ready = readFileSync( - join(import.meta.dirname, '../../__fixtures__/claude-ready-task-wakeup.txt'), - 'utf8' - ) - - function owedRow(paneKey: string): AgentStatusIpcPayload { - const now = Date.now() - return { - paneKey, - state: 'working', - mainAgent: { state: 'done', stateStartedAt: now }, - agentType: 'claude', - prompt: '', - connectionId: null, - launchToken: 'transcript-launch', - claudeTaskWakeupPending: 'notification', - providerSession: { key: 'session_id', id: 'session-a' }, - observation: { - origin: 'hook', - authorityId: 'host-a', - incarnation: 1, - revision: 1, - observedAt: now - }, - receivedAt: now, - stateStartedAt: now - } - } - - afterEach(() => vi.useRealTimers()) - - describe('Claude readiness indexed status reads', () => { - it.each([true, false])( - 'reads only its pane with 512 unrelated agents, including an empty index: pending=%s', - async (pending) => { - const unrelated = Array.from({ length: 512 }, (_, index) => owedRow(`other:${index}`)) - const row = owedRow(PANE) - const full = vi.fn(() => [row, ...unrelated]) - const indexed = vi.fn((paneKey: string) => (pending && paneKey === PANE ? [row] : [])) - const pane = await createTranscriptPane( - { - paneTitle: 'Terminal', - foregroundProcess: 'claude', - data: ready, - launchAgent: 'claude' - }, - { getAgentStatusSnapshot: full, getAgentStatusSnapshotForPane: indexed } - ) - row.receivedAt = Date.now() - full.mockClear() - indexed.mockClear() - try { - const waiting = waitForTranscriptIdle(pane, 2_000) - await (pending - ? expect(waiting).rejects.toThrow(/timeout/) - : expect(waiting).resolves.toMatchObject({ satisfied: true })) - expect(indexed).toHaveBeenCalled() - expect(new Set(indexed.mock.calls.map(([paneKey]) => paneKey))).toEqual(new Set([PANE])) - expect(full).not.toHaveBeenCalled() - } finally { - pane.runtime.onPtyExit(TRANSCRIPT_PANE_PTY_ID, 0) - } - } - ) - }) -}) - describe('Canonical hook status readiness', () => { // The harness pane's identity (agent-transcript-pane-test-harness.ts). const PANE_KEY = makePaneKey('tab-1', '11111111-1111-4111-8111-111111111111') @@ -213,14 +30,7 @@ describe('Canonical hook status readiness', () => { '' ].join('\r\n') // OpenCode 1.18's question tool, which paints no dialog wording the blocked layer knows. - const OPENCODE_QUESTION = [ - ' ┃ Which color do you prefer?', - ' ┃ 1. Red', - ' ┃ 2. Blue', - ' ┃ 3. Type your own answer', - ' ┃ ↑↓ select enter submit esc dismiss', - '' - ].join('\r\n') + // Shorter than the 2 s poll, so only the synchronous verdict can settle a wait. const WAIT_MS = 150 @@ -280,63 +90,7 @@ describe('Canonical hook status readiness', () => { ).toBe('ready') }) - it('ignores the row the PTY id held before a respawn', async () => { - const before = Date.now() - 1000 - const rows = (): AgentStatusIpcPayload[] => [ - row({ receivedAt: before, stateStartedAt: before }) - ] - expect(await waitOutcome({ rows })).toBe('ready') - expect( - await waitOutcome({ - rows, - afterCreate: (runtime) => - runtime.synchronizePtyOutputSequenceFromProvider(TRANSCRIPT_PANE_PTY_ID, { - value: 0, - generation: 'reset' - }) - }) - ).toBe('timeout') - }) - // Input starts a possible new turn, including typed agent restarts; prior done hooks cannot settle it. - it.each([ - ['a prompt Orca sent', 'next task\r'], - ['the user restarting the agent in the same shell', 'pi\r'] - ])('reads no done from before %s', async (_label, input) => { - const before = Date.now() - 1000 - const rows = (): AgentStatusIpcPayload[] => [ - row({ receivedAt: before, stateStartedAt: before }) - ] - const typeAt = (at: number) => (runtime: OrcaRuntimeService) => { - runtime.terminalRunFacts.recordInput(TRANSCRIPT_PANE_PTY_ID, 'driving', input, at) - } - expect(await waitOutcome({ rows, afterCreate: typeAt(before - 1) })).toBe('ready') - expect(await waitOutcome({ rows, afterCreate: typeAt(before + 1) })).toBe('timeout') - }) - - it('reads no done from before a prompt sent just ahead of an adoption of the running agent', async () => { - const before = Date.now() - 1000 - const rows = (): AgentStatusIpcPayload[] => [ - row({ receivedAt: before, stateStartedAt: before }) - ] - expect( - await waitOutcome({ - rows, - afterCreate: (runtime) => { - runtime.terminalRunFacts.recordInput( - TRANSCRIPT_PANE_PTY_ID, - 'driving', - 'next\r', - before + 1 - ) - runtime.noteTerminalSpawnCommit({ - id: TRANSCRIPT_PANE_PTY_ID, - agentSessionEnsure: { disposition: 'adopted' } - }) - } - }) - ).toBe('timeout') - }) // Pi brackets each message in OSC 133 zones itself, so a command marker is no process boundary. it('keeps the row while the agent paints its own shell-integration markers', async () => { @@ -366,22 +120,5 @@ describe('Canonical hook status readiness', () => { }) ).toBe('ready') }) - - it('blocks on an open question the hook reports, and settles once it is answered', async () => { - const options = { launchAgent: 'opencode' as const, data: OPENCODE_QUESTION } - expect(await waitOutcome({ ...options, rows: () => [] })).toBe('timeout') - expect( - await waitOutcome({ - ...options, - rows: () => [row({ agentType: 'opencode', state: 'waiting', receivedAt: Date.now() + 1 })] - }) - ).toBe('blocked:agent-interactive-prompt') - expect( - await waitOutcome({ - ...options, - rows: () => [row({ agentType: 'opencode', receivedAt: Date.now() + 1 })] - }) - ).toBe('ready') - }) }) }) diff --git a/src/shared/agent-hook-listener/claude-tool-request-identity.ts b/src/shared/agent-hook-listener/claude-tool-request-identity.ts index 9ac8daea5eb..50a8062db1e 100644 --- a/src/shared/agent-hook-listener/claude-tool-request-identity.ts +++ b/src/shared/agent-hook-listener/claude-tool-request-identity.ts @@ -5,7 +5,7 @@ import { deriveToolInputPreview, readString } from './tool-input-preview' import { readClaudeBatchSettledRequests } from './claude-settled-permission-requests' import { extractPromptText } from './prompt-fields' import { hasExplicitUserPrompt } from './provider-event-routing' -import { CLAUDE_NEW_SESSION_START_SOURCES } from './providers/claude-session-start-sources' +import { CLAUDE_NEW_SESSION_START_SOURCES } from './providers/claude/lifecycle/claude-session-start-sources' /** The fields that tie a Claude PermissionRequest to the PreToolUse it is asking about. */ export type ClaudeToolRequestFields = { diff --git a/src/shared/agent-hook-listener/grok-result-discovery.ts b/src/shared/agent-hook-listener/grok-result-discovery.ts index 7d13902a460..ebc1b8c30f7 100644 --- a/src/shared/agent-hook-listener/grok-result-discovery.ts +++ b/src/shared/agent-hook-listener/grok-result-discovery.ts @@ -16,7 +16,7 @@ import { parseAgentHookJson } from './request-body' import { GROK_HOME_ENVELOPE_MAX_LENGTH, GROK_SESSION_CWD_MAX_LENGTH } from './listener-limits' import { readLastAssistantFromTranscriptOnce } from './transcript-reader' import { isGrokEvent } from './provider-event-names' -import { isAntigravityStopStillBusy } from './providers/antigravity-event-rules' +import { isAntigravityStopStillBusy } from './providers/antigravity/antigravity-event-rules' export function parseHookBodyPayloadRecord(body: unknown): Record | null { if (typeof body !== 'object' || body === null) { diff --git a/src/shared/agent-hook-listener/listener-state.ts b/src/shared/agent-hook-listener/listener-state.ts index 78409182259..b50e64ad0c2 100644 --- a/src/shared/agent-hook-listener/listener-state.ts +++ b/src/shared/agent-hook-listener/listener-state.ts @@ -9,12 +9,12 @@ import { type AgentStatusLegacyAdmissionMode } from '../agent-status/transport/agent-status-legacy-adapter' import type { AgentStatusLegacyIngressCaller } from '../agent-status/transport/agent-status-legacy-ingress-manifest' -import type { ClaudeLaunchedBackgroundTasks } from './providers/claude-owed-task-notifications' +import type { ClaudeLaunchedBackgroundTasks } from './providers/claude/background-tasks/claude-owed-task-notifications' import type { ClaudeSubagentRoster } from '../claude-subagent-roster' import type { ClaudeToolRequest } from './claude-tool-request-identity' import type { CodexSubagentRoster } from '../codex-subagent-roster' import type { CodexSubagentTranscriptState } from '../codex-subagent-transcript' -import type { CodexAsyncQuestions } from './providers/codex-async-questions' +import type { CodexAsyncQuestions } from './providers/codex/codex-async-questions' import type { MuseSessionLogState } from '../muse-session-log' import type { AgentHookEventPayload, ToolSnapshot } from './listener-event' import { @@ -24,7 +24,7 @@ import { movePaneScopedSetEntries } from './pane-scoped-cache-entries' import type { JcodeUserPromptEvidence } from '../jcode-session-files' -import type { JcodeHelperPermissionWait } from './providers/jcode-helper-permission-wait' +import type { JcodeHelperPermissionWait } from './providers/jcode/jcode-helper-permission-wait' import type { HookRootAttempt } from './presence/hook-process-root-evidence' import { moveOpenCodeSessionBindings, diff --git a/src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-interrupt.test.ts b/src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-interrupt.test.ts index 4b773277bb8..2b28d839b91 100644 --- a/src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-interrupt.test.ts +++ b/src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-interrupt.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it } from 'vitest' import { createHookListenerState, type HookListenerState } from '../listener-state' import { normalizeHookPayload } from './normalize-hook-payload' -import '../providers/codex-state' +import '../providers/codex/codex-state' import { PANE_KEY } from './agent-hook-listener-test-harness' // Payload shape from codex-rs/hooks/schema/generated/interrupt.command.input.schema.json. diff --git a/src/shared/claude-background-task-status.test.ts b/src/shared/agent-hook-listener/normalization/claude-background-task-status.test.ts similarity index 54% rename from src/shared/claude-background-task-status.test.ts rename to src/shared/agent-hook-listener/normalization/claude-background-task-status.test.ts index 4a57728f280..ff91f384a8a 100644 --- a/src/shared/claude-background-task-status.test.ts +++ b/src/shared/agent-hook-listener/normalization/claude-background-task-status.test.ts @@ -1,17 +1,12 @@ import { describe, expect, it } from 'vitest' import { - clearAllListenerCaches, - clearPaneCacheState, createHookListenerState, movePaneCacheState, type HookListenerState -} from './agent-hook-listener/listener-state' -import { - markClaudeLeadTurnInterrupted, - seedClaudeSubagentRosterFromSnapshots -} from './agent-hook-listener/providers/claude-roster-state' -import { normalizeHookPayload } from './agent-hook-listener/normalization/normalize-hook-payload' -import { makePaneKey } from './workspace-layout/stable-pane-id' +} from '../listener-state' +import { seedClaudeSubagentRosterFromSnapshots } from '../providers/claude/lifecycle/claude-roster-state' +import { normalizeHookPayload } from './normalize-hook-payload' +import { makePaneKey } from '../../workspace-layout/stable-pane-id' const SOURCE_PANE = makePaneKey('tab-source', '11111111-1111-4111-8111-111111111111') const TARGET_PANE = makePaneKey('tab-target', '22222222-2222-4222-8222-222222222222') @@ -30,46 +25,6 @@ function claudeEvent(state: HookListenerState, paneKey: string, payload: Record< describe('Claude background task status', () => { // SDK local_* names share inventory classification; teammate tasks stay running permanently, so lifecycle events own their pane state (#8825). - it('drains the Claude 2.1.229 background-task completion sequence', () => { - const state = createHookListenerState() - - expect( - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'UserPromptSubmit', - prompt: 'run a background sleep' - }) - ).toMatchObject({ state: 'working', workingMode: undefined }) - expect( - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - background_tasks: [RUNNING_SHELL] - }) - ).toMatchObject({ state: 'working', workingMode: 'monitoring' }) - expect( - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'SubagentStop', - agent_id: 'a70fdf2986e38302b', - background_tasks: [RUNNING_SHELL] - }) - ).toMatchObject({ state: 'working', workingMode: 'monitoring' }) - - expect( - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'UserPromptSubmit', - prompt: - 'b8rs2wmxgcompleted' - }) - ).toMatchObject({ state: 'working', workingMode: undefined }) - expect( - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - background_tasks: [], - session_crons: [] - })?.state - ).toBe('done') - expect(state.claudeRunningNonAgentTaskPaneKeys.has(SOURCE_PANE)).toBe(false) - }) - // Completion notifications need turnCompletedAt while the pane remains working for monitoring; pin both predicates (STA-4119). it('holds the finish of a lead that ends into a working subagent, unless the lead failed', () => { @@ -213,52 +168,6 @@ describe('Claude background task status', () => { ).toMatchObject({ state: 'done', interrupted: true, mainAgent: { outcome: 'cancellation' } }) }) - it('drains a reported cron inventory mid-turn, not only at a turn boundary', () => { - const state = createHookListenerState() - - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - session_crons: [{ id: 'cron-1' }] - }) - expect(state.claudeActiveSessionCronPaneKeys.has(SOURCE_PANE)).toBe(true) - - expect( - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'PostToolUse', - session_crons: [] - })?.state - ).toBe('working') - expect(state.claudeActiveSessionCronPaneKeys.has(SOURCE_PANE)).toBe(false) - - // A mid-turn drain must survive the boundary or a missing next inventory leaves stale cron work pinned. - expect(claudeEvent(state, SOURCE_PANE, { hook_event_name: 'Stop' })?.state).toBe('done') - }) - - it('only infers an omitted cron inventory is drained from modern lead Stop payloads', () => { - const createCronState = (): HookListenerState => { - const state = createHookListenerState() - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - session_crons: [{ id: 'cron-1' }] - }) - return state - } - - const midTurnState = createCronState() - claudeEvent(midTurnState, SOURCE_PANE, { - hook_event_name: 'UserPromptSubmit', - prompt: 'continue', - background_tasks: [] - }) - expect(midTurnState.claudeActiveSessionCronPaneKeys.has(SOURCE_PANE)).toBe(true) - - const legacyStopState = createCronState() - expect(claudeEvent(legacyStopState, SOURCE_PANE, { hook_event_name: 'Stop' })?.state).toBe( - 'working' - ) - expect(legacyStopState.claudeActiveSessionCronPaneKeys.has(SOURCE_PANE)).toBe(true) - }) - it('drops interrupted state on a mid-turn lead event with no prompt submit', () => { const state = createHookListenerState() claudeEvent(state, SOURCE_PANE, { hook_event_name: 'Stop', is_interrupt: true }) @@ -275,40 +184,6 @@ describe('Claude background task status', () => { ).toMatchObject({ state: 'working', interrupted: undefined }) }) - it('ignores child-attributed inventories for lead-owned background work', () => { - const state = createHookListenerState() - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - agent_id: 'child-1', - background_tasks: [RUNNING_SHELL], - session_crons: [{ id: 'cron-1' }] - }) - expect(state.claudeRunningNonAgentTaskPaneKeys.has(SOURCE_PANE)).toBe(false) - expect(state.claudeActiveSessionCronPaneKeys.has(SOURCE_PANE)).toBe(false) - - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - background_tasks: [RUNNING_SHELL], - session_crons: [{ id: 'cron-1' }] - }) - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - agent_id: 'child-1', - background_tasks: [], - session_crons: [] - }) - expect(state.claudeRunningNonAgentTaskPaneKeys.has(SOURCE_PANE)).toBe(true) - expect(state.claudeActiveSessionCronPaneKeys.has(SOURCE_PANE)).toBe(true) - - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - background_tasks: [], - session_crons: [] - }) - expect(state.claudeRunningNonAgentTaskPaneKeys.has(SOURCE_PANE)).toBe(false) - expect(state.claudeActiveSessionCronPaneKeys.has(SOURCE_PANE)).toBe(false) - }) - it('retains live child rows when an agent inventory is partial or has a future status', () => { for (const backgroundTask of [ { id: 'child-1', type: 'subagent', status: 'in_progress' }, @@ -348,31 +223,6 @@ describe('Claude background task status', () => { ).toBeUndefined() }) - it('ignores TeammateIdle inventories for lead-owned background work', () => { - const state = createHookListenerState() - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - background_tasks: [RUNNING_SHELL] - }) - - expect( - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'TeammateIdle', - teammate_name: 'reviewer', - background_tasks: [] - })?.state - ).toBe('working') - expect(state.claudeRunningNonAgentTaskPaneKeys.has(SOURCE_PANE)).toBe(true) - - claudeEvent(state, SOURCE_PANE, { hook_event_name: 'Stop', background_tasks: [] }) - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'TeammateIdle', - teammate_name: 'reviewer', - background_tasks: [RUNNING_SHELL] - }) - expect(state.claudeRunningNonAgentTaskPaneKeys.has(SOURCE_PANE)).toBe(false) - }) - it('shows a child permission wait after the lead turn is interrupted', () => { const state = createHookListenerState() claudeEvent(state, SOURCE_PANE, { @@ -390,56 +240,4 @@ describe('Claude background task status', () => { }) ).toMatchObject({ state: 'waiting', toolName: 'Bash' }) }) - - it('preserves authoritative work across partial inventories and clears it on teardown', () => { - const state = createHookListenerState() - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - background_tasks: [RUNNING_SHELL] - }) - - expect(claudeEvent(state, SOURCE_PANE, { hook_event_name: 'Stop' })).toMatchObject({ - state: 'working', - workingMode: 'monitoring' - }) - expect(state.claudeRunningNonAgentTaskPaneKeys.has(SOURCE_PANE)).toBe(true) - clearPaneCacheState(state, SOURCE_PANE) - expect(state.claudeRunningNonAgentTaskPaneKeys.size).toBe(0) - - claudeEvent(state, TARGET_PANE, { - hook_event_name: 'Stop', - background_tasks: [RUNNING_SHELL] - }) - clearAllListenerCaches(state) - expect(state.claudeRunningNonAgentTaskPaneKeys.size).toBe(0) - }) - - it('keeps a live shell as a confirmed done-gate for child lifecycle events after a cancel', () => { - const state = createHookListenerState() - // A truncated inventory adds a shell without retiring the restored child seed. - seedClaudeSubagentRosterFromSnapshots(state, SOURCE_PANE, [ - { id: 'restored-1', state: 'working', startedAt: 1 } - ]) - claudeEvent(state, SOURCE_PANE, { hook_event_name: 'UserPromptSubmit', prompt: 'resume' }) - claudeEvent(state, SOURCE_PANE, { - hook_event_name: 'Stop', - background_tasks: [{ id: 'shell-x', status: 'running' }] - }) - expect(state.claudeRunningNonAgentTaskPaneKeys.has(SOURCE_PANE)).toBe(true) - markClaudeLeadTurnInterrupted(state, SOURCE_PANE) - - // A live shell confirms inventory regardless of lead verdict; a sibling’s end cannot make it restored-only. - const ended = normalizeHookPayload( - state, - 'claude', - { paneKey: SOURCE_PANE, payload: { hook_event_name: 'SubagentStop', agent_id: 'other' } }, - 'production' - ) - expect(ended?.restoredUnconfirmed).toBeUndefined() - expect(state.claudeUnconfirmedRestoredStatusPaneKeys.has(SOURCE_PANE)).toBe(false) - expect(ended?.payload).toMatchObject({ - state: 'working', - mainAgent: { state: 'done', outcome: 'cancellation' } - }) - }) }) diff --git a/src/shared/agent-hook-listener/normalization/normalize-hook-payload.ts b/src/shared/agent-hook-listener/normalization/normalize-hook-payload.ts index bd64f855b9a..ce89bf3ebab 100644 --- a/src/shared/agent-hook-listener/normalization/normalize-hook-payload.ts +++ b/src/shared/agent-hook-listener/normalization/normalize-hook-payload.ts @@ -8,7 +8,7 @@ import { canAcceptClaudeCompactCompletion, isClaudeCompactCompletionConsumed, markClaudeCompactCompletionConsumed -} from '../providers/claude-compact-completion' +} from '../providers/claude/lifecycle/claude-compact-completion' import { isProcessExitSessionEnd, parseHookEnvelope } from '../hook-envelope' import { readFirstString } from '../interactive-tool' import type { AgentHookEventPayload } from '../listener-event' @@ -17,19 +17,19 @@ import type { HookListenerState } from '../listener-state' import { extractPromptText } from '../prompt-fields' import { normalizeProviderEvent } from '../provider-dispatch' import { hasExplicitUserPrompt } from '../provider-event-routing' -import { hasExplicitAmpPrompt } from '../providers/amp-events' +import { hasExplicitAmpPrompt } from '../providers/amp/amp-events' import { isOpenCodeSharedServerPost, resolveOpenCodeSharedServerEnvelope, suppressOpenCodeSharedServerPost, trackOpenCodePaneLaunchToken } from '../opencode-session-registry' -import { claudeRowHasUnlistedLiveWork } from '../providers/claude-pane-hold-evidence' +import { claudeRowHasUnlistedLiveWork } from '../providers/claude/lifecycle/claude-pane-hold-evidence' import { readString } from '../tool-input-preview' import { readClaudeBatchSettledRequests } from '../claude-settled-permission-requests' import { withClaudeToolRequestId } from '../claude-tool-request-identity' import { hasSeparateHookSessionAdmission, readPaneSessionContext } from '../pane-provider-session' -import { routeJcodeHelperHook } from '../providers/jcode-helper-permission-wait' +import { routeJcodeHelperHook } from '../providers/jcode/jcode-helper-permission-wait' /** Canonical transport-agnostic normalization entry shared by main and relay listeners. */ export function normalizeHookPayload( diff --git a/src/shared/agent-hook-listener/pane-provider-session.ts b/src/shared/agent-hook-listener/pane-provider-session.ts index 9a66336b95d..c419c8d9b4d 100644 --- a/src/shared/agent-hook-listener/pane-provider-session.ts +++ b/src/shared/agent-hook-listener/pane-provider-session.ts @@ -9,8 +9,8 @@ import { import { readFirstString } from './interactive-tool' import type { HookListenerState } from './listener-state' import type { AgentHookEventPayload } from './listener-event' -import { isGrokSubagentEvent } from './providers/grok-tool-fields' -import { isHermesRootSessionEvent, withHermesPaneSessionId } from './providers/hermes-events' +import { isGrokSubagentEvent } from './providers/grok/grok-tool-fields' +import { isHermesRootSessionEvent, withHermesPaneSessionId } from './providers/hermes/hermes-events' import { readString } from './tool-input-preview' import { readFirstTranscriptRecord } from './transcript-reader' diff --git a/src/shared/agent-hook-listener/provider-dispatch.ts b/src/shared/agent-hook-listener/provider-dispatch.ts index 55af4fa0250..acc4e6efbfe 100644 --- a/src/shared/agent-hook-listener/provider-dispatch.ts +++ b/src/shared/agent-hook-listener/provider-dispatch.ts @@ -1,5 +1,5 @@ import { normalizeCompatibleLifecycleEvent } from './providers/compatible-lifecycle-events' -import { normalizeQoderEvent } from './providers/qoder-events' + import type { AgentMainAgentStatus } from '../agent-status/lifecycle/main-agent-status' import type { ParsedAgentStatusPayload } from '../agent-status/contracts/agent-status-types' import type { AgentHookSource } from './transport/agent-hook-relay' @@ -12,28 +12,29 @@ import type { HookListenerState } from './listener-state' import type { ExtractedPromptText } from './prompt-fields' import { isNewTurnEvent } from './provider-event-routing' import { readLastUserPromptFromTranscript } from './transcript-lines' -import { readJcodeTurnPrompt } from './providers/jcode-turn-prompt' -import { normalizeAntigravityEvent } from './providers/antigravity-events' -import { normalizeAmpEvent } from './providers/amp-events' -import { normalizeClaudeEvent } from './providers/claude-events' -import { codexHookEventForEndedTurn, normalizeCodexEvent } from './providers/codex-events' -import { codexHookEventForEphemeralThread } from './providers/codex-ephemeral-threads' -import { normalizeGeminiEvent } from './providers/gemini-events' -import { normalizeOpenCodeFamilyEvent } from './providers/opencode-family-events' -import { normalizeCursorEvent } from './providers/cursor-events' -import { normalizePiCompatibleEvent } from './providers/pi-family-events' -import { normalizeDroidEvent } from './providers/droid-events' -import { normalizeCommandCodeEvent } from './providers/command-code-events' -import { normalizeGrokEvent } from './providers/grok-events' -import { normalizeCopilotEvent } from './providers/copilot-events' -import { normalizeHermesEvent } from './providers/hermes-events' -import { normalizeDevinEvent } from './providers/devin-events' -import { normalizeKimiEvent } from './providers/kimi-events' -import { normalizeMuseEvent } from './providers/muse-events' -import { normalizeDshEvent } from './providers/dsh-events' -import { normalizeZCodeEvent } from './providers/zcode-events' -import { normalizeJcodeEvent } from './providers/jcode-events' -import { normalizeKiroEvent } from './providers/kiro-events' +import { readJcodeTurnPrompt } from './providers/jcode/jcode-turn-prompt' +import { normalizeAntigravityEvent } from './providers/antigravity/antigravity-events' +import { normalizeAmpEvent } from './providers/amp/amp-events' +import { normalizeClaudeEvent } from './providers/claude/claude-events' +import { normalizeCodexEvent } from './providers/codex/codex-events' +import { codexHookEventForEndedTurn } from './providers/codex/codex-ended-turn-event' +import { codexHookEventForEphemeralThread } from './providers/codex/codex-ephemeral-threads' +import { normalizeGeminiEvent } from './providers/gemini/gemini-events' +import { normalizeOpenCodeFamilyEvent } from './providers/opencode-family/opencode-family-events' +import { normalizeCursorEvent } from './providers/cursor/cursor-events' +import { normalizePiCompatibleEvent } from './providers/pi-family/pi-family-events' +import { normalizeDroidEvent } from './providers/droid/droid-events' +import { normalizeCommandCodeEvent } from './providers/command-code/command-code-events' +import { normalizeGrokEvent } from './providers/grok/grok-events' +import { normalizeCopilotEvent } from './providers/copilot/copilot-events' +import { normalizeHermesEvent } from './providers/hermes/hermes-events' +import { normalizeDevinEvent } from './providers/devin/devin-events' +import { normalizeKimiEvent } from './providers/kimi/kimi-events' +import { normalizeMuseEvent } from './providers/muse/muse-events' +import { normalizeDshEvent } from './providers/dsh/dsh-events' +import { normalizeZCodeEvent } from './providers/zcode/zcode-events' +import { normalizeJcodeEvent } from './providers/jcode/jcode-events' +import { normalizeKiroEvent } from './providers/kiro/kiro-events' export type ProviderDispatchResult = { payload: ParsedAgentStatusPayload | null @@ -209,7 +210,14 @@ export function normalizeProviderEvent(input: { break } case 'qoder': - payload = normalizeQoderEvent(state, eventName, promptText, paneKey, hookPayload) + payload = normalizeCompatibleLifecycleEvent( + 'qoder', + state, + eventName, + promptText, + paneKey, + hookPayload + ) break case 'kimi': payload = normalizeKimiEvent(state, eventName, promptText, paneKey, hookPayload) diff --git a/src/shared/agent-hook-listener/provider-event-routing.ts b/src/shared/agent-hook-listener/provider-event-routing.ts index 082e8662058..95c608dd75f 100644 --- a/src/shared/agent-hook-listener/provider-event-routing.ts +++ b/src/shared/agent-hook-listener/provider-event-routing.ts @@ -2,25 +2,25 @@ import type { AgentHookSource } from './transport/agent-hook-relay' import { isKnownHarnessInjectedUserTurnText } from '../harness-injected-user-turns' import type { ToolSnapshot } from './listener-event' import type { ExtractedPromptText } from './prompt-fields' -import { extractClaudeToolFields } from './providers/claude-tool-fields' -import { extractCodexToolFields } from './providers/codex-tool-fields' -import { extractGeminiToolFields } from './providers/gemini-tool-fields' -import { extractAntigravityToolFields } from './providers/antigravity-tool-fields' -import { extractAmpToolFields } from './providers/amp-tool-fields' -import { extractOpenCodeToolFields } from './providers/opencode-family-tool-fields' -import { extractCursorToolFields } from './providers/cursor-tool-fields' +import { extractClaudeToolFields } from './providers/claude/claude-tool-fields' +import { extractCodexToolFields } from './providers/codex/codex-tool-fields' +import { extractGeminiToolFields } from './providers/gemini/gemini-tool-fields' +import { extractAntigravityToolFields } from './providers/antigravity/antigravity-tool-fields' +import { extractAmpToolFields } from './providers/amp/amp-tool-fields' +import { extractOpenCodeToolFields } from './providers/opencode-family/opencode-family-tool-fields' +import { extractCursorToolFields } from './providers/cursor/cursor-tool-fields' import { extractCopilotToolFields, normalizeCopilotEventName -} from './providers/copilot-tool-fields' -import { extractPiToolFields } from './providers/pi-family-tool-fields' -import { extractDroidToolFields } from './providers/droid-tool-fields' -import { extractCommandCodeToolFields } from './providers/command-code-tool-fields' +} from './providers/copilot/copilot-tool-fields' +import { extractPiToolFields } from './providers/pi-family/pi-family-tool-fields' +import { extractDroidToolFields } from './providers/droid/droid-tool-fields' +import { extractCommandCodeToolFields } from './providers/command-code/command-code-tool-fields' import { isGrokEvent } from './provider-event-names' -import { extractGrokToolFields } from './providers/grok-tool-fields' -import { extractHermesToolFields } from './providers/hermes-tool-fields' -import { extractJcodeToolFields } from './providers/jcode-tool-fields' -import { extractKiroToolFields } from './providers/kiro-tool-fields' +import { extractGrokToolFields } from './providers/grok/grok-tool-fields' +import { extractHermesToolFields } from './providers/hermes/hermes-tool-fields' +import { extractJcodeToolFields } from './providers/jcode/jcode-tool-fields' +import { extractKiroToolFields } from './providers/kiro/kiro-tool-fields' /** Share provider boundaries with observation stamping; avoid a second event-name list. */ export function isNewTurnEvent(source: AgentHookSource, eventName: unknown): boolean { diff --git a/src/shared/agent-hook-listener/providers/amp-events.ts b/src/shared/agent-hook-listener/providers/amp/amp-events.ts similarity index 92% rename from src/shared/agent-hook-listener/providers/amp-events.ts rename to src/shared/agent-hook-listener/providers/amp/amp-events.ts index ac45758dc4e..71e670a46f0 100644 --- a/src/shared/agent-hook-listener/providers/amp-events.ts +++ b/src/shared/agent-hook-listener/providers/amp/amp-events.ts @@ -1,13 +1,13 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import { readFirstString } from '../interactive-tool' -import { readBoundedString } from '../grok-result-discovery' -import { clearPaneTurnCacheState, type HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { AMP_MAX_SCOPED_THREAD_CACHE_KEYS, AMP_THREAD_ID_MAX_LENGTH } from '../listener-limits' +} from '../../../agent-status/contracts/agent-status-types' +import { readFirstString } from '../../interactive-tool' +import { readBoundedString } from '../../grok-result-discovery' +import { clearPaneTurnCacheState, type HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { AMP_MAX_SCOPED_THREAD_CACHE_KEYS, AMP_THREAD_ID_MAX_LENGTH } from '../../listener-limits' export function normalizeAmpEvent( state: HookListenerState, diff --git a/src/shared/agent-hook-listener/providers/amp-tool-fields.ts b/src/shared/agent-hook-listener/providers/amp/amp-tool-fields.ts similarity index 90% rename from src/shared/agent-hook-listener/providers/amp-tool-fields.ts rename to src/shared/agent-hook-listener/providers/amp/amp-tool-fields.ts index 2bd8d3a8781..7a12ffc8c9a 100644 --- a/src/shared/agent-hook-listener/providers/amp-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/amp/amp-tool-fields.ts @@ -1,12 +1,12 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveFallbackToolInputPreview, deriveToolInputPreview, hasAnyOwnField, readString, toolUpdate -} from '../tool-input-preview' -import { extractToolResponseText, readFirstString } from '../interactive-tool' +} from '../../tool-input-preview' +import { extractToolResponseText, readFirstString } from '../../interactive-tool' export function extractAmpToolFields( eventName: unknown, diff --git a/src/shared/agent-hook-listener/providers/antigravity-background-command.test.ts b/src/shared/agent-hook-listener/providers/antigravity/antigravity-background-command.test.ts similarity index 87% rename from src/shared/agent-hook-listener/providers/antigravity-background-command.test.ts rename to src/shared/agent-hook-listener/providers/antigravity/antigravity-background-command.test.ts index 3494b8ea693..2c20d8a30c4 100644 --- a/src/shared/agent-hook-listener/providers/antigravity-background-command.test.ts +++ b/src/shared/agent-hook-listener/providers/antigravity/antigravity-background-command.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest' -import { createHookListenerState } from '../listener-state' -import { normalizeHookPayload } from '../normalization/normalize-hook-payload' -import { PANE_KEY } from '../normalization/agent-hook-listener-test-harness' +import { createHookListenerState } from '../../listener-state' +import { normalizeHookPayload } from '../../normalization/normalize-hook-payload' +import { PANE_KEY } from '../../normalization/agent-hook-listener-test-harness' // Hook order captured from agy 1.5.2 when the model backgrounds run_command (WaitMsBeforeAsync 500). const turn = { conversationId: 'parent', lastUserInput: 'start the job' } diff --git a/src/shared/agent-hook-listener/providers/antigravity-event-rules.ts b/src/shared/agent-hook-listener/providers/antigravity/antigravity-event-rules.ts similarity index 100% rename from src/shared/agent-hook-listener/providers/antigravity-event-rules.ts rename to src/shared/agent-hook-listener/providers/antigravity/antigravity-event-rules.ts diff --git a/src/shared/agent-hook-listener/providers/antigravity-events.ts b/src/shared/agent-hook-listener/providers/antigravity/antigravity-events.ts similarity index 93% rename from src/shared/agent-hook-listener/providers/antigravity-events.ts rename to src/shared/agent-hook-listener/providers/antigravity/antigravity-events.ts index 490f5554677..0042ba19054 100644 --- a/src/shared/agent-hook-listener/providers/antigravity-events.ts +++ b/src/shared/agent-hook-listener/providers/antigravity/antigravity-events.ts @@ -1,12 +1,12 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import { readFirstString } from '../interactive-tool' -import type { HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { readLastUserPromptFromTranscript } from '../transcript-lines' +} from '../../../agent-status/contracts/agent-status-types' +import { readFirstString } from '../../interactive-tool' +import type { HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { readLastUserPromptFromTranscript } from '../../transcript-lines' import { readAntigravityToolCall } from './antigravity-tool-fields' import { isAntigravityStopStillBusy } from './antigravity-event-rules' diff --git a/src/shared/agent-hook-listener/providers/antigravity-tool-fields.ts b/src/shared/agent-hook-listener/providers/antigravity/antigravity-tool-fields.ts similarity index 87% rename from src/shared/agent-hook-listener/providers/antigravity-tool-fields.ts rename to src/shared/agent-hook-listener/providers/antigravity/antigravity-tool-fields.ts index ba57349f14b..fe84d450e49 100644 --- a/src/shared/agent-hook-listener/providers/antigravity-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/antigravity/antigravity-tool-fields.ts @@ -1,12 +1,12 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveFallbackToolInputPreview, deriveToolInputPreview, readString, toolUpdate -} from '../tool-input-preview' -import { readFirstString } from '../interactive-tool' -import { readLastAssistantFromTranscript } from '../transcript-lines' +} from '../../tool-input-preview' +import { readFirstString } from '../../interactive-tool' +import { readLastAssistantFromTranscript } from '../../transcript-lines' import { isAntigravityStopStillBusy } from './antigravity-event-rules' export function readAntigravityToolCall(hookPayload: Record): { diff --git a/src/shared/agent-hook-listener/providers/claude-helper-kill.test-fixture.ts b/src/shared/agent-hook-listener/providers/claude-helper-kill.test-fixture.ts deleted file mode 100644 index f5b7993c56b..00000000000 --- a/src/shared/agent-hook-listener/providers/claude-helper-kill.test-fixture.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { mkdtemp, writeFile, mkdir, rm } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, beforeEach, vi } from 'vitest' -import { createHookListenerState } from '../listener-state' -import { seedLegacyAgentStatusForTests } from '../legacy-agent-status-test-seed' -import { normalizeHookPayload } from '../normalization/normalize-hook-payload' -import { ClaudeHelperKillPoller } from './claude-helper-kill-poller' -import { readClaudeTranscriptChunk } from './claude-transcript-cursor' -import type { AgentHookEventPayload } from '../listener-event' - -export let dir: string -export const now = Date.parse('2026-10-29T12:00:00Z') -export const stamp = (offset: number) => new Date(now + offset).toISOString() -export const kill = (offset = 1) => - `${JSON.stringify({ type: 'system', subtype: 'agents_killed', timestamp: stamp(offset) })}\n` -export const prompt = (offset: number) => - `${JSON.stringify({ - type: 'user', - timestamp: stamp(offset), - message: { role: 'user', content: 'Do the work' } - })}\n` - -beforeEach(async () => { - vi.spyOn(Date, 'now').mockReturnValue(now) - dir = await mkdtemp(join(tmpdir(), 'claude-helper-kill-')) -}) -afterEach(async () => { - vi.restoreAllMocks() - await rm(dir, { recursive: true, force: true }) -}) - -export async function pane(shell = false, reader = readClaudeTranscriptChunk, cron = false) { - const state = createHookListenerState() - const path = join(dir, 'session.jsonl') - await writeFile(path, prompt(-20)) - await mkdir(join(dir, 'session', 'subagents'), { recursive: true }) - const child = (id: string) => join(dir, 'session', 'subagents', `agent-${id}.jsonl`) - await writeFile(child('a123'), prompt(-10)) - let current: AgentHookEventPayload | undefined - const events: AgentHookEventPayload[] = [] - function post(payload: Record) { - const event = normalizeHookPayload( - state, - 'claude', - { - paneKey: 'test-tab:11111111-1111-4111-8111-111111111111', - env: 'production', - payload: { session_id: 'session', transcript_path: path, ...payload } - }, - 'production' - ) - if (!event) { - throw new Error('hook declined') - } - current = event - seedLegacyAgentStatusForTests(state, event) - return event - } - post({ hook_event_name: 'UserPromptSubmit', prompt: 'work' }) - post({ hook_event_name: 'SubagentStart', agent_id: 'a123' }) - post({ - hook_event_name: 'Stop', - last_assistant_message: 'READY', - background_tasks: [ - { id: 'a123', type: 'subagent', status: 'running' }, - ...(shell ? [{ id: 'shell', type: 'local_bash', status: 'running' }] : []) - ], - session_crons: cron ? [{ id: 'cron' }] : [] - }) - const publish = vi.fn((event: AgentHookEventPayload) => { - current = event - events.push(event) - seedLegacyAgentStatusForTests(state, event) - }) - let listening = true - const poller = new ClaudeHelperKillPoller( - state, - { current: () => current, publish, listening: () => listening }, - reader - ) - poller.schedule(current!) - return { - state, - listen: (value: boolean) => { - listening = value - if (value) { - poller.activate() - } - }, - path, - child, - post, - poller, - publish, - events, - row: () => current!, - replace: (row: AgentHookEventPayload) => { - current = row - poller.schedule(row) - } - } -} - -export async function drain(p: Awaited>) { - for (let i = 0; i < 8; i++) { - await p.poller.poll('test-tab:11111111-1111-4111-8111-111111111111') - } -} diff --git a/src/shared/agent-hook-listener/providers/claude-helper-kill.test.ts b/src/shared/agent-hook-listener/providers/claude-helper-kill.test.ts deleted file mode 100644 index 77134f72180..00000000000 --- a/src/shared/agent-hook-listener/providers/claude-helper-kill.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { writeFile, appendFile, rename } from 'node:fs/promises' -import { join } from 'node:path' -import { describe, expect, it } from 'vitest' -import { dir, kill, pane, drain, prompt, stamp } from './claude-helper-kill.test-fixture' - -describe('Claude helper provider evidence', () => { - it.each([false, true])( - 'ignores mixed server-result echoes after a kill; real prompt: %s', - async (realPrompt) => { - const p = await pane() - try { - await writeFile(p.child('a123'), prompt(1)) - await appendFile(p.path, kill(2)) - await appendFile( - p.child('a123'), - `${JSON.stringify({ - type: 'user', - timestamp: stamp(3), - message: { - role: 'user', - content: [ - { type: 'text', text: 'server context' }, - { - type: 'advisor_tool_result', - tool_use_id: 'server', - content: { type: 'advisor_redacted_result' } - } - ] - } - })}\n` - ) - if (realPrompt) { - await appendFile(p.child('a123'), prompt(4)) - } - await drain(p) - if (realPrompt) { - expect(p.row().payload.subagents?.[0]?.state).toBe('working') - } else { - expect(p.row().payload.subagents ?? []).toEqual([]) - } - } finally { - p.poller.clearAll() - } - } - ) - - it('forgets kills on parent file replacement', async () => { - const p = await pane() - try { - await appendFile(p.path, kill()) - await drain(p) - expect(p.row().payload.subagents ?? []).toEqual([]) - await writeFile(join(dir, 'replacement.jsonl'), kill()) - await rename(join(dir, 'replacement.jsonl'), p.path) - p.replace(p.post({ hook_event_name: 'SubagentStart', agent_id: 'a123' })) - await drain(p) - expect(p.row().payload.subagents?.[0]?.state).toBe('working') - } finally { - p.poller.clearAll() - } - }) - - it('does not apply an in-flight read after pane retirement or to its successor', async () => { - const p = await pane() - try { - await appendFile(p.path, kill()) - const reading = p.poller.poll('test-tab:11111111-1111-4111-8111-111111111111') - p.poller.clear('test-tab:11111111-1111-4111-8111-111111111111') - p.replace({ - ...p.row(), - providerSession: { key: 'session_id', id: 'replacement', transcriptPath: p.path } - }) - await reading - expect(p.publish).not.toHaveBeenCalled() - } finally { - p.poller.clearAll() - } - }) -}) diff --git a/src/shared/agent-hook-listener/providers/claude-transcript-semantics.test.ts b/src/shared/agent-hook-listener/providers/claude-transcript-semantics.test.ts deleted file mode 100644 index a32cae6d045..00000000000 --- a/src/shared/agent-hook-listener/providers/claude-transcript-semantics.test.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { mkdtemp, writeFile, appendFile, readFile, rm } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { createClaudeTranscriptCursor, readClaudeTranscriptChunk } from './claude-transcript-cursor' -import { - readClaudeHelperEvidence, - readClaudeChildPrompts, - type ClaudeChildTranscript, - type ClaudeHelperTranscriptEvidence -} from './claude-helper-transcript-evidence' - -function isRecord(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !Array.isArray(value) -} - -let dir: string -const now = Date.parse('2026-10-29T12:00:00Z') -const stamp = (offset: number) => new Date(now + offset).toISOString() -const kill = (offset = 1) => - `${JSON.stringify({ type: 'system', subtype: 'agents_killed', timestamp: stamp(offset) })}\n` -const prompt = (offset: number) => - `${JSON.stringify({ type: 'user', timestamp: stamp(offset), message: { content: 'Do the work' } })}\n` -beforeEach(async () => { - dir = await mkdtemp(join(tmpdir(), 'claude-transcript-semantics-')) -}) -afterEach(async () => { - vi.restoreAllMocks() - await rm(dir, { recursive: true, force: true }) -}) - -describe('forward transcript reading', () => { - it('treats missing files as unverifiable and replacement/shrink as a new baseline', async () => { - const path = join(dir, 'replace.jsonl') - const cursor = createClaudeTranscriptCursor(path) - expect(await readClaudeTranscriptChunk(cursor)).toBeUndefined() - await writeFile(path, `${prompt(0)}${' '.repeat(200)}`) - await readClaudeTranscriptChunk(cursor) - await writeFile(path, kill()) - expect((await readClaudeTranscriptChunk(cursor))?.records).toEqual([]) - await appendFile(path, kill(20)) - expect((await readClaudeTranscriptChunk(cursor))?.records).toHaveLength(1) - }) -}) - -it('consumes every settled launch correlation at N and 10N without inventing failed resumes', () => { - for (const count of [10, 100]) { - const evidence = { - observedAt: now, - resumedAt: new Map(), - launches: new Map() - } - for (let index = 0; index < count; index++) { - for (const outcome of ['sync', 'failed', 'async', 'missing-time']) { - const id = `${outcome}-${index}` - readClaudeHelperEvidence(evidence, [ - { - type: 'assistant', - timestamp: stamp(index), - message: { content: [{ type: 'tool_use', name: 'Agent', id }] } - }, - { - type: 'user', - ...(outcome === 'missing-time' ? {} : { timestamp: stamp(index + 1) }), - toolUseResult: { isAsync: outcome === 'async' || outcome === 'failed', agentId: id }, - message: { - content: [{ type: 'tool_result', tool_use_id: id, is_error: outcome === 'failed' }] - } - } - ]) - } - } - expect(evidence.launches.size).toBe(0) - expect([...evidence.resumedAt.keys()]).toEqual( - Array.from({ length: count }, (_, i) => `async-${i}`) - ) - } -}) - -it('matches complete JSON evidence on captured and adversarial final values without provisional facts', async () => { - const captured = await Promise.all( - ['parent', 'child'].map(async (stream) => - ( - await readFile( - new URL(`../../__fixtures__/claude-helper-kill-2.1.310-${stream}.jsonl`, import.meta.url), - 'utf8' - ) - ) - .trim() - .split('\n') - ) - ) - const base = `"type":"user","timestamp":${JSON.stringify(stamp(20))}` - const huge = ' '.repeat(300_000) - const raw = [ - ...captured.flat(), - `{${base},"message":{"content":"${huge}real é😀"}}`, - `{${base},"message":{"content":[{"text":"x","type":"text"},{"type":"tool_result"}]}}`, - `{${base},"message":{"content":[{"type":"text","text":"real"}]},"isMeta":true}`, - `{${base},"message":{"content":"real"},"isCompactSummary":true,"isCompactSummary":false}`, - `{${base},"message":{"content":"real"},"message":null}`, - `{${base},"message":{"content":[{"type":"text","text":"real"}],"content":false}}`, - `{${base},"message":{"content":"injected","content":[{"type":"text","text":"[Request interrupted by user]"}]}}`, - `{${base},"message":{"content":[{"type":"tool_result","type":"text","text":"real"}]}}`, - `{${base},"message":{"content":[{"type":"text","text":"real","text":null}]}}`, - `{${base},"message":{"content":"real"},"${'unknown'.repeat(60_000)}":[[[{"ignored":"${huge}"}]]]}`, - `{${base},"message":{"content":"\uFEFF\t\u00a0"}}`, - `{${base},"message":{"content":"\u0085"}}`, - `{${base},"message":{"content":"real"},"type":"system","subtype":"agents_killed"}`, - `{"type":"assistant","timestamp":${JSON.stringify(stamp(20))},"message":{"content":[{"type":"tool_use","name":"Task","id":"use","input":"${huge}"}]}}`, - `{"type":"user","toolUseResult":{"isAsync":true,"agentId":"a123"},"message":{"content":[{"type":"tool_result","tool_use_id":"use","is_error":false}]}}`, - `{"type":"user","toolUseResult":{"isAsync":true,"agentId":"a123"},"toolUseResult":3,"message":{"content":[{"type":"tool_result","tool_use_id":"use"}]}}`, - `{"type":"user","toolUseResult":{"isAsync":true,"agentId":"a123"},"message":{"content":[{"type":"tool_result","tool_use_id":"use","is_error":false,"is_error":true}]}}`, - `{"type":"user","toolUseResult":{"isAsync":true,"isAsync":false,"agentId":"a123"},"message":{"content":[{"type":"tool_result","tool_use_id":"use"}]}}`, - `{${base},"message":{"content":[{"type":"text","text":"real"}],"content":null},"message":{"content":"new final message"}}`, - `{${base},"message":{"content":"real"},"timestamp${'x'.repeat(300_000)}":"1900-01-01","ignored":1${'0'.repeat(300_000)}}`, - JSON.stringify({ - type: 'user', - timestamp: stamp(20), - message: { content: `${' '.repeat(65)}[Request interrupted by user]` } - }), - `{"type":"system","subtype":"agents_killed","timestamp":${JSON.stringify(stamp(30))}} trailing`, - `{${base},"message":{"content":"real"},"bad":[1,]}`, - '[]', - 'null', - 'true', - '"not an object"' - ] - const originals: Record[] = [] - for (const line of raw) { - try { - const value: unknown = JSON.parse(line) - if (isRecord(value)) { - originals.push(value) - } - } catch { - /* Intentionally invalid complete frames have no effects. */ - } - } - const path = join(dir, 'semantic-parity.jsonl') - const wire = `${raw.join('\r\n')}\r\n` - await writeFile(path, wire) - const cursor = createClaudeTranscriptCursor(path) - const actual: Record[] = [] - while (cursor.offset < Buffer.byteLength(wire)) { - const result = await readClaudeTranscriptChunk(cursor) - actual.push(...(result?.records ?? [])) - } - expect(actual).toHaveLength(originals.length) - const evidence = (): ClaudeHelperTranscriptEvidence => ({ - observedAt: now, - launches: new Map([['use', now + 20]]), - resumedAt: new Map() - }) - for (let index = 0; index < originals.length; index++) { - const expected = evidence(), - projected = evidence() - readClaudeHelperEvidence(expected, [originals[index]]) - readClaudeHelperEvidence(projected, [actual[index]]) - expect(projected).toEqual(expected) - const expectedChild: ClaudeChildTranscript = { cursor: createClaudeTranscriptCursor(path) } - const projectedChild: ClaudeChildTranscript = { cursor: expectedChild.cursor } - readClaudeChildPrompts(expectedChild, [originals[index]]) - readClaudeChildPrompts(projectedChild, [actual[index]]) - expect(projectedChild.promptedAt).toBe(expectedChild.promptedAt) - } - const expected = evidence(), - projected = evidence() - readClaudeHelperEvidence(expected, originals) - readClaudeHelperEvidence(projected, actual) - expect(projected).toEqual(expected) - // Selected scalars/escaped keys cross every byte boundary, including UTF-8 and surrogate escapes. - const split = `{"ty\\u0070e":"assistant","timestamp":${JSON.stringify(stamp(20))},"message":{"content":[{"type":"tool_use","name":"Agent","id":"é😀\\ud800"}]}}\n` - await writeFile(path, split) - const tiny = createClaudeTranscriptCursor(path) - const selected: Record[] = [] - while (tiny.offset < Buffer.byteLength(split)) { - const chunk = await readClaudeTranscriptChunk(tiny, 1) - selected.push(...(chunk?.records ?? [])) - if (tiny.offset < Buffer.byteLength(split)) { - expect(chunk?.records).toEqual([]) - expect(chunk?.caughtUp).toBe(false) - } - } - const final = evidence(), - oracle = evidence() - readClaudeHelperEvidence(final, selected) - readClaudeHelperEvidence(oracle, [JSON.parse(split)]) - expect(final).toEqual(oracle) -}) diff --git a/src/shared/agent-hook-listener/providers/claude-owed-notification-expiry-timers.ts b/src/shared/agent-hook-listener/providers/claude/background-tasks/claude-owed-notification-expiry-timers.ts similarity index 84% rename from src/shared/agent-hook-listener/providers/claude-owed-notification-expiry-timers.ts rename to src/shared/agent-hook-listener/providers/claude/background-tasks/claude-owed-notification-expiry-timers.ts index 03ecb922f01..eb31d8f9ae6 100644 --- a/src/shared/agent-hook-listener/providers/claude-owed-notification-expiry-timers.ts +++ b/src/shared/agent-hook-listener/providers/claude/background-tasks/claude-owed-notification-expiry-timers.ts @@ -1,13 +1,13 @@ -import type { AgentHookEventPayload } from '../listener-event' -import type { HookListenerState } from '../listener-state' -import { mainAgentIdleSince } from '../main-agent-turn-state' -import { claudeRowHasUnlistedLiveWork } from './claude-pane-hold-evidence' -import { buildClaudeCachedLeadStatusPayload } from './claude-lifecycle-events' +import type { AgentHookEventPayload } from '../../../listener-event' +import type { HookListenerState } from '../../../listener-state' +import { mainAgentIdleSince } from '../../../main-agent-turn-state' +import { claudeRowHasUnlistedLiveWork } from '../lifecycle/claude-pane-hold-evidence' +import { buildClaudeCachedLeadStatusPayload } from '../lifecycle/claude-lifecycle-events' import { CLAUDE_OWED_TASK_NOTIFICATION_LEASE_MS, claudeOwedTaskNotificationDeadline } from './claude-owed-task-notifications' -import { CodexSubagentPollScheduler } from '../transcripts/codex-subagent-poll-scheduler' +import { CodexSubagentPollScheduler } from '../../../transcripts/codex-subagent-poll-scheduler' type PublishRow = (row: AgentHookEventPayload) => void diff --git a/src/shared/agent-hook-listener/providers/claude-owed-task-notifications.ts b/src/shared/agent-hook-listener/providers/claude/background-tasks/claude-owed-task-notifications.ts similarity index 98% rename from src/shared/agent-hook-listener/providers/claude-owed-task-notifications.ts rename to src/shared/agent-hook-listener/providers/claude/background-tasks/claude-owed-task-notifications.ts index 877a2ac00a7..d1c5f38addf 100644 --- a/src/shared/agent-hook-listener/providers/claude-owed-task-notifications.ts +++ b/src/shared/agent-hook-listener/providers/claude/background-tasks/claude-owed-task-notifications.ts @@ -1,5 +1,5 @@ -import { isRecord } from '../../agent-status/child-work/agent-status-child-work-value-guards' -import { runsOrcaOrchestrationWait } from '../../orca-cli-invocations' +import { isRecord } from '../../../../agent-status/child-work/agent-status-child-work-value-guards' +import { runsOrcaOrchestrationWait } from '../../../../orca-cli-invocations' /** Main-agent background tasks keyed by launch/notification task ID. Notifications can arrive a turn after task end and an empty Stop; owed work keeps the pane active (#23942). Captures: __fixtures__/claude-task-notification-hooks.jsonl. */ export type ClaudeLaunchedBackgroundTasks = Map diff --git a/src/shared/agent-hook-listener/providers/claude-task-notification-delivery.ts b/src/shared/agent-hook-listener/providers/claude/background-tasks/claude-task-notification-delivery.ts similarity index 91% rename from src/shared/agent-hook-listener/providers/claude-task-notification-delivery.ts rename to src/shared/agent-hook-listener/providers/claude/background-tasks/claude-task-notification-delivery.ts index d76dbf044d2..dabe7b95769 100644 --- a/src/shared/agent-hook-listener/providers/claude-task-notification-delivery.ts +++ b/src/shared/agent-hook-listener/providers/claude/background-tasks/claude-task-notification-delivery.ts @@ -1,4 +1,4 @@ -import type { readClaudeBackgroundAgentTasks } from '../../claude-background-task-inventory' +import type { readClaudeBackgroundAgentTasks } from '../../../../claude-background-task-inventory' import { forgetStoppedClaudeShellTask, oweClaudeShellTaskNotifications, @@ -7,9 +7,9 @@ import { recordClaudeUnconfirmedAgentNotification, settleClaudeTaskNotification } from './claude-owed-task-notifications' -import { readClaudeTaskNotification } from '../../claude-task-notification-text' -import type { HookListenerState } from '../listener-state' -import { readString } from '../tool-input-preview' +import { readClaudeTaskNotification } from '../../../../claude-task-notification-text' +import type { HookListenerState } from '../../../listener-state' +import { readString } from '../../../tool-input-preview' /** Track main-agent launches only: child-launched task notifications wake that child, not this pane. */ export function trackClaudeTaskNotificationDelivery( diff --git a/src/shared/agent-hook-listener/providers/claude-events.ts b/src/shared/agent-hook-listener/providers/claude/claude-events.ts similarity index 92% rename from src/shared/agent-hook-listener/providers/claude-events.ts rename to src/shared/agent-hook-listener/providers/claude/claude-events.ts index 1ae22a8a04b..467d90e34a8 100644 --- a/src/shared/agent-hook-listener/providers/claude-events.ts +++ b/src/shared/agent-hook-listener/providers/claude/claude-events.ts @@ -1,11 +1,11 @@ -import type { ParsedAgentStatusPayload } from '../../agent-status/contracts/agent-status-types' -import { CLAUDE_NEW_SESSION_START_SOURCES } from './claude-session-start-sources' +import type { ParsedAgentStatusPayload } from '../../../agent-status/contracts/agent-status-types' +import { CLAUDE_NEW_SESSION_START_SOURCES } from './lifecycle/claude-session-start-sources' import { heldTurnCountsAsFinished, isAgentStatusHeldOpenByChildWork -} from '../../agent-status/lifecycle/agent-lead-status-fold' -import { isAskUserQuestionTool } from '../../agent-status/lifecycle/agent-question-answered-intent' -import { readClaudeBackgroundAgentTasks } from '../../claude-background-task-inventory' +} from '../../../agent-status/lifecycle/agent-lead-status-fold' +import { isAskUserQuestionTool } from '../../../agent-status/lifecycle/agent-question-answered-intent' +import { readClaudeBackgroundAgentTasks } from '../../../claude-background-task-inventory' import { claudeRosterHasRestoredSnapshotSubagent, claudeRosterHasRuntimeWorkingSubagent, @@ -13,31 +13,31 @@ import { isClaudeChildTurnEndEvent, reapUnconfirmedRestoredClaudeSubagents, upsertWorkingClaudeSubagent -} from '../../claude-subagent-roster' -import type { HookListenerState } from '../listener-state' -import { readFirstString } from '../interactive-tool' -import { shouldIgnoreCompactContinuationUserPromptSubmit } from '../prompt-fields' -import { readString } from '../tool-input-preview' +} from '../../../claude-subagent-roster' +import type { HookListenerState } from '../../listener-state' +import { readFirstString } from '../../interactive-tool' +import { shouldIgnoreCompactContinuationUserPromptSubmit } from '../../prompt-fields' +import { readString } from '../../tool-input-preview' import { buildClaudeCachedLeadStatusPayload, normalizeClaudeSubagentLifecycleEvent -} from './claude-lifecycle-events' +} from './lifecycle/claude-lifecycle-events' import { getOrCreateClaudeSubagentRoster, readClaudeLeadModel, setClaudeMainAgentTurnState, updateClaudeRunningNonAgentTask, voidClaimsOfReplacedClaudeSession -} from './claude-roster-state' +} from './lifecycle/claude-roster-state' import { resolveClaudePaneStatus, claudePaneHasRunningChildWork, claudeRowHasUnlistedLiveWork -} from './claude-pane-hold-evidence' -import { buildClaudeStatusPayload } from './claude-status-build' -import { claudeWaitRequest, settleClaudeToolBatch } from './claude-tool-request-wait' -import { trackClaudeTaskNotificationDelivery } from './claude-task-notification-delivery' -import { recordClaudeTeammateSpawn } from './claude-teammate-spawn' +} from './lifecycle/claude-pane-hold-evidence' +import { buildClaudeStatusPayload } from './lifecycle/claude-status-build' +import { claudeWaitRequest, settleClaudeToolBatch } from './lifecycle/claude-tool-request-wait' +import { trackClaudeTaskNotificationDelivery } from './background-tasks/claude-task-notification-delivery' +import { recordClaudeTeammateSpawn } from './lifecycle/claude-teammate-spawn' const CLAUDE_HOOK_AGENT_ID_PREFIX = 'hook-agent-' diff --git a/src/shared/agent-hook-listener/providers/claude-tool-fields.ts b/src/shared/agent-hook-listener/providers/claude/claude-tool-fields.ts similarity index 87% rename from src/shared/agent-hook-listener/providers/claude-tool-fields.ts rename to src/shared/agent-hook-listener/providers/claude/claude-tool-fields.ts index f0ce6fb4297..a28ab80e4e2 100644 --- a/src/shared/agent-hook-listener/providers/claude-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/claude/claude-tool-fields.ts @@ -1,11 +1,16 @@ -import type { ToolSnapshot } from '../listener-event' -import { deriveToolInputPreview, hasOwnField, readString, toolUpdate } from '../tool-input-preview' +import type { ToolSnapshot } from '../../listener-event' +import { + deriveToolInputPreview, + hasOwnField, + readString, + toolUpdate +} from '../../tool-input-preview' import { clearActiveToolFieldsUpdate, deriveInteractivePrompt, extractToolResponseText -} from '../interactive-tool' -import { readLastAssistantFromTranscript } from '../transcript-lines' +} from '../../interactive-tool' +import { readLastAssistantFromTranscript } from '../../transcript-lines' export function extractClaudeToolFields( eventName: unknown, diff --git a/src/shared/agent-hook-listener/providers/claude-compact-completion.ts b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-compact-completion.ts similarity index 95% rename from src/shared/agent-hook-listener/providers/claude-compact-completion.ts rename to src/shared/agent-hook-listener/providers/claude/lifecycle/claude-compact-completion.ts index fa8a1f5e784..36349230c4a 100644 --- a/src/shared/agent-hook-listener/providers/claude-compact-completion.ts +++ b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-compact-completion.ts @@ -3,8 +3,8 @@ import { agentProviderSessionsEqual, type AgentProviderSessionMetadata -} from '../../agent-session/lifecycle/agent-session-resume' -import type { AgentHookSource } from '../transport/agent-hook-relay' +} from '../../../../agent-session/lifecycle/agent-session-resume' +import type { AgentHookSource } from '../../../transport/agent-hook-relay' export type ClaudeCompactCompletionOwner = { source?: AgentHookSource diff --git a/src/shared/agent-hook-listener/providers/claude-lifecycle-events.ts b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-lifecycle-events.ts similarity index 95% rename from src/shared/agent-hook-listener/providers/claude-lifecycle-events.ts rename to src/shared/agent-hook-listener/providers/claude/lifecycle/claude-lifecycle-events.ts index ec77de80f3f..3a752d90860 100644 --- a/src/shared/agent-hook-listener/providers/claude-lifecycle-events.ts +++ b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-lifecycle-events.ts @@ -1,9 +1,9 @@ -import type { ParsedAgentStatusPayload } from '../../agent-status/contracts/agent-status-types' +import type { ParsedAgentStatusPayload } from '../../../../agent-status/contracts/agent-status-types' import { markClaudeBackgroundAgentRunning, oweClaudeAgentTaskNotification, recordClaudeUnconfirmedAgentEnd -} from './claude-owed-task-notifications' +} from '../background-tasks/claude-owed-task-notifications' import { claudeRosterHasRestoredSnapshotSubagent, claudeRosterHasRuntimeWorkingSubagent, @@ -11,10 +11,10 @@ import { idleClaudeTeammateByName, stopClaudeSubagent, upsertWorkingClaudeSubagent -} from '../../claude-subagent-roster' -import type { HookListenerState } from '../listener-state' +} from '../../../../claude-subagent-roster' +import type { HookListenerState } from '../../../listener-state' import { claudeRowHasUnlistedLiveWork, resolveClaudePaneStatus } from './claude-pane-hold-evidence' -import { readString } from '../tool-input-preview' +import { readString } from '../../../tool-input-preview' import { clearClaudePendingWaitForAgent, getOrCreateClaudeSubagentRoster diff --git a/src/shared/agent-hook-listener/providers/claude-pane-hold-evidence.ts b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-pane-hold-evidence.ts similarity index 88% rename from src/shared/agent-hook-listener/providers/claude-pane-hold-evidence.ts rename to src/shared/agent-hook-listener/providers/claude/lifecycle/claude-pane-hold-evidence.ts index 74b5a91c958..2746ecaae3f 100644 --- a/src/shared/agent-hook-listener/providers/claude-pane-hold-evidence.ts +++ b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-pane-hold-evidence.ts @@ -1,15 +1,15 @@ import { foldAgentLeadStatus, type AgentLeadStatusResolution -} from '../../agent-status/lifecycle/agent-lead-status-fold' -import { agentChildWorkLivenessFromEvidence } from '../../agent-status/child-work/agent-status-child-work-liveness' +} from '../../../../agent-status/lifecycle/agent-lead-status-fold' +import { agentChildWorkLivenessFromEvidence } from '../../../../agent-status/child-work/agent-status-child-work-liveness' import { claudeLiveOwedTaskNotificationKinds, claudeTasksAwaitAgents -} from './claude-owed-task-notifications' -import { claudeRosterHasWorkingSubagent } from '../../claude-subagent-roster' -import type { HookListenerState } from '../listener-state' -import { mainAgentIdleSince, type ClaudeLeadTurnState } from '../main-agent-turn-state' +} from '../background-tasks/claude-owed-task-notifications' +import { claudeRosterHasWorkingSubagent } from '../../../../claude-subagent-roster' +import type { HookListenerState } from '../../../listener-state' +import { mainAgentIdleSince, type ClaudeLeadTurnState } from '../../../main-agent-turn-state' /** Everything that can hold a pane whose main agent's own turn is over. */ type ClaudePaneHoldEvidence = { diff --git a/src/shared/agent-hook-listener/providers/claude-roster-state.ts b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-roster-state.ts similarity index 93% rename from src/shared/agent-hook-listener/providers/claude-roster-state.ts rename to src/shared/agent-hook-listener/providers/claude/lifecycle/claude-roster-state.ts index 2dfbf769611..cbaf4cf4e73 100644 --- a/src/shared/agent-hook-listener/providers/claude-roster-state.ts +++ b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-roster-state.ts @@ -1,22 +1,22 @@ import { AGENT_MODEL_MAX_LENGTH, normalizeOptionalField -} from '../../agent-status/contracts/agent-status-field-normalization' -import type { AgentMainAgentStatus } from '../../agent-status/lifecycle/main-agent-status' +} from '../../../../agent-status/contracts/agent-status-field-normalization' +import type { AgentMainAgentStatus } from '../../../../agent-status/lifecycle/main-agent-status' import type { AgentStatusState, AgentWorkingMode -} from '../../agent-status/contracts/agent-status-types' -import type { AgentSubagentSnapshot } from '../../agent-status/contracts/agent-status-subagent-snapshot' +} from '../../../../agent-status/contracts/agent-status-types' +import type { AgentSubagentSnapshot } from '../../../../agent-status/contracts/agent-status-subagent-snapshot' import { continueMainAgentStatus, mainAgentTurnInterrupted -} from '../../agent-status/lifecycle/agent-lead-status-fold' -import type { ClaudeSubagentRoster } from '../../claude-subagent-roster' -import type { AgentHookEventPayload } from '../listener-event' -import type { HookListenerState } from '../listener-state' -import type { ClaudeLeadTurnState } from '../main-agent-turn-state' -import { readString } from '../tool-input-preview' +} from '../../../../agent-status/lifecycle/agent-lead-status-fold' +import type { ClaudeSubagentRoster } from '../../../../claude-subagent-roster' +import type { AgentHookEventPayload } from '../../../listener-event' +import type { HookListenerState } from '../../../listener-state' +import type { ClaudeLeadTurnState } from '../../../main-agent-turn-state' +import { readString } from '../../../tool-input-preview' import { CLAUDE_NEW_SESSION_START_SOURCES } from './claude-session-start-sources' import { resolveClaudePaneStatus } from './claude-pane-hold-evidence' diff --git a/src/shared/agent-hook-listener/providers/claude-session-start-sources.ts b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-session-start-sources.ts similarity index 100% rename from src/shared/agent-hook-listener/providers/claude-session-start-sources.ts rename to src/shared/agent-hook-listener/providers/claude/lifecycle/claude-session-start-sources.ts diff --git a/src/shared/agent-hook-listener/providers/claude-status-build.ts b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-status-build.ts similarity index 83% rename from src/shared/agent-hook-listener/providers/claude-status-build.ts rename to src/shared/agent-hook-listener/providers/claude/lifecycle/claude-status-build.ts index 8957e6cb192..b22799f9243 100644 --- a/src/shared/agent-hook-listener/providers/claude-status-build.ts +++ b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-status-build.ts @@ -3,12 +3,12 @@ import { type AgentStatusState, type AgentWorkingMode, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import { claudeRosterToSnapshots } from '../../claude-subagent-roster' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import type { HookListenerState } from '../listener-state' -import { mainAgentTurnInterrupted } from '../../agent-status/lifecycle/agent-lead-status-fold' +} from '../../../../agent-status/contracts/agent-status-types' +import { claudeRosterToSnapshots } from '../../../../claude-subagent-roster' +import { resolvePrompt, resolveToolState } from '../../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../../provider-event-routing' +import type { HookListenerState } from '../../../listener-state' +import { mainAgentTurnInterrupted } from '../../../../agent-status/lifecycle/agent-lead-status-fold' import { claudeMainAgentStatusForPayload } from './claude-roster-state' export function buildClaudeStatusPayload( diff --git a/src/shared/agent-hook-listener/providers/claude-teammate-spawn.ts b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-teammate-spawn.ts similarity index 88% rename from src/shared/agent-hook-listener/providers/claude-teammate-spawn.ts rename to src/shared/agent-hook-listener/providers/claude/lifecycle/claude-teammate-spawn.ts index 65926f6037b..ccf3a6b707f 100644 --- a/src/shared/agent-hook-listener/providers/claude-teammate-spawn.ts +++ b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-teammate-spawn.ts @@ -1,9 +1,9 @@ import { isClaudeTeammateLifecycleId, recordClaudeTeammateTaskDescription -} from '../../claude-subagent-roster' -import type { HookListenerState } from '../listener-state' -import { readString } from '../tool-input-preview' +} from '../../../../claude-subagent-roster' +import type { HookListenerState } from '../../../listener-state' +import { readString } from '../../../tool-input-preview' import { getOrCreateClaudeSubagentRoster } from './claude-roster-state' // Captured Claude 2.1.308 names background_tasks entries by the lead Agent call’s spawn description. diff --git a/src/shared/agent-hook-listener/providers/claude-tool-request-wait.ts b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-tool-request-wait.ts similarity index 85% rename from src/shared/agent-hook-listener/providers/claude-tool-request-wait.ts rename to src/shared/agent-hook-listener/providers/claude/lifecycle/claude-tool-request-wait.ts index 9c98ed3729d..336e59cc31a 100644 --- a/src/shared/agent-hook-listener/providers/claude-tool-request-wait.ts +++ b/src/shared/agent-hook-listener/providers/claude/lifecycle/claude-tool-request-wait.ts @@ -1,8 +1,8 @@ -import type { ParsedAgentStatusPayload } from '../../agent-status/contracts/agent-status-types' -import { readClaudeBatchSettledRequests } from '../claude-settled-permission-requests' -import { claudeHasRequestAwaitingUser } from '../claude-tool-request-identity' -import type { HookListenerState } from '../listener-state' -import type { ClaudeLeadTurnState } from '../main-agent-turn-state' +import type { ParsedAgentStatusPayload } from '../../../../agent-status/contracts/agent-status-types' +import { readClaudeBatchSettledRequests } from '../../../claude-settled-permission-requests' +import { claudeHasRequestAwaitingUser } from '../../../claude-tool-request-identity' +import type { HookListenerState } from '../../../listener-state' +import type { ClaudeLeadTurnState } from '../../../main-agent-turn-state' import { buildClaudeCachedLeadStatusPayload } from './claude-lifecycle-events' import { resolveClaudePaneStatus } from './claude-pane-hold-evidence' import { setClaudeMainAgentTurnState } from './claude-roster-state' diff --git a/src/shared/agent-hook-listener/providers/claude-helper-kill-poller.ts b/src/shared/agent-hook-listener/providers/claude/transcripts/claude-helper-kill-poller.ts similarity index 92% rename from src/shared/agent-hook-listener/providers/claude-helper-kill-poller.ts rename to src/shared/agent-hook-listener/providers/claude/transcripts/claude-helper-kill-poller.ts index f4d6861d0e0..1a0761459b6 100644 --- a/src/shared/agent-hook-listener/providers/claude-helper-kill-poller.ts +++ b/src/shared/agent-hook-listener/providers/claude/transcripts/claude-helper-kill-poller.ts @@ -1,15 +1,15 @@ import { dirname, basename, join, posix, win32 } from 'node:path' -import { AgentTranscriptPollScheduler } from '../transcripts/agent-transcript-poll-scheduler' +import { AgentTranscriptPollScheduler } from '../../../transcripts/agent-transcript-poll-scheduler' import { claudeRosterHasRuntimeWorkingSubagent, stopClaudeSubagent -} from '../../claude-subagent-roster' -import { stopOwingClaudeAgentTaskNotification } from './claude-owed-task-notifications' -import type { HookListenerState } from '../listener-state' -import type { AgentHookEventPayload } from '../listener-event' -import { buildClaudeCachedLeadStatusPayload } from './claude-lifecycle-events' -import { clearClaudePendingWaitForAgent } from './claude-roster-state' -import { claudeRowHasUnlistedLiveWork } from './claude-pane-hold-evidence' +} from '../../../../claude-subagent-roster' +import { stopOwingClaudeAgentTaskNotification } from '../background-tasks/claude-owed-task-notifications' +import type { HookListenerState } from '../../../listener-state' +import type { AgentHookEventPayload } from '../../../listener-event' +import { buildClaudeCachedLeadStatusPayload } from '../lifecycle/claude-lifecycle-events' +import { clearClaudePendingWaitForAgent } from '../lifecycle/claude-roster-state' +import { claudeRowHasUnlistedLiveWork } from '../lifecycle/claude-pane-hold-evidence' import { createClaudeTranscriptCursor, clearClaudeTranscriptCursor, diff --git a/src/shared/agent-hook-listener/providers/claude-helper-transcript-evidence.ts b/src/shared/agent-hook-listener/providers/claude/transcripts/claude-helper-transcript-evidence.ts similarity index 97% rename from src/shared/agent-hook-listener/providers/claude-helper-transcript-evidence.ts rename to src/shared/agent-hook-listener/providers/claude/transcripts/claude-helper-transcript-evidence.ts index 24c750aa1a7..deb7ff71a46 100644 --- a/src/shared/agent-hook-listener/providers/claude-helper-transcript-evidence.ts +++ b/src/shared/agent-hook-listener/providers/claude/transcripts/claude-helper-transcript-evidence.ts @@ -1,10 +1,10 @@ -import { isClaudeToolResultContent } from '../../claude-tool-content' +import { isClaudeToolResultContent } from '../../../../claude-tool-content' import { setImmediate as yieldToEventLoop } from 'node:timers/promises' import type { ClaudeTranscriptCursor, ClaudeTranscriptApplication } from './claude-transcript-cursor' -import { isKnownHarnessInjectedUserTurnText } from '../../harness-injected-user-turns' +import { isKnownHarnessInjectedUserTurnText } from '../../../../harness-injected-user-turns' export type ClaudeHelperTranscriptEvidence = { observedAt: number diff --git a/src/shared/agent-hook-listener/providers/claude-transcript-cursor.ts b/src/shared/agent-hook-listener/providers/claude/transcripts/claude-transcript-cursor.ts similarity index 100% rename from src/shared/agent-hook-listener/providers/claude-transcript-cursor.ts rename to src/shared/agent-hook-listener/providers/claude/transcripts/claude-transcript-cursor.ts diff --git a/src/shared/agent-hook-listener/providers/claude-transcript-decoder.ts b/src/shared/agent-hook-listener/providers/claude/transcripts/claude-transcript-decoder.ts similarity index 97% rename from src/shared/agent-hook-listener/providers/claude-transcript-decoder.ts rename to src/shared/agent-hook-listener/providers/claude/transcripts/claude-transcript-decoder.ts index e2dfdc3f91b..45134956778 100644 --- a/src/shared/agent-hook-listener/providers/claude-transcript-decoder.ts +++ b/src/shared/agent-hook-listener/providers/claude/transcripts/claude-transcript-decoder.ts @@ -1,8 +1,8 @@ import parser, { type Token } from 'stream-json/core/parser.js' import exec from 'stream-chain/exec.js' import { none } from 'stream-chain/defs.js' -import { isKnownHarnessInjectedUserTurnText } from '../../harness-injected-user-turns' -import { isClaudeToolResultContent } from '../../claude-tool-content' +import { isKnownHarnessInjectedUserTurnText } from '../../../../harness-injected-user-turns' +import { isClaudeToolResultContent } from '../../../../claude-tool-content' type Scope = 'root' | 'message' | 'content' | 'block' | 'result' | 'discard' type Frame = { diff --git a/src/shared/agent-hook-listener/providers/claude/transcripts/claude-transcript-semantics.test.ts b/src/shared/agent-hook-listener/providers/claude/transcripts/claude-transcript-semantics.test.ts new file mode 100644 index 00000000000..26d788d0b27 --- /dev/null +++ b/src/shared/agent-hook-listener/providers/claude/transcripts/claude-transcript-semantics.test.ts @@ -0,0 +1,34 @@ +import { mkdtemp, writeFile, appendFile, rm } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { createClaudeTranscriptCursor, readClaudeTranscriptChunk } from './claude-transcript-cursor' + +let dir: string +const now = Date.parse('2026-10-29T12:00:00Z') +const stamp = (offset: number) => new Date(now + offset).toISOString() +const kill = (offset = 1) => + `${JSON.stringify({ type: 'system', subtype: 'agents_killed', timestamp: stamp(offset) })}\n` +const prompt = (offset: number) => + `${JSON.stringify({ type: 'user', timestamp: stamp(offset), message: { content: 'Do the work' } })}\n` +beforeEach(async () => { + dir = await mkdtemp(join(tmpdir(), 'claude-transcript-semantics-')) +}) +afterEach(async () => { + vi.restoreAllMocks() + await rm(dir, { recursive: true, force: true }) +}) + +describe('forward transcript reading', () => { + it('treats missing files as unverifiable and replacement/shrink as a new baseline', async () => { + const path = join(dir, 'replace.jsonl') + const cursor = createClaudeTranscriptCursor(path) + expect(await readClaudeTranscriptChunk(cursor)).toBeUndefined() + await writeFile(path, `${prompt(0)}${' '.repeat(200)}`) + await readClaudeTranscriptChunk(cursor) + await writeFile(path, kill()) + expect((await readClaudeTranscriptChunk(cursor))?.records).toEqual([]) + await appendFile(path, kill(20)) + expect((await readClaudeTranscriptChunk(cursor))?.records).toHaveLength(1) + }) +}) diff --git a/src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-main-agent.test.ts b/src/shared/agent-hook-listener/providers/codex/agent-hook-listener-codex-main-agent.test.ts similarity index 92% rename from src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-main-agent.test.ts rename to src/shared/agent-hook-listener/providers/codex/agent-hook-listener-codex-main-agent.test.ts index ee52fc1fe24..d4947b0e9a0 100644 --- a/src/shared/agent-hook-listener/normalization/agent-hook-listener-codex-main-agent.test.ts +++ b/src/shared/agent-hook-listener/providers/codex/agent-hook-listener-codex-main-agent.test.ts @@ -1,11 +1,11 @@ import { beforeEach, describe, expect, it } from 'vitest' -import { createHookListenerState, type HookListenerState } from '../listener-state' +import { createHookListenerState, type HookListenerState } from '../../listener-state' import { markCodexLeadTurnInterrupted, reconcileRemoteCodexState, seedCodexStateFromSnapshot -} from '../providers/codex-state' -import { PANE_KEY } from './agent-hook-listener-test-harness' +} from './codex-state' +import { PANE_KEY } from '../../normalization/agent-hook-listener-test-harness' describe('the Codex root record seeded from a durable row', () => { let state: HookListenerState diff --git a/src/shared/agent-hook-listener/providers/codex-async-questions.ts b/src/shared/agent-hook-listener/providers/codex/codex-async-questions.ts similarity index 96% rename from src/shared/agent-hook-listener/providers/codex-async-questions.ts rename to src/shared/agent-hook-listener/providers/codex/codex-async-questions.ts index 4f0e47921f9..ed117fd04ae 100644 --- a/src/shared/agent-hook-listener/providers/codex-async-questions.ts +++ b/src/shared/agent-hook-listener/providers/codex/codex-async-questions.ts @@ -1,9 +1,9 @@ import { CODEX_ASYNC_QUESTION_TOOL, parseCodexAsyncQuestionsInput -} from '../../codex-async-question-input' -import type { HookListenerState } from '../listener-state' -import { readString } from '../tool-input-preview' +} from '../../../codex-async-question-input' +import type { HookListenerState } from '../../listener-state' +import { readString } from '../../tool-input-preview' /** One `request_user_input_async` call and the question indexes it still waits on. */ type CodexAsyncQuestionCall = { diff --git a/src/shared/agent-hook-listener/providers/codex-ended-turn-event.ts b/src/shared/agent-hook-listener/providers/codex/codex-ended-turn-event.ts similarity index 79% rename from src/shared/agent-hook-listener/providers/codex-ended-turn-event.ts rename to src/shared/agent-hook-listener/providers/codex/codex-ended-turn-event.ts index 3ef0a57c3fe..11d15729ad7 100644 --- a/src/shared/agent-hook-listener/providers/codex-ended-turn-event.ts +++ b/src/shared/agent-hook-listener/providers/codex/codex-ended-turn-event.ts @@ -1,6 +1,6 @@ -import { codexTranscriptTurnEnd } from '../../codex-subagent-transcript' -import type { HookListenerState } from '../listener-state' -import { readString } from '../tool-input-preview' +import { codexTranscriptTurnEnd } from '../../../codex-subagent-transcript' +import type { HookListenerState } from '../../listener-state' +import { readString } from '../../tool-input-preview' /** Replay rollout-confirmed root ends as missing Stops, including /review; drop delayed hooks once the lead moved to another turn. */ export function codexHookEventForEndedTurn( diff --git a/src/shared/agent-hook-listener/providers/codex-ephemeral-threads.ts b/src/shared/agent-hook-listener/providers/codex/codex-ephemeral-threads.ts similarity index 94% rename from src/shared/agent-hook-listener/providers/codex-ephemeral-threads.ts rename to src/shared/agent-hook-listener/providers/codex/codex-ephemeral-threads.ts index f4f1daf6b66..ef0ba3dd290 100644 --- a/src/shared/agent-hook-listener/providers/codex-ephemeral-threads.ts +++ b/src/shared/agent-hook-listener/providers/codex/codex-ephemeral-threads.ts @@ -1,6 +1,6 @@ -import type { AgentHookEventPayload } from '../listener-event' -import type { HookListenerState } from '../listener-state' -import { readString } from '../tool-input-preview' +import type { AgentHookEventPayload } from '../../listener-event' +import type { HookListenerState } from '../../listener-state' +import { readString } from '../../tool-input-preview' // Enough for any run of /side chats in one pane; the oldest ended long ago. const MAX_FORKS_PER_PANE = 16 diff --git a/src/shared/agent-hook-listener/providers/codex-events.ts b/src/shared/agent-hook-listener/providers/codex/codex-events.ts similarity index 93% rename from src/shared/agent-hook-listener/providers/codex-events.ts rename to src/shared/agent-hook-listener/providers/codex/codex-events.ts index aaff6d57a3d..def9de822a5 100644 --- a/src/shared/agent-hook-listener/providers/codex-events.ts +++ b/src/shared/agent-hook-listener/providers/codex/codex-events.ts @@ -1,35 +1,35 @@ import { AGENT_MODEL_MAX_LENGTH, normalizeOptionalField -} from '../../agent-status/contracts/agent-status-field-normalization' +} from '../../../agent-status/contracts/agent-status-field-normalization' import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' +} from '../../../agent-status/contracts/agent-status-types' import { mainAgentTurnInterrupted, type AgentLeadStatusResolution -} from '../../agent-status/lifecycle/agent-lead-status-fold' +} from '../../../agent-status/lifecycle/agent-lead-status-fold' import { codexRosterToSnapshots, enrichCodexSubagentIdentity, finishCodexSubagent, upsertCodexSubagent -} from '../../codex-subagent-roster' +} from '../../../codex-subagent-roster' import { codexTranscriptTurnEnd, reconcileCodexSubagentTranscript -} from '../../codex-subagent-transcript' +} from '../../../codex-subagent-transcript' import { codexTurnApprovalsAreAutoReviewed, reconcileCodexSubagentReviewer -} from '../../codex-subagent-reviewer' -import { readFirstString } from '../interactive-tool' -import type { HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { readString } from '../tool-input-preview' +} from '../../../codex-subagent-reviewer' +import { readFirstString } from '../../interactive-tool' +import type { HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { readString } from '../../tool-input-preview' import { applyCodexSessionStart } from './codex-session-start' import { codexHookEventState, @@ -302,5 +302,3 @@ export function normalizeCodexEvent( updateLead: true }) } - -export { codexHookEventForEndedTurn } from './codex-ended-turn-event' diff --git a/src/shared/agent-hook-listener/providers/codex-question-skip.ts b/src/shared/agent-hook-listener/providers/codex/codex-question-skip.ts similarity index 92% rename from src/shared/agent-hook-listener/providers/codex-question-skip.ts rename to src/shared/agent-hook-listener/providers/codex/codex-question-skip.ts index cce40781eb0..ef6f3c716e9 100644 --- a/src/shared/agent-hook-listener/providers/codex-question-skip.ts +++ b/src/shared/agent-hook-listener/providers/codex/codex-question-skip.ts @@ -1,5 +1,5 @@ -import type { AgentHookEventPayload } from '../listener-event' -import type { HookListenerState } from '../listener-state' +import type { AgentHookEventPayload } from '../../listener-event' +import type { HookListenerState } from '../../listener-state' import { pendingCodexAsyncQuestion, skipCodexAsyncQuestion } from './codex-async-questions' import { buildCodexStatusPayload } from './codex-events' import { resolveCodexPaneStatus, setCodexMainAgentTurnState } from './codex-state' diff --git a/src/shared/agent-hook-listener/providers/codex-session-start.ts b/src/shared/agent-hook-listener/providers/codex/codex-session-start.ts similarity index 95% rename from src/shared/agent-hook-listener/providers/codex-session-start.ts rename to src/shared/agent-hook-listener/providers/codex/codex-session-start.ts index 9dc8ad63ce2..6d829ecae83 100644 --- a/src/shared/agent-hook-listener/providers/codex-session-start.ts +++ b/src/shared/agent-hook-listener/providers/codex/codex-session-start.ts @@ -1,15 +1,15 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' +} from '../../../agent-status/contracts/agent-status-types' import { sameAgentStatusSessionOwner, sameAgentStatusTerminalOwner -} from '../../agent-status/identity/agent-status-terminal-owner' -import type { AgentLeadStatusResolution } from '../../agent-status/lifecycle/agent-lead-status-fold' -import { codexRosterToSnapshots, type CodexSubagentRoster } from '../../codex-subagent-roster' -import type { AgentHookEventPayload } from '../listener-event' -import type { HookListenerState } from '../listener-state' +} from '../../../agent-status/identity/agent-status-terminal-owner' +import type { AgentLeadStatusResolution } from '../../../agent-status/lifecycle/agent-lead-status-fold' +import { codexRosterToSnapshots, type CodexSubagentRoster } from '../../../codex-subagent-roster' +import type { AgentHookEventPayload } from '../../listener-event' +import type { HookListenerState } from '../../listener-state' import { codexMainAgentStatusForPayload, reconcileRemoteCodexState, diff --git a/src/shared/agent-hook-listener/providers/codex-state.ts b/src/shared/agent-hook-listener/providers/codex/codex-state.ts similarity index 94% rename from src/shared/agent-hook-listener/providers/codex-state.ts rename to src/shared/agent-hook-listener/providers/codex/codex-state.ts index 39e9d88f518..96ad622aa0c 100644 --- a/src/shared/agent-hook-listener/providers/codex-state.ts +++ b/src/shared/agent-hook-listener/providers/codex/codex-state.ts @@ -1,27 +1,27 @@ -import type { AgentMainAgentStatus } from '../../agent-status/lifecycle/main-agent-status' -import type { ParsedAgentStatusPayload } from '../../agent-status/contracts/agent-status-types' -import { isAskUserQuestionTool } from '../../agent-status/lifecycle/agent-question-answered-intent' -import { CODEX_ASYNC_QUESTION_TOOL } from '../../codex-async-question-input' +import type { AgentMainAgentStatus } from '../../../agent-status/lifecycle/main-agent-status' +import type { ParsedAgentStatusPayload } from '../../../agent-status/contracts/agent-status-types' +import { isAskUserQuestionTool } from '../../../agent-status/lifecycle/agent-question-answered-intent' +import { CODEX_ASYNC_QUESTION_TOOL } from '../../../codex-async-question-input' import { continueMainAgentStatus, foldAgentLeadStatus, mainAgentTurnInterrupted, type AgentLeadStatusResolution -} from '../../agent-status/lifecycle/agent-lead-status-fold' +} from '../../../agent-status/lifecycle/agent-lead-status-fold' import { codexRosterChildWorkLiveness, codexRosterToSnapshots, finishCodexSubagent, seedCodexSubagentRoster, type CodexSubagentRoster -} from '../../codex-subagent-roster' +} from '../../../codex-subagent-roster' import { createCodexSubagentTranscriptState, hasTrackedCodexTranscriptSubagents, type CodexSubagentTranscriptState -} from '../../codex-subagent-transcript' -import type { HookListenerState } from '../listener-state' -import type { CodexLeadTurnState } from '../main-agent-turn-state' +} from '../../../codex-subagent-transcript' +import type { HookListenerState } from '../../listener-state' +import type { CodexLeadTurnState } from '../../main-agent-turn-state' export function getOrCreateCodexSubagentRoster( state: HookListenerState, diff --git a/src/shared/agent-hook-listener/providers/codex-tool-fields.ts b/src/shared/agent-hook-listener/providers/codex/codex-tool-fields.ts similarity index 88% rename from src/shared/agent-hook-listener/providers/codex-tool-fields.ts rename to src/shared/agent-hook-listener/providers/codex/codex-tool-fields.ts index 09b87c794a5..a3271f2f9c1 100644 --- a/src/shared/agent-hook-listener/providers/codex-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/codex/codex-tool-fields.ts @@ -1,11 +1,11 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveToolInputPreview, hasAnyOwnField, readString, toolUpdate -} from '../tool-input-preview' -import { deriveInteractivePrompt } from '../interactive-tool' +} from '../../tool-input-preview' +import { deriveInteractivePrompt } from '../../interactive-tool' export function extractCodexToolFields( eventName: unknown, diff --git a/src/shared/agent-hook-listener/providers/codex-transcript-poll.ts b/src/shared/agent-hook-listener/providers/codex/codex-transcript-poll.ts similarity index 87% rename from src/shared/agent-hook-listener/providers/codex-transcript-poll.ts rename to src/shared/agent-hook-listener/providers/codex/codex-transcript-poll.ts index 608994e21d5..d7cc2f32371 100644 --- a/src/shared/agent-hook-listener/providers/codex-transcript-poll.ts +++ b/src/shared/agent-hook-listener/providers/codex/codex-transcript-poll.ts @@ -1,15 +1,15 @@ -import { record } from '../../codex-rollout-jsonl-cursor' +import { record } from '../../../codex-rollout-jsonl-cursor' import { hasPendingCodexSubagentIdentity, retryPendingCodexSubagentIdentities -} from '../../codex-subagent-roster' +} from '../../../codex-subagent-roster' import { codexTranscriptTurnEnd, reconcileCodexSubagentTranscript -} from '../../codex-subagent-transcript' -import { normalizeHookPayload } from '../normalization/normalize-hook-payload' -import type { AgentHookEventPayload } from '../listener-event' -import type { HookListenerState } from '../listener-state' +} from '../../../codex-subagent-transcript' +import { normalizeHookPayload } from '../../normalization/normalize-hook-payload' +import type { AgentHookEventPayload } from '../../listener-event' +import type { HookListenerState } from '../../listener-state' import { buildCodexChildDrivenStatusPayload } from './codex-events' import { getOrCreateCodexSubagentRoster, markCodexLeadTurnInterrupted } from './codex-state' diff --git a/src/shared/agent-hook-listener/providers/command-code-events.ts b/src/shared/agent-hook-listener/providers/command-code/command-code-events.ts similarity index 80% rename from src/shared/agent-hook-listener/providers/command-code-events.ts rename to src/shared/agent-hook-listener/providers/command-code/command-code-events.ts index a86d56deeda..dc762a7443b 100644 --- a/src/shared/agent-hook-listener/providers/command-code-events.ts +++ b/src/shared/agent-hook-listener/providers/command-code/command-code-events.ts @@ -1,10 +1,10 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import type { HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' +} from '../../../agent-status/contracts/agent-status-types' +import type { HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' export function normalizeCommandCodeEvent( state: HookListenerState, diff --git a/src/shared/agent-hook-listener/providers/command-code-tool-fields.ts b/src/shared/agent-hook-listener/providers/command-code/command-code-tool-fields.ts similarity index 86% rename from src/shared/agent-hook-listener/providers/command-code-tool-fields.ts rename to src/shared/agent-hook-listener/providers/command-code/command-code-tool-fields.ts index 5d785d832c3..c92fdc0a3c6 100644 --- a/src/shared/agent-hook-listener/providers/command-code-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/command-code/command-code-tool-fields.ts @@ -1,13 +1,13 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveFallbackToolInputPreview, deriveToolInputPreview, hasOwnField, readString, toolUpdate -} from '../tool-input-preview' -import { extractToolResponseText } from '../interactive-tool' -import { readLastCommandCodeAssistantFromTranscript } from '../command-code-transcript' +} from '../../tool-input-preview' +import { extractToolResponseText } from '../../interactive-tool' +import { readLastCommandCodeAssistantFromTranscript } from '../../command-code-transcript' export function extractCommandCodeToolFields( eventName: unknown, diff --git a/src/shared/agent-hook-listener/providers/copilot-events.ts b/src/shared/agent-hook-listener/providers/copilot/copilot-events.ts similarity index 88% rename from src/shared/agent-hook-listener/providers/copilot-events.ts rename to src/shared/agent-hook-listener/providers/copilot/copilot-events.ts index 9410202769a..9cb2b009f49 100644 --- a/src/shared/agent-hook-listener/providers/copilot-events.ts +++ b/src/shared/agent-hook-listener/providers/copilot/copilot-events.ts @@ -1,11 +1,11 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import { readFirstString } from '../interactive-tool' -import type { HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' +} from '../../../agent-status/contracts/agent-status-types' +import { readFirstString } from '../../interactive-tool' +import type { HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' import { isAskUserTool, normalizeCopilotEventName, diff --git a/src/shared/agent-hook-listener/providers/copilot-tool-fields.ts b/src/shared/agent-hook-listener/providers/copilot/copilot-tool-fields.ts similarity index 97% rename from src/shared/agent-hook-listener/providers/copilot-tool-fields.ts rename to src/shared/agent-hook-listener/providers/copilot/copilot-tool-fields.ts index 491be19a30f..f5b99ec90c9 100644 --- a/src/shared/agent-hook-listener/providers/copilot-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/copilot/copilot-tool-fields.ts @@ -1,17 +1,17 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveToolInputPreview, hasAnyOwnField, readString, toolUpdate -} from '../tool-input-preview' +} from '../../tool-input-preview' import { clearActiveToolFieldsUpdate, extractToolResponseText, parseJsonObjectString, readFirstString -} from '../interactive-tool' -import { readLastAssistantFromTranscript } from '../transcript-lines' +} from '../../interactive-tool' +import { readLastAssistantFromTranscript } from '../../transcript-lines' export function normalizeCopilotEventName(eventName: unknown): unknown { if (typeof eventName !== 'string') { diff --git a/src/shared/agent-hook-listener/providers/cursor-events.ts b/src/shared/agent-hook-listener/providers/cursor/cursor-events.ts similarity index 88% rename from src/shared/agent-hook-listener/providers/cursor-events.ts rename to src/shared/agent-hook-listener/providers/cursor/cursor-events.ts index e45fff8b06b..28ecfcb04ad 100644 --- a/src/shared/agent-hook-listener/providers/cursor-events.ts +++ b/src/shared/agent-hook-listener/providers/cursor/cursor-events.ts @@ -1,10 +1,10 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import type { HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' +} from '../../../agent-status/contracts/agent-status-types' +import type { HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' export function normalizeCursorEvent( state: HookListenerState, diff --git a/src/shared/agent-hook-listener/providers/cursor-tool-fields.ts b/src/shared/agent-hook-listener/providers/cursor/cursor-tool-fields.ts similarity index 95% rename from src/shared/agent-hook-listener/providers/cursor-tool-fields.ts rename to src/shared/agent-hook-listener/providers/cursor/cursor-tool-fields.ts index 7e0ab173c18..ed47925fa75 100644 --- a/src/shared/agent-hook-listener/providers/cursor-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/cursor/cursor-tool-fields.ts @@ -1,12 +1,12 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveToolInputPreview, hasAnyOwnField, hasOwnField, readString, toolUpdate -} from '../tool-input-preview' -import { clearActiveToolFieldsUpdate, extractToolResponseText } from '../interactive-tool' +} from '../../tool-input-preview' +import { clearActiveToolFieldsUpdate, extractToolResponseText } from '../../interactive-tool' export function extractCursorToolFields( eventName: unknown, diff --git a/src/shared/agent-hook-listener/providers/devin-events.ts b/src/shared/agent-hook-listener/providers/devin/devin-events.ts similarity index 89% rename from src/shared/agent-hook-listener/providers/devin-events.ts rename to src/shared/agent-hook-listener/providers/devin/devin-events.ts index 89c203ceb5f..8e23302dba2 100644 --- a/src/shared/agent-hook-listener/providers/devin-events.ts +++ b/src/shared/agent-hook-listener/providers/devin/devin-events.ts @@ -1,10 +1,10 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import { clearPaneTurnCacheState, type HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' +} from '../../../agent-status/contracts/agent-status-types' +import { clearPaneTurnCacheState, type HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' // Why: Devin uses Claude-compatible payloads but its own lifecycle event set; normalize those event names while keeping Devin attribution. export function normalizeDevinEvent( diff --git a/src/shared/agent-hook-listener/providers/droid-events.ts b/src/shared/agent-hook-listener/providers/droid/droid-events.ts similarity index 90% rename from src/shared/agent-hook-listener/providers/droid-events.ts rename to src/shared/agent-hook-listener/providers/droid/droid-events.ts index 8a7de57105a..61e56171f2d 100644 --- a/src/shared/agent-hook-listener/providers/droid-events.ts +++ b/src/shared/agent-hook-listener/providers/droid/droid-events.ts @@ -1,11 +1,11 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import { clearPaneTurnCacheState, type HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { readString } from '../tool-input-preview' +} from '../../../agent-status/contracts/agent-status-types' +import { clearPaneTurnCacheState, type HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { readString } from '../../tool-input-preview' import { isDroidAskUserTool, isDroidHighRiskToolUse, diff --git a/src/shared/agent-hook-listener/providers/droid-tool-fields.ts b/src/shared/agent-hook-listener/providers/droid/droid-tool-fields.ts similarity index 93% rename from src/shared/agent-hook-listener/providers/droid-tool-fields.ts rename to src/shared/agent-hook-listener/providers/droid/droid-tool-fields.ts index 1f88f2da439..52561f4f2bd 100644 --- a/src/shared/agent-hook-listener/providers/droid-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/droid/droid-tool-fields.ts @@ -1,12 +1,12 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveToolInputPreview, hasAnyOwnField, readString, toolUpdate -} from '../tool-input-preview' -import { extractToolResponseText } from '../interactive-tool' -import { readLastAssistantFromTranscript } from '../transcript-lines' +} from '../../tool-input-preview' +import { extractToolResponseText } from '../../interactive-tool' +import { readLastAssistantFromTranscript } from '../../transcript-lines' export function isDroidPermissionNotification(message: string | undefined): boolean { if (!message) { diff --git a/src/shared/agent-hook-listener/providers/dsh-events.test.ts b/src/shared/agent-hook-listener/providers/dsh/dsh-events.test.ts similarity index 93% rename from src/shared/agent-hook-listener/providers/dsh-events.test.ts rename to src/shared/agent-hook-listener/providers/dsh/dsh-events.test.ts index c64cce02b74..5d2503b6819 100644 --- a/src/shared/agent-hook-listener/providers/dsh-events.test.ts +++ b/src/shared/agent-hook-listener/providers/dsh/dsh-events.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it } from 'vitest' -import { createHookListenerState, type HookListenerState } from '../listener-state' -import { normalizeAndAccept } from '../normalization/agent-hook-listener-test-harness' +import { createHookListenerState, type HookListenerState } from '../../listener-state' +import { normalizeAndAccept } from '../../normalization/agent-hook-listener-test-harness' // Real @deepseek-ai/dsh-hooks-claude-code0.1.5-rc.3 payloads: session/cwd/event fields and empty transcript_path, mounted through DSH_HOME/cordis.patch.yml. const SESSION_ID = '9478e2d8-29bc-4009-ab32-657efa2bd763' diff --git a/src/shared/agent-hook-listener/providers/dsh-events.ts b/src/shared/agent-hook-listener/providers/dsh/dsh-events.ts similarity index 84% rename from src/shared/agent-hook-listener/providers/dsh-events.ts rename to src/shared/agent-hook-listener/providers/dsh/dsh-events.ts index 74675e79feb..997151d3a49 100644 --- a/src/shared/agent-hook-listener/providers/dsh-events.ts +++ b/src/shared/agent-hook-listener/providers/dsh/dsh-events.ts @@ -1,16 +1,16 @@ -import { isAskUserQuestionTool } from '../../agent-status/lifecycle/agent-question-answered-intent' +import { isAskUserQuestionTool } from '../../../agent-status/lifecycle/agent-question-answered-intent' import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import type { HookListenerState } from '../listener-state' +} from '../../../agent-status/contracts/agent-status-types' +import type { HookListenerState } from '../../listener-state' import { resolvePrompt, resolveToolState, shouldIgnoreCompactContinuationUserPromptSubmit -} from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { readString } from '../tool-input-preview' +} from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { readString } from '../../tool-input-preview' /** DSH's Claude-shaped bridge lacks Notification/PermissionRequest. ask_user_question blocks between Pre/PostToolUse; unobservable approval pauses remain working. */ export function normalizeDshEvent( diff --git a/src/shared/agent-hook-listener/providers/gemini-events.ts b/src/shared/agent-hook-listener/providers/gemini/gemini-events.ts similarity index 83% rename from src/shared/agent-hook-listener/providers/gemini-events.ts rename to src/shared/agent-hook-listener/providers/gemini/gemini-events.ts index 894135d9aeb..940119a8a03 100644 --- a/src/shared/agent-hook-listener/providers/gemini-events.ts +++ b/src/shared/agent-hook-listener/providers/gemini/gemini-events.ts @@ -1,10 +1,10 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import type { HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' +} from '../../../agent-status/contracts/agent-status-types' +import type { HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' export function normalizeGeminiEvent( state: HookListenerState, diff --git a/src/shared/agent-hook-listener/providers/gemini-tool-fields.ts b/src/shared/agent-hook-listener/providers/gemini/gemini-tool-fields.ts similarity index 91% rename from src/shared/agent-hook-listener/providers/gemini-tool-fields.ts rename to src/shared/agent-hook-listener/providers/gemini/gemini-tool-fields.ts index 9fadfea6e3c..40bbe9b960f 100644 --- a/src/shared/agent-hook-listener/providers/gemini-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/gemini/gemini-tool-fields.ts @@ -1,10 +1,10 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveToolInputPreview, hasAnyOwnField, readString, toolUpdate -} from '../tool-input-preview' +} from '../../tool-input-preview' export function extractGeminiToolFields( eventName: unknown, diff --git a/src/shared/agent-hook-listener/providers/grok-background-tasks.ts b/src/shared/agent-hook-listener/providers/grok/grok-background-tasks.ts similarity index 89% rename from src/shared/agent-hook-listener/providers/grok-background-tasks.ts rename to src/shared/agent-hook-listener/providers/grok/grok-background-tasks.ts index 558f8d0bf2f..c503aa9d3ad 100644 --- a/src/shared/agent-hook-listener/providers/grok-background-tasks.ts +++ b/src/shared/agent-hook-listener/providers/grok/grok-background-tasks.ts @@ -1,12 +1,12 @@ -import type { AgentChildWorkKind } from '../../agent-status/child-work/agent-status-child-work' +import type { AgentChildWorkKind } from '../../../agent-status/child-work/agent-status-child-work' import { agentChildWorkLiveness, type AgentChildWorkLiveness -} from '../../agent-status/child-work/agent-status-child-work-liveness' -import { isRecord } from '../../agent-status/child-work/agent-status-child-work-value-guards' -import type { HookListenerState } from '../listener-state' -import { isGrokEvent } from '../provider-event-names' -import { readString } from '../tool-input-preview' +} from '../../../agent-status/child-work/agent-status-child-work-liveness' +import { isRecord } from '../../../agent-status/child-work/agent-status-child-work-value-guards' +import type { HookListenerState } from '../../listener-state' +import { isGrokEvent } from '../../provider-event-names' +import { readString } from '../../tool-input-preview' import { getGrokNotificationType } from './grok-tool-fields' // Grok's own task ids per pane, since StopCancelled carries no inventory. A child session's id is its diff --git a/src/shared/agent-hook-listener/providers/grok-events.ts b/src/shared/agent-hook-listener/providers/grok/grok-events.ts similarity index 93% rename from src/shared/agent-hook-listener/providers/grok-events.ts rename to src/shared/agent-hook-listener/providers/grok/grok-events.ts index a2ac1ce621f..9ba5f9f2e68 100644 --- a/src/shared/agent-hook-listener/providers/grok-events.ts +++ b/src/shared/agent-hook-listener/providers/grok/grok-events.ts @@ -1,23 +1,23 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import { isAskUserQuestionTool } from '../../agent-status/lifecycle/agent-question-answered-intent' +} from '../../../agent-status/contracts/agent-status-types' +import { isAskUserQuestionTool } from '../../../agent-status/lifecycle/agent-question-answered-intent' import { continueMainAgentStatus, foldAgentLeadStatus -} from '../../agent-status/lifecycle/agent-lead-status-fold' +} from '../../../agent-status/lifecycle/agent-lead-status-fold' import { agentChildWorkLiveness, type AgentChildWorkLiveness, type AgentChildWorkLivenessCandidate -} from '../../agent-status/child-work/agent-status-child-work-liveness' -import { clearPaneTurnCacheState, type HookListenerState } from '../listener-state' -import { normalizeGrokPromptId } from '../listener-limits' -import { resolvePrompt, resolveToolState, stripGrokUserQueryWrapper } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { readString } from '../tool-input-preview' -import { isGrokEvent } from '../provider-event-names' +} from '../../../agent-status/child-work/agent-status-child-work-liveness' +import { clearPaneTurnCacheState, type HookListenerState } from '../../listener-state' +import { normalizeGrokPromptId } from '../../listener-limits' +import { resolvePrompt, resolveToolState, stripGrokUserQueryWrapper } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { readString } from '../../tool-input-preview' +import { isGrokEvent } from '../../provider-event-names' import { grokBackgroundTaskLiveness, grokFiniteTaskKind, diff --git a/src/shared/agent-hook-listener/providers/grok-tool-fields.ts b/src/shared/agent-hook-listener/providers/grok/grok-tool-fields.ts similarity index 92% rename from src/shared/agent-hook-listener/providers/grok-tool-fields.ts rename to src/shared/agent-hook-listener/providers/grok/grok-tool-fields.ts index fab7c0b962c..d3e9b118800 100644 --- a/src/shared/agent-hook-listener/providers/grok-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/grok/grok-tool-fields.ts @@ -1,19 +1,19 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveFallbackToolInputPreview, deriveToolInputPreview, hasAnyOwnField, readString, toolUpdate -} from '../tool-input-preview' +} from '../../tool-input-preview' import { clearActiveToolFieldsUpdate, deriveInteractivePrompt, extractToolResponseText -} from '../interactive-tool' -import { readLastAssistantFromTranscript } from '../transcript-lines' -import { readLastAssistantFromGrokChatHistory } from '../grok-result-discovery' -import { isGrokEvent } from '../provider-event-names' +} from '../../interactive-tool' +import { readLastAssistantFromTranscript } from '../../transcript-lines' +import { readLastAssistantFromGrokChatHistory } from '../../grok-result-discovery' +import { isGrokEvent } from '../../provider-event-names' export function extractGrokToolFields( eventName: unknown, diff --git a/src/shared/agent-hook-listener/providers/hermes-events.test.ts b/src/shared/agent-hook-listener/providers/hermes/hermes-events.test.ts similarity index 99% rename from src/shared/agent-hook-listener/providers/hermes-events.test.ts rename to src/shared/agent-hook-listener/providers/hermes/hermes-events.test.ts index 27f40b21947..00dfc756d09 100644 --- a/src/shared/agent-hook-listener/providers/hermes-events.test.ts +++ b/src/shared/agent-hook-listener/providers/hermes/hermes-events.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createHookListenerState, type HookListenerState } from '../listener-state' +import { createHookListenerState, type HookListenerState } from '../../listener-state' import { normalizeHermesEvent } from './hermes-events' describe('normalizeHermesEvent', () => { diff --git a/src/shared/agent-hook-listener/providers/hermes-events.ts b/src/shared/agent-hook-listener/providers/hermes/hermes-events.ts similarity index 93% rename from src/shared/agent-hook-listener/providers/hermes-events.ts rename to src/shared/agent-hook-listener/providers/hermes/hermes-events.ts index 8572bc025a9..bf01a2e2d12 100644 --- a/src/shared/agent-hook-listener/providers/hermes-events.ts +++ b/src/shared/agent-hook-listener/providers/hermes/hermes-events.ts @@ -1,10 +1,10 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import type { HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' +} from '../../../agent-status/contracts/agent-status-types' +import type { HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' /** Session open/switch/reset is idle, not a turn. done+sessionBoundary avoids phantom work and 30-minute tui-idle vetoes. */ const HERMES_EVENT_STATES: Record = { diff --git a/src/shared/agent-hook-listener/providers/hermes-tool-fields.ts b/src/shared/agent-hook-listener/providers/hermes/hermes-tool-fields.ts similarity index 93% rename from src/shared/agent-hook-listener/providers/hermes-tool-fields.ts rename to src/shared/agent-hook-listener/providers/hermes/hermes-tool-fields.ts index 865e92b20ff..45e82d2dd68 100644 --- a/src/shared/agent-hook-listener/providers/hermes-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/hermes/hermes-tool-fields.ts @@ -1,12 +1,12 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveFallbackToolInputPreview, deriveToolInputPreview, hasAnyOwnField, readString, toolUpdate -} from '../tool-input-preview' -import { extractToolResponseText } from '../interactive-tool' +} from '../../tool-input-preview' +import { extractToolResponseText } from '../../interactive-tool' export function extractHermesToolFields( eventName: unknown, diff --git a/src/shared/agent-hook-listener/providers/jcode-turn-prompt.test.ts b/src/shared/agent-hook-listener/providers/jcode-turn-prompt.test.ts deleted file mode 100644 index a1ec6b923b6..00000000000 --- a/src/shared/agent-hook-listener/providers/jcode-turn-prompt.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { JcodeUserPromptEvidence } from '../../jcode-session-files' - -const { readMock } = vi.hoisted(() => ({ - readMock: vi.fn<() => JcodeUserPromptEvidence | null>() -})) -vi.mock('../../jcode-session-files', () => ({ - readLastJcodeUserPromptFromHookPayload: readMock -})) - -import { createHookListenerState, type HookListenerState } from '../listener-state' -import { readJcodeTurnPrompt } from './jcode-turn-prompt' - -const PAYLOAD = { session_id: 'session_jc_9' } - -// jcode blocks on pre_tool; repeated synchronous transcript scans add latency to every tool. -describe('jcode turn prompt cache', () => { - let state: HookListenerState - - beforeEach(() => { - state = createHookListenerState() - readMock.mockReset() - readMock.mockReturnValue({ text: 'ship it', interactionKey: 'k1' }) - }) - - it('re-reads on the next turn, so a new prompt is not served from the old turn', () => { - const first = readJcodeTurnPrompt(state, 'turn_start', 'pane-a', PAYLOAD) - expect(first).toEqual({ text: 'ship it', interactionKey: 'k1' }) - const next = { text: 'review the release', interactionKey: 'k2' } - readMock.mockReturnValue(next) - expect(readJcodeTurnPrompt(state, 'pre_tool', 'pane-a', PAYLOAD)).toEqual(first) - expect(readJcodeTurnPrompt(state, 'turn_start', 'pane-a', PAYLOAD)).toEqual(next) - expect(readJcodeTurnPrompt(state, 'post_tool', 'pane-a', PAYLOAD)).toEqual(next) - expect(readMock).toHaveBeenCalledTimes(2) - }) - - it('caches per pane, so one pane cannot answer for another', () => { - const paneA = { text: 'ship it', interactionKey: 'k1' } - const paneB = { text: 'review the release', interactionKey: 'k2' } - const payloadB = { session_id: 'session_jc_10' } - readMock.mockReturnValueOnce(paneA).mockReturnValueOnce(paneB) - expect(readJcodeTurnPrompt(state, 'pre_tool', 'pane-a', PAYLOAD)).toEqual(paneA) - expect(readJcodeTurnPrompt(state, 'pre_tool', 'pane-b', payloadB)).toEqual(paneB) - expect(readJcodeTurnPrompt(state, 'post_tool', 'pane-a', PAYLOAD)).toEqual(paneA) - expect(readJcodeTurnPrompt(state, 'post_tool', 'pane-b', payloadB)).toEqual(paneB) - expect(readMock).toHaveBeenCalledTimes(2) - }) -}) diff --git a/src/shared/agent-hook-listener/providers/jcode-events.ts b/src/shared/agent-hook-listener/providers/jcode/jcode-events.ts similarity index 89% rename from src/shared/agent-hook-listener/providers/jcode-events.ts rename to src/shared/agent-hook-listener/providers/jcode/jcode-events.ts index 3a111fa2691..8321bc6d428 100644 --- a/src/shared/agent-hook-listener/providers/jcode-events.ts +++ b/src/shared/agent-hook-listener/providers/jcode/jcode-events.ts @@ -2,11 +2,11 @@ import { normalizeAgentStatusPayload, type AgentStatusState, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import { clearPaneTurnCacheState, type HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { readString } from '../tool-input-preview' +} from '../../../agent-status/contracts/agent-status-types' +import { clearPaneTurnCacheState, type HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { readString } from '../../tool-input-preview' import { isJcodeUserInputTool } from './jcode-tool-fields' // Lifecycle mapping follows docs/reference/jcode-hook-events.md; idle session_start takes the early path. diff --git a/src/shared/agent-hook-listener/providers/jcode-helper-permission-wait.ts b/src/shared/agent-hook-listener/providers/jcode/jcode-helper-permission-wait.ts similarity index 90% rename from src/shared/agent-hook-listener/providers/jcode-helper-permission-wait.ts rename to src/shared/agent-hook-listener/providers/jcode/jcode-helper-permission-wait.ts index 3dbe62cb420..89cb8610105 100644 --- a/src/shared/agent-hook-listener/providers/jcode-helper-permission-wait.ts +++ b/src/shared/agent-hook-listener/providers/jcode/jcode-helper-permission-wait.ts @@ -1,9 +1,9 @@ -import { isCachedJcodeHelperSession } from '../../jcode-helper-sessions' -import type { ParsedHookEnvelope } from '../hook-envelope' -import type { AgentHookEventPayload } from '../listener-event' -import type { HookListenerState } from '../listener-state' -import { readString } from '../tool-input-preview' -import type { AgentHookSource } from '../transport/agent-hook-relay' +import { isCachedJcodeHelperSession } from '../../../jcode-helper-sessions' +import type { ParsedHookEnvelope } from '../../hook-envelope' +import type { AgentHookEventPayload } from '../../listener-event' +import type { HookListenerState } from '../../listener-state' +import { readString } from '../../tool-input-preview' +import type { AgentHookSource } from '../../transport/agent-hook-relay' import { extractJcodeToolFields, isJcodeUserInputTool } from './jcode-tool-fields' type HelperRequest = { toolInput?: string; interactivePrompt?: string } diff --git a/src/shared/agent-hook-listener/providers/jcode-tool-fields.ts b/src/shared/agent-hook-listener/providers/jcode/jcode-tool-fields.ts similarity index 96% rename from src/shared/agent-hook-listener/providers/jcode-tool-fields.ts rename to src/shared/agent-hook-listener/providers/jcode/jcode-tool-fields.ts index 78a72ff4263..63030e70075 100644 --- a/src/shared/agent-hook-listener/providers/jcode-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/jcode/jcode-tool-fields.ts @@ -1,11 +1,11 @@ -import type { ToolSnapshot } from '../listener-event' +import type { ToolSnapshot } from '../../listener-event' import { deriveFallbackToolInputPreview, deriveToolInputPreview, hasOwnField, readString, toolUpdate -} from '../tool-input-preview' +} from '../../tool-input-preview' /** Only request_permission waits on a person (resolved via jcode permissions); other safety decisions stay in-model/tool. Match exactly to avoid widening human waits. */ export function isJcodeUserInputTool(toolName: string | undefined): boolean { diff --git a/src/shared/agent-hook-listener/providers/jcode-turn-prompt.ts b/src/shared/agent-hook-listener/providers/jcode/jcode-turn-prompt.ts similarity index 81% rename from src/shared/agent-hook-listener/providers/jcode-turn-prompt.ts rename to src/shared/agent-hook-listener/providers/jcode/jcode-turn-prompt.ts index 7b6459bdacd..b7fa7e50440 100644 --- a/src/shared/agent-hook-listener/providers/jcode-turn-prompt.ts +++ b/src/shared/agent-hook-listener/providers/jcode/jcode-turn-prompt.ts @@ -1,9 +1,9 @@ import { readLastJcodeUserPromptFromHookPayload, type JcodeUserPromptEvidence -} from '../../jcode-session-files' -import { isNewTurnEvent } from '../provider-event-routing' -import type { HookListenerState } from '../listener-state' +} from '../../../jcode-session-files' +import { isNewTurnEvent } from '../../provider-event-routing' +import type { HookListenerState } from '../../listener-state' /** Cache one bounded synchronous journal read per turn: jcode blocks on pre_tool. Without a pane key, read directly. */ export function readJcodeTurnPrompt( diff --git a/src/shared/agent-hook-listener/providers/kimi-events.ts b/src/shared/agent-hook-listener/providers/kimi/kimi-events.ts similarity index 90% rename from src/shared/agent-hook-listener/providers/kimi-events.ts rename to src/shared/agent-hook-listener/providers/kimi/kimi-events.ts index 96cc084b19e..667d6a84c72 100644 --- a/src/shared/agent-hook-listener/providers/kimi-events.ts +++ b/src/shared/agent-hook-listener/providers/kimi/kimi-events.ts @@ -1,15 +1,15 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import type { HookListenerState } from '../listener-state' +} from '../../../agent-status/contracts/agent-status-types' +import type { HookListenerState } from '../../listener-state' import { resolvePrompt, resolveToolState, shouldIgnoreCompactContinuationUserPromptSubmit -} from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { readString } from '../tool-input-preview' +} from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { readString } from '../../tool-input-preview' // Auto-allowed AskUserQuestion still blocks a human on PreToolUse. export function isKimiUserInputTool(toolName: string | undefined): boolean { diff --git a/src/shared/agent-hook-listener/providers/kiro-events.ts b/src/shared/agent-hook-listener/providers/kiro/kiro-events.ts similarity index 84% rename from src/shared/agent-hook-listener/providers/kiro-events.ts rename to src/shared/agent-hook-listener/providers/kiro/kiro-events.ts index 942150f7062..b3bcab5a35e 100644 --- a/src/shared/agent-hook-listener/providers/kiro-events.ts +++ b/src/shared/agent-hook-listener/providers/kiro/kiro-events.ts @@ -1,10 +1,10 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import type { HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' +} from '../../../agent-status/contracts/agent-status-types' +import type { HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' /** Agent-config Kiro hooks use Claude-shaped fields without permission/notification events; approval pauses remain working. Subagents have their own config/hooks, not the lead's. */ export function normalizeKiroEvent( diff --git a/src/shared/agent-hook-listener/providers/kiro-tool-fields.ts b/src/shared/agent-hook-listener/providers/kiro/kiro-tool-fields.ts similarity index 84% rename from src/shared/agent-hook-listener/providers/kiro-tool-fields.ts rename to src/shared/agent-hook-listener/providers/kiro/kiro-tool-fields.ts index 7d5fe87711e..514a49312df 100644 --- a/src/shared/agent-hook-listener/providers/kiro-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/kiro/kiro-tool-fields.ts @@ -1,6 +1,6 @@ -import type { ToolSnapshot } from '../listener-event' -import { readString } from '../tool-input-preview' -import { extractClaudeToolFields } from './claude-tool-fields' +import type { ToolSnapshot } from '../../listener-event' +import { readString } from '../../tool-input-preview' +import { extractClaudeToolFields } from '../claude/claude-tool-fields' /** Kiro V2 camelCase hook names mapped onto Claude's; V3 already sends Claude's names. */ export const KIRO_CLAUDE_EVENT_NAMES: Readonly> = Object.freeze({ diff --git a/src/shared/agent-hook-listener/providers/muse-events.test.ts b/src/shared/agent-hook-listener/providers/muse-events.test.ts deleted file mode 100644 index c0496d2aef0..00000000000 --- a/src/shared/agent-hook-listener/providers/muse-events.test.ts +++ /dev/null @@ -1,229 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { appendFileSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { createHookListenerState, type HookListenerState } from '../listener-state' -import { normalizeAndAccept, PANE_KEY } from '../normalization/agent-hook-listener-test-harness' - -const SESSION_ID = '01a0caa3-0e77-7d41-bad7-46283a45633d' -const TURN_ID = 'e495d1a5-59aa-47b4-8efb-a1bd75509afc' -const CHILD_ID = '1471f5e6-bd60-41a2-bfcf-c9086dbd4092' -const PROMPT_ID = '01a0caa3-a25a-7810-8229-4de04b2e7ca3' -const QUESTION = { - id: 'fav_color', - header: 'Color', - question: 'What is your favorite color?', - options: [{ label: 'Blue' }, { label: 'Green' }, { label: 'Red' }] -} - -const envelope = { - cwd: '/tmp/ws', - transcript_path: null, - model: 'muse-spark-1.3', - permission_mode: 'default', - model_provider: 'meta' -} -const main = { ...envelope, session_id: SESSION_ID, turn_id: TURN_ID } -const child = { ...envelope, session_id: CHILD_ID, turn_id: CHILD_ID } -const reminderInput = { decision: 'none', skill_id: 'bundled:grill' } - -function sessionLogLine(event: Record): string { - return `${JSON.stringify({ - schema_version: 1, - stream: { kind: 'session', id: SESSION_ID }, - record_type: 'event', - payload_type: 'runtime.session', - payload: { kind: 'run', run_id: TURN_ID, event } - })}\n` -} - -function childReminderHooks(): Record[] { - return [ - { - ...child, - hook_event_name: 'PreToolUse', - tool_name: 'submit_reminder_decision', - tool_input: reminderInput, - tool_use_id: 'call_child' - }, - { - ...child, - hook_event_name: 'PermissionRequest', - tool_name: 'submit_reminder_decision', - tool_input: reminderInput - }, - { - ...child, - hook_event_name: 'PostToolUseFailure', - tool_name: 'submit_reminder_decision', - tool_input: reminderInput, - error: - 'tool failed: invalid reminder decision payload: unexpected field `additionalProperties`' - }, - { ...child, hook_event_name: 'SubagentStop', subagent_id: 'skill-reminder' } - ] -} - -describe('Muse hook events', () => { - let state: HookListenerState - let dataHome: string - - beforeEach(() => { - state = createHookListenerState() - dataHome = mkdtempSync(join(tmpdir(), 'muse-events-')) - vi.stubEnv('XDG_DATA_HOME', dataHome) - }) - - afterEach(() => { - vi.unstubAllEnvs() - rmSync(dataHome, { recursive: true, force: true }) - }) - - function sessionLogPath(): string { - const date = new Date(Number.parseInt(SESSION_ID.replace(/-/g, '').slice(0, 12), 16)) - const dir = join( - dataHome, - 'muse', - 'sessions', - String(date.getFullYear()), - String(date.getMonth() + 1).padStart(2, '0'), - String(date.getDate()).padStart(2, '0'), - SESSION_ID - ) - mkdirSync(dir, { recursive: true }) - return join(dir, 'session.jsonl') - } - - it('drops reminder-subagent hooks announced by SubagentStart, even after Stop', () => { - normalizeAndAccept(state, 'muse', { - ...main, - hook_event_name: 'UserPromptSubmit', - prompt: 'list files' - }) - const start = normalizeAndAccept(state, 'muse', { - ...child, - hook_event_name: 'SubagentStart', - subagent_id: 'skill-reminder', - child_session_id: CHILD_ID - }) - expect(start).toBeNull() - for (const hook of childReminderHooks()) { - expect(normalizeAndAccept(state, 'muse', hook)).toBeNull() - } - - const stopped = normalizeAndAccept(state, 'muse', { - ...main, - hook_event_name: 'Stop', - stop_hook_active: false, - last_assistant_message: 'Here are the files.' - }) - expect(stopped?.payload.state).toBe('done') - for (const hook of childReminderHooks()) { - expect(normalizeAndAccept(state, 'muse', hook)).toBeNull() - } - expect(state.lastStatusByPaneKey.get(PANE_KEY)?.payload).toMatchObject({ - state: 'done', - lastAssistantMessage: 'Here are the files.' - }) - }) - - // Restart/reconnect can lose the child's SubagentStart. - it('drops child-session hooks whose turn id equals their session id without SubagentStart', () => { - normalizeAndAccept(state, 'muse', { - ...main, - hook_event_name: 'Stop', - stop_hook_active: false, - last_assistant_message: 'done' - }) - for (const hook of childReminderHooks()) { - expect(normalizeAndAccept(state, 'muse', hook)).toBeNull() - } - expect(state.lastStatusByPaneKey.get(PANE_KEY)?.payload.state).toBe('done') - }) - - // Muse 1.4.14 subagent_spawn trace: the child starts before the lead's Stop and ends long after it. - const SPAWNED_ID = '01a1afae-e716-7a93-8ecf-67cb69c7e7aa' - const spawned = { ...envelope, session_id: CHILD_ID, turn_id: CHILD_ID } - const leadStop = { - ...main, - hook_event_name: 'Stop', - stop_hook_active: false, - last_assistant_message: 'SPAWNED' - } - - function holdLeadStopForSpawnedChild(): void { - normalizeAndAccept(state, 'muse', { - ...main, - hook_event_name: 'UserPromptSubmit', - prompt: 'go' - }) - normalizeAndAccept(state, 'muse', { - ...spawned, - hook_event_name: 'SubagentStart', - subagent_id: SPAWNED_ID, - child_session_id: CHILD_ID - }) - expect(normalizeAndAccept(state, 'muse', leadStop)?.payload).toMatchObject({ - state: 'working', - mainAgent: { state: 'done' } - }) - } - - it('holds the finished turn working until the spawned subagent stops', () => { - holdLeadStopForSpawnedChild() - expect( - normalizeAndAccept(state, 'muse', { - ...spawned, - hook_event_name: 'SubagentStop', - subagent_id: SPAWNED_ID, - child_session_id: CHILD_ID - }) - ).toBeNull() - // The transcript poll replays the held Stop. - expect(normalizeAndAccept(state, 'muse', leadStop)?.payload.state).toBe('done') - }) - - it('releases a held turn when the user interrupts Muse', () => { - holdLeadStopForSpawnedChild() - expect(normalizeAndAccept(state, 'muse', { ...main, hook_event_name: 'Interrupt' })).toBeNull() - expect(normalizeAndAccept(state, 'muse', leadStop)?.payload.state).toBe('done') - }) - - it('reports a request_user_input question from the session log until it settles', () => { - const logPath = sessionLogPath() - const body = { ...main, hook_event_name: 'UserPromptSubmit', prompt: 'ask my favorite color' } - const working = normalizeAndAccept(state, 'muse', body) - expect(working?.payload.state).toBe('working') - - writeFileSync( - logPath, - sessionLogLine({ - kind: 'user_input_prompt_requested', - prompt_id: PROMPT_ID, - tool_name: 'request_user_input', - questions: [QUESTION] - }) - ) - const waiting = normalizeAndAccept(state, 'muse', body) - expect(waiting?.payload).toMatchObject({ - agentType: 'muse', - state: 'waiting', - toolName: 'request_user_input', - prompt: 'ask my favorite color', - interactivePrompt: JSON.stringify({ questions: [QUESTION] }) - }) - - appendFileSync( - logPath, - sessionLogLine({ - kind: 'user_input_prompt_settled', - prompt_id: PROMPT_ID, - outcome: 'answered', - answers: [{ id: 'fav_color', selected_label: 'Blue' }] - }) - ) - const answered = normalizeAndAccept(state, 'muse', body) - expect(answered?.payload.state).toBe('working') - expect(answered?.payload.interactivePrompt).toBeUndefined() - }) -}) diff --git a/src/shared/agent-hook-listener/providers/muse/muse-events.test.ts b/src/shared/agent-hook-listener/providers/muse/muse-events.test.ts new file mode 100644 index 00000000000..b204ef7b561 --- /dev/null +++ b/src/shared/agent-hook-listener/providers/muse/muse-events.test.ts @@ -0,0 +1,85 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { createHookListenerState, type HookListenerState } from '../../listener-state' +import { normalizeAndAccept } from '../../normalization/agent-hook-listener-test-harness' + +const SESSION_ID = '01a0caa3-0e77-7d41-bad7-46283a45633d' +const TURN_ID = 'e495d1a5-59aa-47b4-8efb-a1bd75509afc' +const CHILD_ID = '1471f5e6-bd60-41a2-bfcf-c9086dbd4092' + +const envelope = { + cwd: '/tmp/ws', + transcript_path: null, + model: 'muse-spark-1.3', + permission_mode: 'default', + model_provider: 'meta' +} +const main = { ...envelope, session_id: SESSION_ID, turn_id: TURN_ID } + +describe('Muse hook events', () => { + let state: HookListenerState + let dataHome: string + + beforeEach(() => { + state = createHookListenerState() + dataHome = mkdtempSync(join(tmpdir(), 'muse-events-')) + vi.stubEnv('XDG_DATA_HOME', dataHome) + }) + + afterEach(() => { + vi.unstubAllEnvs() + rmSync(dataHome, { recursive: true, force: true }) + }) + + // Restart/reconnect can lose the child's SubagentStart. + + // Muse 1.4.14 subagent_spawn trace: the child starts before the lead's Stop and ends long after it. + const SPAWNED_ID = '01a1afae-e716-7a93-8ecf-67cb69c7e7aa' + const spawned = { ...envelope, session_id: CHILD_ID, turn_id: CHILD_ID } + const leadStop = { + ...main, + hook_event_name: 'Stop', + stop_hook_active: false, + last_assistant_message: 'SPAWNED' + } + + function holdLeadStopForSpawnedChild(): void { + normalizeAndAccept(state, 'muse', { + ...main, + hook_event_name: 'UserPromptSubmit', + prompt: 'go' + }) + normalizeAndAccept(state, 'muse', { + ...spawned, + hook_event_name: 'SubagentStart', + subagent_id: SPAWNED_ID, + child_session_id: CHILD_ID + }) + expect(normalizeAndAccept(state, 'muse', leadStop)?.payload).toMatchObject({ + state: 'working', + mainAgent: { state: 'done' } + }) + } + + it('holds the finished turn working until the spawned subagent stops', () => { + holdLeadStopForSpawnedChild() + expect( + normalizeAndAccept(state, 'muse', { + ...spawned, + hook_event_name: 'SubagentStop', + subagent_id: SPAWNED_ID, + child_session_id: CHILD_ID + }) + ).toBeNull() + // The transcript poll replays the held Stop. + expect(normalizeAndAccept(state, 'muse', leadStop)?.payload.state).toBe('done') + }) + + it('releases a held turn when the user interrupts Muse', () => { + holdLeadStopForSpawnedChild() + expect(normalizeAndAccept(state, 'muse', { ...main, hook_event_name: 'Interrupt' })).toBeNull() + expect(normalizeAndAccept(state, 'muse', leadStop)?.payload.state).toBe('done') + }) +}) diff --git a/src/shared/agent-hook-listener/providers/muse-events.ts b/src/shared/agent-hook-listener/providers/muse/muse-events.ts similarity index 92% rename from src/shared/agent-hook-listener/providers/muse-events.ts rename to src/shared/agent-hook-listener/providers/muse/muse-events.ts index 58f1741d728..a63dcbb000c 100644 --- a/src/shared/agent-hook-listener/providers/muse-events.ts +++ b/src/shared/agent-hook-listener/providers/muse/muse-events.ts @@ -1,25 +1,25 @@ -import { isAskUserQuestionTool } from '../../agent-status/lifecycle/agent-question-answered-intent' +import { isAskUserQuestionTool } from '../../../agent-status/lifecycle/agent-question-answered-intent' import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' +} from '../../../agent-status/contracts/agent-status-types' import { agentChildWorkLiveness, type AgentChildWorkLiveness -} from '../../agent-status/child-work/agent-status-child-work-liveness' +} from '../../../agent-status/child-work/agent-status-child-work-liveness' import { continueMainAgentStatus, foldAgentLeadStatus -} from '../../agent-status/lifecycle/agent-lead-status-fold' -import { createMuseSessionLogState, readMusePendingUserInput } from '../../muse-session-log' -import type { HookListenerState, MusePaneState } from '../listener-state' +} from '../../../agent-status/lifecycle/agent-lead-status-fold' +import { createMuseSessionLogState, readMusePendingUserInput } from '../../../muse-session-log' +import type { HookListenerState, MusePaneState } from '../../listener-state' import { resolvePrompt, resolveToolState, shouldIgnoreCompactContinuationUserPromptSubmit -} from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { readString } from '../tool-input-preview' +} from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { readString } from '../../tool-input-preview' // Reminders use role names (skill-reminder); spawned children use UUIDs. const BACKGROUND_SUBAGENT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i diff --git a/src/shared/agent-hook-listener/providers/opencode-family-events.ts b/src/shared/agent-hook-listener/providers/opencode-family/opencode-family-events.ts similarity index 85% rename from src/shared/agent-hook-listener/providers/opencode-family-events.ts rename to src/shared/agent-hook-listener/providers/opencode-family/opencode-family-events.ts index f5b4df3fef5..8f98e2e6ea3 100644 --- a/src/shared/agent-hook-listener/providers/opencode-family-events.ts +++ b/src/shared/agent-hook-listener/providers/opencode-family/opencode-family-events.ts @@ -1,12 +1,12 @@ -import { continueMainAgentStatus } from '../../agent-status/lifecycle/agent-lead-status-fold' +import { continueMainAgentStatus } from '../../../agent-status/lifecycle/agent-lead-status-fold' import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import type { AgentMainAgentStatus } from '../../agent-status/lifecycle/main-agent-status' -import type { HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' +} from '../../../agent-status/contracts/agent-status-types' +import type { AgentMainAgentStatus } from '../../../agent-status/lifecycle/main-agent-status' +import type { HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' export function normalizeOpenCodeFamilyEvent( source: 'opencode' | 'opencode2' | 'mimo-code', diff --git a/src/shared/agent-hook-listener/providers/opencode-family-tool-fields.ts b/src/shared/agent-hook-listener/providers/opencode-family/opencode-family-tool-fields.ts similarity index 92% rename from src/shared/agent-hook-listener/providers/opencode-family-tool-fields.ts rename to src/shared/agent-hook-listener/providers/opencode-family/opencode-family-tool-fields.ts index 1afec20f4ff..a7fec2f2b47 100644 --- a/src/shared/agent-hook-listener/providers/opencode-family-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/opencode-family/opencode-family-tool-fields.ts @@ -1,11 +1,11 @@ -import { capOpenCodeHookText } from '../listener-limits' -import type { ToolSnapshot } from '../listener-event' -import { hasAnyOwnField, hasOwnField, readString, toolUpdate } from '../tool-input-preview' +import { capOpenCodeHookText } from '../../listener-limits' +import type { ToolSnapshot } from '../../listener-event' +import { hasAnyOwnField, hasOwnField, readString, toolUpdate } from '../../tool-input-preview' import { deriveInteractivePrompt, readFirstString, stripHookEnvelopeKeys -} from '../interactive-tool' +} from '../../interactive-tool' /** Resumed sessions emit no SessionStart; explicitly retire permission fields or answered cards persist on later frames. */ const OPENCODE_TOOL_FIELDS_RETIRED: ToolSnapshot = { diff --git a/src/shared/agent-hook-listener/providers/pi-family-events.ts b/src/shared/agent-hook-listener/providers/pi-family/pi-family-events.ts similarity index 93% rename from src/shared/agent-hook-listener/providers/pi-family-events.ts rename to src/shared/agent-hook-listener/providers/pi-family/pi-family-events.ts index 850eb031221..faa8d3567c9 100644 --- a/src/shared/agent-hook-listener/providers/pi-family-events.ts +++ b/src/shared/agent-hook-listener/providers/pi-family/pi-family-events.ts @@ -1,13 +1,13 @@ import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import { isAskUserQuestionTool } from '../../agent-status/lifecycle/agent-question-answered-intent' -import { clearPaneTurnCacheState, type HookListenerState } from '../listener-state' -import { resolvePrompt, resolveToolState } from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { readString } from '../tool-input-preview' -import { parseOmpPlanReviewPrompt } from '../../omp-plan-review-prompt' +} from '../../../agent-status/contracts/agent-status-types' +import { isAskUserQuestionTool } from '../../../agent-status/lifecycle/agent-question-answered-intent' +import { clearPaneTurnCacheState, type HookListenerState } from '../../listener-state' +import { resolvePrompt, resolveToolState } from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { readString } from '../../tool-input-preview' +import { parseOmpPlanReviewPrompt } from '../../../omp-plan-review-prompt' /** Normalize Pi/OMP/Prime lifecycle, question waits and live roster/model evidence; ignore events with no status. */ export function normalizePiCompatibleEvent( diff --git a/src/shared/agent-hook-listener/providers/pi-family-tool-fields.ts b/src/shared/agent-hook-listener/providers/pi-family/pi-family-tool-fields.ts similarity index 90% rename from src/shared/agent-hook-listener/providers/pi-family-tool-fields.ts rename to src/shared/agent-hook-listener/providers/pi-family/pi-family-tool-fields.ts index 38821a3f8f3..649c37fcaa5 100644 --- a/src/shared/agent-hook-listener/providers/pi-family-tool-fields.ts +++ b/src/shared/agent-hook-listener/providers/pi-family/pi-family-tool-fields.ts @@ -1,8 +1,13 @@ -import type { ToolSnapshot } from '../listener-event' -import { isAskUserQuestionTool } from '../../agent-status/lifecycle/agent-question-answered-intent' -import { deriveToolInputPreview, hasOwnField, readString, toolUpdate } from '../tool-input-preview' -import { clearActiveToolFieldsUpdate, deriveInteractivePrompt } from '../interactive-tool' -import { serializeOmpPlanReviewPrompt } from '../../omp-plan-review-prompt' +import type { ToolSnapshot } from '../../listener-event' +import { isAskUserQuestionTool } from '../../../agent-status/lifecycle/agent-question-answered-intent' +import { + deriveToolInputPreview, + hasOwnField, + readString, + toolUpdate +} from '../../tool-input-preview' +import { clearActiveToolFieldsUpdate, deriveInteractivePrompt } from '../../interactive-tool' +import { serializeOmpPlanReviewPrompt } from '../../../omp-plan-review-prompt' /** OMP's `ask` carries the same questions/options payload as Pi's question tool. */ function serializeQuestionPrompt(toolInput: unknown): string | undefined { diff --git a/src/shared/agent-hook-listener/providers/qoder-events.ts b/src/shared/agent-hook-listener/providers/qoder-events.ts deleted file mode 100644 index c83923d58f7..00000000000 --- a/src/shared/agent-hook-listener/providers/qoder-events.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { normalizeCompatibleLifecycleEvent } from './compatible-lifecycle-events' -import type { HookListenerState } from '../listener-state' - -export function normalizeQoderEvent( - state: HookListenerState, - eventName: unknown, - promptText: string, - paneKey: string, - hookPayload: Record -) { - return normalizeCompatibleLifecycleEvent( - 'qoder', - state, - eventName, - promptText, - paneKey, - hookPayload - ) -} diff --git a/src/shared/agent-hook-listener/providers/zcode-events.test.ts b/src/shared/agent-hook-listener/providers/zcode/zcode-events.test.ts similarity index 94% rename from src/shared/agent-hook-listener/providers/zcode-events.test.ts rename to src/shared/agent-hook-listener/providers/zcode/zcode-events.test.ts index 43feebb3bc7..cc04c44b1fa 100644 --- a/src/shared/agent-hook-listener/providers/zcode-events.test.ts +++ b/src/shared/agent-hook-listener/providers/zcode/zcode-events.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it } from 'vitest' -import { createHookListenerState, type HookListenerState } from '../listener-state' -import { normalizeAndAccept } from '../normalization/agent-hook-listener-test-harness' +import { createHookListenerState, type HookListenerState } from '../../listener-state' +import { normalizeAndAccept } from '../../normalization/agent-hook-listener-test-harness' const SESSION_ID = 'sess_01a0caa30e777d41bad746283a45633d' const TURN_ID = 'turn_e495d1a559aa478efba1bd75509afc' diff --git a/src/shared/agent-hook-listener/providers/zcode-events.ts b/src/shared/agent-hook-listener/providers/zcode/zcode-events.ts similarity index 87% rename from src/shared/agent-hook-listener/providers/zcode-events.ts rename to src/shared/agent-hook-listener/providers/zcode/zcode-events.ts index 72bdf3239dc..bc52b65909b 100644 --- a/src/shared/agent-hook-listener/providers/zcode-events.ts +++ b/src/shared/agent-hook-listener/providers/zcode/zcode-events.ts @@ -1,16 +1,16 @@ -import { isAskUserQuestionTool } from '../../agent-status/lifecycle/agent-question-answered-intent' +import { isAskUserQuestionTool } from '../../../agent-status/lifecycle/agent-question-answered-intent' import { normalizeAgentStatusPayload, type ParsedAgentStatusPayload -} from '../../agent-status/contracts/agent-status-types' -import type { HookListenerState } from '../listener-state' +} from '../../../agent-status/contracts/agent-status-types' +import type { HookListenerState } from '../../listener-state' import { resolvePrompt, resolveToolState, shouldIgnoreCompactContinuationUserPromptSubmit -} from '../prompt-fields' -import { extractToolFields, isNewTurnEvent } from '../provider-event-routing' -import { readString } from '../tool-input-preview' +} from '../../prompt-fields' +import { extractToolFields, isNewTurnEvent } from '../../provider-event-routing' +import { readString } from '../../tool-input-preview' // ZCode configured-runner-input emits Claude-compatible aliases beside native camelCase; reuse Claude tool fields with ZCode identity. const ZCODE_IDLE_SESSION_START_SOURCES: ReadonlySet = new Set([ diff --git a/src/shared/agent-hook-listener/transcript-poll-policy.ts b/src/shared/agent-hook-listener/transcript-poll-policy.ts index c18f2aad283..7cec0e32f7a 100644 --- a/src/shared/agent-hook-listener/transcript-poll-policy.ts +++ b/src/shared/agent-hook-listener/transcript-poll-policy.ts @@ -1,8 +1,8 @@ import type { AgentHookSource } from './transport/agent-hook-relay' import type { AgentHookEventPayload } from './listener-event' import type { HookListenerState } from './listener-state' -import { hasCodexTranscriptSubagents } from './providers/codex-state' -import { hasMuseSessionLog } from './providers/muse-events' +import { hasCodexTranscriptSubagents } from './providers/codex/codex-state' +import { hasMuseSessionLog } from './providers/muse/muse-events' import { hasPendingCodexSubagentIdentity } from '../codex-subagent-roster' import { parseHookEnvelope } from './hook-envelope' diff --git a/src/wsl-guest/agent-hooks/results/agent-hook-result-retry-scheduler.ts b/src/wsl-guest/agent-hooks/results/agent-hook-result-retry-scheduler.ts index fd52f7bdf72..0f1b5743d2b 100644 --- a/src/wsl-guest/agent-hooks/results/agent-hook-result-retry-scheduler.ts +++ b/src/wsl-guest/agent-hooks/results/agent-hook-result-retry-scheduler.ts @@ -1,5 +1,5 @@ -import { ClaudeHelperKillPoller } from '../../../shared/agent-hook-listener/providers/claude-helper-kill-poller' -import { pollCodexTranscriptStatus } from '../../../shared/agent-hook-listener/providers/codex-transcript-poll' +import { ClaudeHelperKillPoller } from '../../../shared/agent-hook-listener/providers/claude/transcripts/claude-helper-kill-poller' +import { pollCodexTranscriptStatus } from '../../../shared/agent-hook-listener/providers/codex/codex-transcript-poll' // Retry late transcript results against the same cached event; one owner cancels both timer families before cache teardown. import { hasPendingAgentResultText, @@ -19,7 +19,7 @@ import { transcriptPollUpdate } from '../../../shared/agent-hook-listener/transcript-poll-policy' import { AgentTranscriptPollScheduler } from '../../../shared/agent-hook-listener/transcripts/agent-transcript-poll-scheduler' -import { ClaudeOwedNotificationExpiryTimers } from '../../../shared/agent-hook-listener/providers/claude-owed-notification-expiry-timers' +import { ClaudeOwedNotificationExpiryTimers } from '../../../shared/agent-hook-listener/providers/claude/background-tasks/claude-owed-notification-expiry-timers' const ASSISTANT_MESSAGE_RETRY_ATTEMPTS = 5 const ASSISTANT_MESSAGE_RETRY_MS = 50 diff --git a/src/wsl-guest/agent-hooks/results/agent-hook-server-claude-owed-notification-expiry.test.ts b/src/wsl-guest/agent-hooks/results/agent-hook-server-claude-owed-notification-expiry.test.ts deleted file mode 100644 index da65dec02e0..00000000000 --- a/src/wsl-guest/agent-hooks/results/agent-hook-server-claude-owed-notification-expiry.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -// The relay owns remote Claude records and must restate status when an owed task notification never arrives. -import { mkdtempSync, rmSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { AgentHookRelayEnvelope } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' -import { CLAUDE_OWED_TASK_NOTIFICATION_LEASE_MS } from '../../../shared/agent-hook-listener/providers/claude-owed-task-notifications' -import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id' -import { RelayAgentHookServer } from '../server/agent-hook-server' - -const PANE_KEY = makePaneKey('tab-1', '11111111-1111-4111-8111-111111111111') -const SHELL = { id: 'b1', type: 'shell', status: 'running' } - -describe('Claude owed task notification expiry on the relay', () => { - let dir: string - beforeEach(() => { - dir = mkdtempSync(join(tmpdir(), 'relay-hook-expiry-')) - // Why shouldAdvanceTime: the hooks are real loopback POSTs, which need the clock to move. - vi.useFakeTimers({ - shouldAdvanceTime: true, - toFake: ['setTimeout', 'clearTimeout', 'Date', 'performance'] - }) - }) - afterEach(() => { - vi.useRealTimers() - rmSync(dir, { recursive: true, force: true }) - }) - - it.each(['Stop', 'Escape'])( - 'forwards the settled row after %s when a launched shell vanished without a notification', - async (ending) => { - const forward = vi.fn<(envelope: AgentHookRelayEnvelope) => void>() - const server = new RelayAgentHookServer({ endpointDir: dir, forward }) - await server.start() - const { port, token } = server.getCoordinates() - const post = (payload: Record) => - fetch(`http://127.0.0.1:${port}/hook/claude`, { - method: 'POST', - headers: { 'Content-Type': 'application/json', 'X-Orca-Agent-Hook-Token': token }, - body: JSON.stringify({ - paneKey: PANE_KEY, - tabId: 'tab-1', - worktreeId: 'wt-1', - payload: { session_id: 'session-1', ...payload } - }) - }) - try { - await post({ hook_event_name: 'UserPromptSubmit', prompt: 'start the dev server' }) - await post({ - hook_event_name: 'PostToolUse', - tool_name: 'Bash', - tool_response: { backgroundTaskId: SHELL.id } - }) - await post({ hook_event_name: 'Stop', background_tasks: [SHELL] }) - if (ending === 'Escape') { - await post({ hook_event_name: 'Stop', background_tasks: [] }) - } - await post({ hook_event_name: 'UserPromptSubmit', prompt: 'thanks' }) - if (ending === 'Stop') { - await post({ hook_event_name: 'Stop', background_tasks: [] }) - } else { - server.claudeTerminalInterrupts.observe(PANE_KEY, { kind: 'title', title: '◐ Thanks' }) - server.claudeTerminalInterrupts.observe(PANE_KEY, { kind: 'input', data: '\x1b[27u' }) - server.claudeTerminalInterrupts.observe(PANE_KEY, { kind: 'title', title: '✳ Thanks' }) - expect(forward.mock.lastCall?.[0].payload).toMatchObject({ - mainAgent: { state: 'done', outcome: 'cancellation' }, - claudeTaskWakeupPending: 'notification' - }) - } - expect(forward.mock.lastCall?.[0].payload.state).toBe('working') - - vi.advanceTimersByTime(CLAUDE_OWED_TASK_NOTIFICATION_LEASE_MS) - - expect(forward.mock.lastCall?.[0]).toMatchObject({ - paneKey: PANE_KEY, - claudeRunningNonAgentTask: false, - payload: { state: 'done' } - }) - } finally { - server.stop() - } - } - ) -}) diff --git a/src/wsl-guest/agent-hooks/status/agent-hook-event-admission.ts b/src/wsl-guest/agent-hooks/status/agent-hook-event-admission.ts index 984efd78cbd..8db4f3f9b2a 100644 --- a/src/wsl-guest/agent-hooks/status/agent-hook-event-admission.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-event-admission.ts @@ -16,7 +16,7 @@ import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/ import type { AgentHookSource } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' import { retainClaudeSettledRequests } from '../../../shared/agent-hook-listener/claude-settled-permission-requests' import { reduceHostTurnEnd } from '../../../shared/agent-hook-listener/state/agent-hook-turn-end-timing' -import { previousCodexTurnContext } from '../../../shared/agent-hook-listener/providers/codex-session-start' +import { previousCodexTurnContext } from '../../../shared/agent-hook-listener/providers/codex/codex-session-start' import { buildRelayHookEnvelope } from '../publication/agent-hook-envelope-build' import type { RelayHookForward } from '../server/agent-hook-server-contract' import { MAX_CACHED_PANES, type CachedPaneEnvelopeMeta } from './agent-hook-cached-pane-status' diff --git a/src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.ts b/src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.ts index 94a1bdb7b89..fe6201aa322 100644 --- a/src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.ts +++ b/src/wsl-guest/agent-hooks/status/agent-hook-interrupt-reconciliation.ts @@ -12,8 +12,8 @@ import { import { markClaudeLeadTurnInterrupted, setClaudeMainAgentTurnState -} from '../../../shared/agent-hook-listener/providers/claude-roster-state' -import { claudeRowHasUnlistedLiveWork } from '../../../shared/agent-hook-listener/providers/claude-pane-hold-evidence' +} from '../../../shared/agent-hook-listener/providers/claude/lifecycle/claude-roster-state' +import { claudeRowHasUnlistedLiveWork } from '../../../shared/agent-hook-listener/providers/claude/lifecycle/claude-pane-hold-evidence' import type { HookListenerState } from '../../../shared/agent-hook-listener/listener-state' import type { AgentHookEventPayload } from '../../../shared/agent-hook-listener/listener-event' import type { AgentHookSource } from '../../../shared/agent-hook-listener/transport/agent-hook-relay' From 8c9aff20a34184b998a7dde1347b5e23fb63eadd Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 03:43:34 -0800 Subject: [PATCH 081/178] test: remove filesystem scale and telemetry schema replays (#41988) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 1 - .../scripts/ci/sharding/ci-shard-timings.json | 1 - ...telemetry-events-feature-education.spec.ts | 38 ------ .../telemetry-feature-wall-events.spec.ts | 27 ---- .../telemetry-orca-cli-feature-tip.spec.ts | 20 --- .../telemetry-payload-contracts.test.ts | 3 - .../workspace-space-entry-traversal.test.ts | 120 ------------------ .../workspace-space-scan-budget.test.ts | 33 ----- 8 files changed, 243 deletions(-) delete mode 100644 src/shared/telemetry-events-feature-education.spec.ts delete mode 100644 src/shared/telemetry-feature-wall-events.spec.ts delete mode 100644 src/shared/telemetry-orca-cli-feature-tip.spec.ts delete mode 100644 src/shared/telemetry-payload-contracts.test.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index a965c78d283..c4db40f9a5b 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -630,7 +630,6 @@ 1 test-timeout-needs-reason:src/renderer/src/lib/source-control/checks/check-run-details-log-normalization-performance.test.ts 1 test-timeout-needs-reason:src/shared/__tests__/process-host/windows-tree-kill-late-taskkill.win32.test.ts 1 test-timeout-needs-reason:src/shared/dsh-real-cli.test.ts -1 test-timeout-needs-reason:src/shared/workspace-disk-space/workspace-space-entry-traversal.test.ts 1 test-timeout-needs-reason:src/shared/wsl-login-shell-command.test.ts 1 test-timeout-needs-reason:tests/e2e/active-view-restart-restore.spec.ts 1 test-timeout-needs-reason:tests/e2e/agent-send-picker-reveal.spec.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index ca81603760a..eef8f4ab3db 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -6688,7 +6688,6 @@ "src/shared/tab-agent-from-signals-pi-identity.test.ts": 103, "src/shared/task-query.test.ts": 55, "src/shared/task-source-context.test.ts": 103, - "src/shared/telemetry-payload-contracts.test.ts": 282, "src/shared/program-status/terminal-agent-status-processor.test.ts": 74, "src/shared/terminal/output/terminal-bell-detector.test.ts": 40, "src/shared/terminal/protocol/queries/terminal-color-scheme-protocol.test.ts": 39, diff --git a/src/shared/telemetry-events-feature-education.spec.ts b/src/shared/telemetry-events-feature-education.spec.ts deleted file mode 100644 index c8def20c9d5..00000000000 --- a/src/shared/telemetry-events-feature-education.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { eventSchemas } from './telemetry-events' - -describe('feature education telemetry event schemas', () => { - it('accepts contextual tour outcome payloads with bounded step counts', () => { - const parsed = eventSchemas.contextual_tour_outcome.safeParse({ - tour_id: 'tasks', - source: 'tasks_open', - outcome: 'completed', - steps_seen: 2, - total_steps: 3 - }) - - expect(parsed.success).toBe(true) - }) - - it('rejects contextual tour outcome payloads with impossible progress', () => { - const parsed = eventSchemas.contextual_tour_outcome.safeParse({ - tour_id: 'tasks', - source: 'tasks_open', - outcome: 'completed', - steps_seen: 4, - total_steps: 3 - }) - - expect(parsed.success).toBe(false) - }) - - it('rejects raw contextual tour sources', () => { - const parsed = eventSchemas.contextual_tour_shown.safeParse({ - tour_id: 'browser', - source: 'http://localhost:3000/private', - was_feature_previously_interacted: false - }) - - expect(parsed.success).toBe(false) - }) -}) diff --git a/src/shared/telemetry-feature-wall-events.spec.ts b/src/shared/telemetry-feature-wall-events.spec.ts deleted file mode 100644 index 9935070b4af..00000000000 --- a/src/shared/telemetry-feature-wall-events.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { eventSchemas } from './telemetry-events' - -describe('feature wall schemas', () => { - it('rejects invalid close depth fields', () => { - expect( - eventSchemas.feature_wall_closed.safeParse({ - dwell_ms: 1200, - source: 'help_menu', - exit_action: 'left', - furthest_step: 'review_raw_url' - }).success - ).toBe(false) - expect( - eventSchemas.feature_wall_closed.safeParse({ - dwell_ms: 1200, - visited_workflow_count: 6 - }).success - ).toBe(false) - expect( - eventSchemas.feature_wall_closed.safeParse({ - dwell_ms: 1200, - repo_path: '/Users/alice/project' - }).success - ).toBe(false) - }) -}) diff --git a/src/shared/telemetry-orca-cli-feature-tip.spec.ts b/src/shared/telemetry-orca-cli-feature-tip.spec.ts deleted file mode 100644 index 51cb5bed010..00000000000 --- a/src/shared/telemetry-orca-cli-feature-tip.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { eventSchemas } from './telemetry-events' - -describe('orca cli feature tip schemas', () => { - it('rejects raw CLI details and unknown result values', () => { - expect( - eventSchemas.orca_cli_feature_tip_setup_result.safeParse({ - source: 'app_open', - result: 'installed', - command_path: '/Users/alice/bin/orca' - }).success - ).toBe(false) - expect( - eventSchemas.orca_cli_feature_tip_setup_result.safeParse({ - source: 'app_open', - result: 'installed_after_retry' - }).success - ).toBe(false) - }) -}) diff --git a/src/shared/telemetry-payload-contracts.test.ts b/src/shared/telemetry-payload-contracts.test.ts deleted file mode 100644 index c83bd727095..00000000000 --- a/src/shared/telemetry-payload-contracts.test.ts +++ /dev/null @@ -1,3 +0,0 @@ -import './telemetry-orca-cli-feature-tip.spec' -import './telemetry-events-feature-education.spec' -import './telemetry-feature-wall-events.spec' diff --git a/src/shared/workspace-disk-space/workspace-space-entry-traversal.test.ts b/src/shared/workspace-disk-space/workspace-space-entry-traversal.test.ts index 2e81ae91584..f2eb7b823f8 100644 --- a/src/shared/workspace-disk-space/workspace-space-entry-traversal.test.ts +++ b/src/shared/workspace-disk-space/workspace-space-entry-traversal.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from 'vitest' import { scanWorkspaceSpaceEntryTree } from './workspace-space-entry-traversal' -import { WorkspaceSpaceScanCapacityError } from './workspace-space-scan-budget' type Entry = { name: string } @@ -85,29 +84,6 @@ describe('scanWorkspaceSpaceEntryTree', () => { expect(result.sizeBytes).toBe(3) }) - it('aggregates a deep chain exactly at the entry cap without recursive unwinding', async () => { - const depth = 256 - const directories = new Map() - let path = '/root' - for (let index = 0; index < depth; index += 1) { - const name = `directory-${index}` - directories.set(path, [{ name }]) - path = `${path}/${name}` - } - directories.set(path, []) - - const result = await makeTraversal( - directories, - async () => ({ kind: 'directory', sizeBytes: 1 }), - { maxEntries: depth } - ) - - expect(result.sizeBytes).toBe(depth + 1) - expect(result.children).toEqual([ - expect.objectContaining({ name: 'directory-0', sizeBytes: depth }) - ]) - }) - it('scans a chain far longer than the cap because listings are released', async () => { const depth = 50 const directories = new Map() @@ -127,100 +103,4 @@ describe('scanWorkspaceSpaceEntryTree', () => { expect(result.sizeBytes).toBe(depth + 1) }) - - // Per-worker entry caps make capacity depend on concurrency (docs/workspace-space-scan-resource-bounds.md). - describe('capacity depends on directory shape, not tree size or concurrency', () => { - function buildWideTree(dirCount: number, filesPerDir: number) { - const directories = new Map() - directories.set( - '/root', - Array.from({ length: dirCount }, (_, index) => ({ name: `dir-${index}` })) - ) - for (let index = 0; index < dirCount; index += 1) { - directories.set( - `/root/dir-${index}`, - Array.from({ length: filesPerDir }, (_, file) => ({ name: `file-${file}` })) - ) - } - return directories - } - - function scanWideTree(dirCount: number, filesPerDir: number, concurrency: number) { - const directories = buildWideTree(dirCount, filesPerDir) - return makeTraversal( - directories, - async (path) => ({ - kind: directories.has(path) ? 'directory' : 'file', - sizeBytes: 1 - }), - { maxEntries: 100_000, maxRetainedBytes: Number.MAX_SAFE_INTEGER }, - concurrency - ) - } - - it('still rejects a single directory above the cap', async () => { - await expect(scanWideTree(1, 100_001, 48)).rejects.toBeInstanceOf( - WorkspaceSpaceScanCapacityError - ) - }, 30_000) - }) - - // Unlike the tests above, these exercise the production byte cap across checkout depths. - describe('capacity at the production default limits', () => { - // The regression appeared for checkout paths longer than about 58 characters. - const DEEP_ROOT = '/Users/octocat/projects/orca/.claude/worktrees/wf_d39acf3c-e7d-2' - - function scanAtDefaults( - rootPath: string, - dirCount: number, - filesPerDir: number, - concurrency: number - ) { - const directories = new Map() - directories.set( - rootPath, - Array.from({ length: dirCount }, (_, index) => ({ name: `dir-${index}` })) - ) - for (let index = 0; index < dirCount; index += 1) { - directories.set( - `${rootPath}/dir-${index}`, - Array.from({ length: filesPerDir }, (_, file) => ({ name: `file-${file}.ts` })) - ) - } - return scanWorkspaceSpaceEntryTree({ - rootPath, - rootName: 'root', - concurrency, - entryName: (entry: Entry) => entry.name, - joinPath: (parent, child) => `${parent}/${child}`, - classifyEntry: async (path) => ({ - kind: directories.has(path) ? ('directory' as const) : ('file' as const), - sizeBytes: 1 - }), - readDirectory: async (path) => { - const entries = directories.get(path) - if (!entries) { - throw new Error(`unreadable ${path}`) - } - return entries - }, - checkCancelled: () => undefined, - createCancellationError: () => new Error('cancelled'), - isCancellationError: (error) => error instanceof Error && error.message === 'cancelled' - }) - } - - it.each([ - { dirCount: 48, filesPerDir: 2_100, concurrency: 48 }, - { dirCount: 10, filesPerDir: 10_001, concurrency: 10 }, - { dirCount: 40, filesPerDir: 2_500, concurrency: 48 } - ])( - 'scans $dirCount x $filesPerDir at concurrency $concurrency under a deep root', - async ({ dirCount, filesPerDir, concurrency }) => { - const result = await scanAtDefaults(DEEP_ROOT, dirCount, filesPerDir, concurrency) - expect(result.sizeBytes).toBe(dirCount * filesPerDir + dirCount + 1) - }, - 30_000 - ) - }) }) diff --git a/src/shared/workspace-disk-space/workspace-space-scan-budget.test.ts b/src/shared/workspace-disk-space/workspace-space-scan-budget.test.ts index 8cb82b8d327..5651c3366bd 100644 --- a/src/shared/workspace-disk-space/workspace-space-scan-budget.test.ts +++ b/src/shared/workspace-disk-space/workspace-space-scan-budget.test.ts @@ -2,9 +2,6 @@ import { describe, expect, it } from 'vitest' import { collectWorkspaceSpaceDirectoryEntries, createWorkspaceSpaceScanBudget, - estimateWorkspaceSpaceEntryRetainedBytes, - estimateWorkspaceSpaceListingRetainedBytes, - releaseWorkspaceSpaceScanEntries, WorkspaceSpaceScanCapacityError } from './workspace-space-scan-budget' @@ -28,36 +25,6 @@ describe('workspace space scan budget', () => { expect(budget).toMatchObject({ retainedBytes: 0 }) }) - it('frees capacity for later directories once a listing is released', async () => { - const parentPath = '/workspace' - const entries = [{ name: 'first' }, { name: 'second' }] - const exactBytes = entries.reduce( - (total, entry) => total + estimateWorkspaceSpaceEntryRetainedBytes(entry.name), - estimateWorkspaceSpaceListingRetainedBytes(parentPath) - ) - const budget = createWorkspaceSpaceScanBudget({ maxRetainedBytes: exactBytes }) - - const first = await collectWorkspaceSpaceDirectoryEntries( - entries, - parentPath, - (entry) => entry.name, - budget, - () => undefined - ) - // Why: a cumulative counter would reject the identical second listing here. - releaseWorkspaceSpaceScanEntries(budget, first.retainedBytes) - - await expect( - collectWorkspaceSpaceDirectoryEntries( - entries, - parentPath, - (entry) => entry.name, - budget, - () => undefined - ) - ).resolves.toMatchObject({ retainedBytes: exactBytes }) - }) - it('closes an async directory iterator when the next entry exceeds the budget', async () => { let closed = false async function* directory() { From 59b88a117662e0c5b9812906f9a73f641a4c7e79 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 13 Nov 2026 03:52:49 -0800 Subject: [PATCH 082/178] refactor: group GitHub task-page modules by domain (#41995) --- config/react-compiler-bailout-baseline.txt | 26 +- .../scripts/ci/sharding/ci-shard-timings.json | 3 - .../check-changed-code-quality.mjs | 2 +- package.json | 2 +- .../gh-edit-close-as-duplicate.ts | 2 +- .../gh-edit-section-horizontal.tsx | 2 +- .../gh-edit-section-mutations.ts | 4 +- .../gh-edit-section-status-popover.tsx | 2 +- .../gh-edit-section-top-columns.tsx | 2 +- ...gh-edit-section.assignee-identity.test.tsx | 304 ------------------ .../edit-item-fields/gh-edit-section.tsx | 2 +- .../land-pull-request/pr-actions-panel.tsx | 2 +- .../github/work-item-state-presentation.tsx | 2 +- .../src/components/task-page/Content.tsx | 2 +- .../src/components/task-page/ListChrome.tsx | 2 +- .../components/task-page/ProviderFilters.tsx | 2 +- .../src/components/task-page/Surface.tsx | 2 +- .../src/components/task-page/TaskPage.tsx | 18 +- .../task-page-github-resume-cache.ts | 2 +- ...e-task-page-github-cache-reconciliation.ts | 8 +- ...se-task-page-work-items-cache-selection.ts | 6 +- .../{ => issue-creation}/IssueDialog.tsx | 4 +- .../{ => issue-creation}/IssueSelectors.tsx | 4 +- .../use-task-page-github-issue-creation.ts | 4 +- .../use-task-page-github-issue-draft.ts | 6 +- .../{ => issue-status}/DuplicatePicker.tsx | 2 +- .../github/{ => issue-status}/StatusCell.tsx | 20 +- .../task-page-github-status-actions.ts | 4 +- .../task-page-github-status-state.ts | 2 +- .../github/{ => listing}/AssigneesCell.tsx | 14 +- .../github/{ => listing}/Avatars.tsx | 4 +- .../github/{ => listing}/Filters.tsx | 4 +- .../task-page/github/{ => listing}/List.tsx | 6 +- .../github/{ => listing}/ModeControls.tsx | 2 +- .../task-page/github/{ => listing}/Rows.tsx | 20 +- .../task-page-github-list-scroll-restore.ts | 0 ...ask-page-github-work-item-status-badge.tsx | 2 +- .../task-page-github-work-item-status.ts | 2 +- .../{ => listing}/task-page-pr-chips.test.ts | 0 .../{ => listing}/task-page-pr-chips.ts | 8 +- .../task-page-preset-highlight.ts | 6 +- .../use-task-page-github-collection-state.ts | 12 +- .../use-task-page-github-list-pending.ts | 2 +- .../use-task-page-github-list-projection.ts | 12 +- .../use-task-page-github-list-state.ts | 8 +- .../use-task-page-github-search-pagination.ts | 12 +- ...ask-page-github-confirmed-client-values.ts | 4 +- ...task-page-github-dialog-state-authority.ts | 8 +- ...page-github-work-item-filter-membership.ts | 6 +- ...e-github-work-item-mutation-composition.ts | 10 +- ...ask-page-github-work-item-mutation-keys.ts | 0 ...age-github-work-item-mutation-lifecycle.ts | 8 +- ...sk-page-github-work-item-mutation-pages.ts | 8 +- ...-page-github-work-item-mutation-patches.ts | 6 +- ...page-github-work-item-mutation-registry.ts | 18 +- ...sk-page-github-work-item-mutation-types.ts | 8 +- .../task-page-github-work-item-mutations.ts | 20 +- ...sk-page-github-work-item-registry-types.ts | 4 +- .../use-task-page-github-mutation-state.ts | 18 +- .../github/pr-check-hydration.test.tsx | 195 ----------- .../github/{ => pull-requests}/ChecksCell.tsx | 6 +- .../github/{ => pull-requests}/MergeCell.tsx | 12 +- .../github/{ => pull-requests}/ReviewCell.tsx | 14 +- .../{ => pull-requests}/ReviewerPicker.tsx | 4 +- .../task-page-github-review-model.tsx | 10 +- .../task-page-github-reviewer-actions.ts | 18 +- .../use-task-page-pr-check-hydration.ts | 14 +- .../task-page-github-landing-refresh-model.ts | 2 +- .../task-page-github-landing-refresh-run.tsx | 22 +- .../task-page-github-quiet-refresh-run.tsx | 22 +- ...page-github-work-item-authority-refresh.ts | 6 +- .../task-page-github-work-item-quiet-adopt.ts | 21 +- ...-page-github-work-item-quiet-revalidate.ts | 10 +- .../task-page-github-work-item-quiet-state.ts | 2 +- .../use-task-page-github-landing-refresh.ts | 2 +- ...e-task-page-github-quiet-refresh-effect.ts | 0 .../use-task-page-github-quiet-refresh.ts | 4 +- ...sk-page-github-list-scroll-restore.test.ts | 112 ------- .../github/use-task-page-github-detail.ts | 6 +- .../jira/use-task-page-jira-creation-state.ts | 2 +- .../task-page-linear-jira-list-model.tsx | 2 +- .../use-task-page-linear-project-creation.ts | 2 +- .../task-page-github-pr-chips.test.tsx | 63 ---- .../task-page/task-page-localized-options.tsx | 2 +- .../task-page/use-task-page-detail-routing.ts | 2 +- .../task-page/use-task-page-search-actions.ts | 4 +- ...-task-page-source-display.test-fixture.tsx | 197 ------------ .../useTaskPageGitHubWorkItemMutation.ts | 10 +- .../sources/github-source-lifetime.test.tsx | 167 ---------- .../github/sources/source-collections.ts | 2 +- .../task-records/github-task-records.test.ts | 186 ----------- .../github/task-records/task-record-owners.ts | 4 +- 92 files changed, 274 insertions(+), 1526 deletions(-) delete mode 100644 src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.assignee-identity.test.tsx rename src/renderer/src/components/task-page/github/{ => cache}/task-page-github-resume-cache.ts (96%) rename src/renderer/src/components/task-page/github/{ => cache}/use-task-page-github-cache-reconciliation.ts (93%) rename src/renderer/src/components/task-page/github/{ => cache}/use-task-page-work-items-cache-selection.ts (92%) rename src/renderer/src/components/task-page/github/{ => issue-creation}/IssueDialog.tsx (98%) rename src/renderer/src/components/task-page/github/{ => issue-creation}/IssueSelectors.tsx (97%) rename src/renderer/src/components/task-page/github/{ => issue-creation}/use-task-page-github-issue-creation.ts (97%) rename src/renderer/src/components/task-page/github/{ => issue-creation}/use-task-page-github-issue-draft.ts (94%) rename src/renderer/src/components/task-page/github/{ => issue-status}/DuplicatePicker.tsx (97%) rename src/renderer/src/components/task-page/github/{ => issue-status}/StatusCell.tsx (93%) rename src/renderer/src/components/task-page/github/{ => issue-status}/task-page-github-status-actions.ts (94%) rename src/renderer/src/components/task-page/github/{ => issue-status}/task-page-github-status-state.ts (91%) rename src/renderer/src/components/task-page/github/{ => listing}/AssigneesCell.tsx (93%) rename src/renderer/src/components/task-page/github/{ => listing}/Avatars.tsx (84%) rename src/renderer/src/components/task-page/github/{ => listing}/Filters.tsx (99%) rename src/renderer/src/components/task-page/github/{ => listing}/List.tsx (98%) rename src/renderer/src/components/task-page/github/{ => listing}/ModeControls.tsx (98%) rename src/renderer/src/components/task-page/github/{ => listing}/Rows.tsx (96%) rename src/renderer/src/components/task-page/github/{ => listing}/task-page-github-list-scroll-restore.ts (100%) rename src/renderer/src/components/task-page/github/{ => listing}/task-page-github-work-item-status-badge.tsx (89%) rename src/renderer/src/components/task-page/github/{ => listing}/task-page-github-work-item-status.ts (96%) rename src/renderer/src/components/task-page/github/{ => listing}/task-page-pr-chips.test.ts (100%) rename src/renderer/src/components/task-page/github/{ => listing}/task-page-pr-chips.ts (91%) rename src/renderer/src/components/task-page/github/{ => listing}/task-page-preset-highlight.ts (71%) rename src/renderer/src/components/task-page/github/{ => listing}/use-task-page-github-collection-state.ts (86%) rename src/renderer/src/components/task-page/github/{ => listing}/use-task-page-github-list-pending.ts (95%) rename src/renderer/src/components/task-page/github/{ => listing}/use-task-page-github-list-projection.ts (90%) rename src/renderer/src/components/task-page/github/{ => listing}/use-task-page-github-list-state.ts (97%) rename src/renderer/src/components/task-page/github/{ => listing}/use-task-page-github-search-pagination.ts (93%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-confirmed-client-values.ts (90%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-dialog-state-authority.ts (88%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-work-item-filter-membership.ts (91%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-work-item-mutation-composition.ts (94%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-work-item-mutation-keys.ts (100%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-work-item-mutation-lifecycle.ts (95%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-work-item-mutation-pages.ts (93%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-work-item-mutation-patches.ts (95%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-work-item-mutation-registry.ts (93%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-work-item-mutation-types.ts (71%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-work-item-mutations.ts (88%) rename src/renderer/src/components/task-page/github/{ => mutations}/task-page-github-work-item-registry-types.ts (78%) rename src/renderer/src/components/task-page/github/{ => mutations}/use-task-page-github-mutation-state.ts (87%) delete mode 100644 src/renderer/src/components/task-page/github/pr-check-hydration.test.tsx rename src/renderer/src/components/task-page/github/{ => pull-requests}/ChecksCell.tsx (89%) rename src/renderer/src/components/task-page/github/{ => pull-requests}/MergeCell.tsx (93%) rename src/renderer/src/components/task-page/github/{ => pull-requests}/ReviewCell.tsx (95%) rename src/renderer/src/components/task-page/github/{ => pull-requests}/ReviewerPicker.tsx (97%) rename src/renderer/src/components/task-page/github/{ => pull-requests}/task-page-github-review-model.tsx (83%) rename src/renderer/src/components/task-page/github/{ => pull-requests}/task-page-github-reviewer-actions.ts (90%) rename src/renderer/src/components/task-page/github/{ => pull-requests}/use-task-page-pr-check-hydration.ts (93%) rename src/renderer/src/components/task-page/github/{ => refresh}/task-page-github-landing-refresh-model.ts (92%) rename src/renderer/src/components/task-page/github/{ => refresh}/task-page-github-landing-refresh-run.tsx (93%) rename src/renderer/src/components/task-page/github/{ => refresh}/task-page-github-quiet-refresh-run.tsx (92%) rename src/renderer/src/components/task-page/github/{ => refresh}/task-page-github-work-item-authority-refresh.ts (94%) rename src/renderer/src/components/task-page/github/{ => refresh}/task-page-github-work-item-quiet-adopt.ts (87%) rename src/renderer/src/components/task-page/github/{ => refresh}/task-page-github-work-item-quiet-revalidate.ts (92%) rename src/renderer/src/components/task-page/github/{ => refresh}/task-page-github-work-item-quiet-state.ts (96%) rename src/renderer/src/components/task-page/github/{ => refresh}/use-task-page-github-landing-refresh.ts (90%) rename src/renderer/src/components/task-page/github/{ => refresh}/use-task-page-github-quiet-refresh-effect.ts (100%) rename src/renderer/src/components/task-page/github/{ => refresh}/use-task-page-github-quiet-refresh.ts (94%) delete mode 100644 src/renderer/src/components/task-page/github/task-page-github-list-scroll-restore.test.ts delete mode 100644 src/renderer/src/components/task-page/task-page-github-pr-chips.test.tsx delete mode 100644 src/renderer/src/components/task-page/use-task-page-source-display.test-fixture.tsx delete mode 100644 src/renderer/src/store/github/sources/github-source-lifetime.test.tsx delete mode 100644 src/renderer/src/store/github/task-records/github-task-records.test.ts diff --git a/config/react-compiler-bailout-baseline.txt b/config/react-compiler-bailout-baseline.txt index 643bcde57d2..a52ba10321a 100644 --- a/config/react-compiler-bailout-baseline.txt +++ b/config/react-compiler-bailout-baseline.txt @@ -577,20 +577,20 @@ 1 src/renderer/src/components/tab-bar/use-tab-bar-create-menu-controller.ts 2 src/renderer/src/components/tab-bar/use-tab-bar-item-actions.ts 1 src/renderer/src/components/tab-group/useTabDragSplit.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 -1 src/renderer/src/components/task-page/github/use-task-page-github-cache-reconciliation.ts +1 src/renderer/src/components/task-page/github/listing/AssigneesCell.tsx +2 src/renderer/src/components/task-page/github/pull-requests/MergeCell.tsx +1 src/renderer/src/components/task-page/github/issue-status/StatusCell.tsx +1 src/renderer/src/components/task-page/github/cache/use-task-page-github-cache-reconciliation.ts 2 src/renderer/src/components/task-page/github/use-task-page-github-detail.ts -2 src/renderer/src/components/task-page/github/use-task-page-github-issue-creation.ts -21 src/renderer/src/components/task-page/github/use-task-page-github-issue-draft.ts -1 src/renderer/src/components/task-page/github/use-task-page-github-landing-refresh.ts -3 src/renderer/src/components/task-page/github/use-task-page-github-list-state.ts -7 src/renderer/src/components/task-page/github/use-task-page-github-mutation-state.ts -1 src/renderer/src/components/task-page/github/use-task-page-github-quiet-refresh-effect.ts -5 src/renderer/src/components/task-page/github/use-task-page-github-quiet-refresh.ts -3 src/renderer/src/components/task-page/github/use-task-page-github-search-pagination.ts -4 src/renderer/src/components/task-page/github/use-task-page-work-items-cache-selection.ts +2 src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-creation.ts +21 src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-draft.ts +1 src/renderer/src/components/task-page/github/refresh/use-task-page-github-landing-refresh.ts +3 src/renderer/src/components/task-page/github/listing/use-task-page-github-list-state.ts +7 src/renderer/src/components/task-page/github/mutations/use-task-page-github-mutation-state.ts +1 src/renderer/src/components/task-page/github/refresh/use-task-page-github-quiet-refresh-effect.ts +5 src/renderer/src/components/task-page/github/refresh/use-task-page-github-quiet-refresh.ts +3 src/renderer/src/components/task-page/github/listing/use-task-page-github-search-pagination.ts +4 src/renderer/src/components/task-page/github/cache/use-task-page-work-items-cache-selection.ts 1 src/renderer/src/components/task-page/gitlab/use-task-page-gitlab-loading.ts 2 src/renderer/src/components/task-page/jira/task-page-jira-board-view.tsx 1 src/renderer/src/components/task-page/jira/use-task-page-jira-creation-projects.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index eef8f4ab3db..081ff049122 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -4530,7 +4530,6 @@ "src/renderer/src/components/feature-wall/setup/FullDiskAccessSetupPrompt.test.ts": 466, "src/renderer/src/components/floating-terminal/floating-workspace-tab-reorder.test.ts": 2520, "src/renderer/src/components/github-checks-tab-state.test.ts": 52, - "src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.assignee-identity.test.tsx": 461, "src/renderer/src/components/github-item-dialog/land-pull-request/pr-reviewers-request-actions.test.ts": 1976, "src/renderer/src/components/github-item-dialog/load-item-details/use-github-item-dialog-details.test.tsx": 2850, "src/renderer/src/components/github-item-dialog/load-item-details/use-github-task-refresh.test.tsx": 1070, @@ -5088,7 +5087,6 @@ "src/renderer/src/components/tab-group/useTabGroupCloseScopeCommands.test.tsx": 2023, "src/renderer/src/components/tab-group/workspace-tab-close-commands.frame-flush.test.ts": 1604, "src/renderer/src/components/task-page/github/task-page-github-landing-refresh-record.test.tsx": 971, - "src/renderer/src/components/task-page/github/task-page-github-list-scroll-restore.test.ts": 197, "src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-regressions.test.ts": 140, "src/renderer/src/components/task-page/github/task-page-github-work-item-mutations.test.ts": 169, "src/renderer/src/components/task-page/github/use-task-page-github-issue-creation.test.tsx": 1769, @@ -5831,7 +5829,6 @@ "src/renderer/src/store/github/repositories/github-repo-lookup-index.test.ts": 51, "src/renderer/src/store/github/review-threads/github-review-thread-rollback.test.ts": 663, "src/renderer/src/store/github/task-records/github-task-record-retries.test.ts": 758, - "src/renderer/src/store/github/task-records/github-task-records.test.ts": 807, "src/renderer/src/store/github/work-items/github-work-items-error-envelope.test.ts": 1746, "src/renderer/src/store/github/work-items/github-work-items-pagination.test.ts": 1806, "src/renderer/src/store/github/work-items/github-work-items-runtime-routing.test.ts": 1891, diff --git a/config/scripts/preflight/changed-checks/check-changed-code-quality.mjs b/config/scripts/preflight/changed-checks/check-changed-code-quality.mjs index 8cd2d6cf97d..00753773695 100644 --- a/config/scripts/preflight/changed-checks/check-changed-code-quality.mjs +++ b/config/scripts/preflight/changed-checks/check-changed-code-quality.mjs @@ -76,7 +76,7 @@ const SUPPRESSED_REACT_DOCTOR_DIAGNOSTICS = new Map([ [ 'react-doctor(no-adjust-state-on-prop-change)', new Set([ - 'src/renderer/src/components/task-page/github/use-task-page-github-issue-draft.ts', + 'src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-draft.ts', 'src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts' ]) ], diff --git a/package.json b/package.json index a15fcdc3edb..cf4dd2f8e49 100644 --- a/package.json +++ b/package.json @@ -440,7 +440,7 @@ }, { "files": [ - "src/renderer/src/components/task-page/github/use-task-page-github-issue-draft.ts", + "src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-draft.ts", "src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts" ], "rules": [ diff --git a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-close-as-duplicate.ts b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-close-as-duplicate.ts index 5a1ff81006b..011be2c853b 100644 --- a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-close-as-duplicate.ts +++ b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-close-as-duplicate.ts @@ -2,7 +2,7 @@ import { getTaskPageGitHubDuplicateTargetErrorMessage, validateTaskPageGitHubDuplicateTarget, type TaskPageGitHubCloseAction -} from '@/components/task-page/github/task-page-github-status-actions' +} from '@/components/task-page/github/issue-status/task-page-github-status-actions' import { translate } from '@/i18n/i18n' export function closeGHEditAsDuplicate({ diff --git a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-horizontal.tsx b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-horizontal.tsx index 644262111fa..a9a543cf62d 100644 --- a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-horizontal.tsx +++ b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-horizontal.tsx @@ -10,7 +10,7 @@ import { } from '@/components/ui/dropdown-menu' import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskPageGitHubCloseAction } from '@/components/task-page/github/task-page-github-status-actions' +import type { TaskPageGitHubCloseAction } from '@/components/task-page/github/issue-status/task-page-github-status-actions' import { useTranslate } from '@/i18n/use-translate' import { GHEditSectionStatusPopover } from './gh-edit-section-status-popover' import { GHEditSectionAssigneesPill } from './gh-edit-section-assignees' diff --git a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-mutations.ts b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-mutations.ts index a1ac4285b77..d2cf514ad78 100644 --- a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-mutations.ts +++ b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-mutations.ts @@ -4,9 +4,9 @@ import type { useImmediateMutation } from '@/hooks/useIssueMetadata' import { buildTaskPageGitHubCloseUpdate, type TaskPageGitHubCloseAction -} from '@/components/task-page/github/task-page-github-status-actions' +} from '@/components/task-page/github/issue-status/task-page-github-status-actions' import { beginEdit, isTrackedEdit } from './gh-edit-task-record' -import { assertTaskPageGitHubDialogStateAuthority } from '@/components/task-page/github/task-page-github-dialog-state-authority' +import { assertTaskPageGitHubDialogStateAuthority } from '@/components/task-page/github/mutations/task-page-github-dialog-state-authority' import { runIssueUpdate } from '@/components/github/github-work-item-edit-mutations' import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../shared/github/repositories/repository-identity' diff --git a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-status-popover.tsx b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-status-popover.tsx index 99e9fbf6c70..570788962a0 100644 --- a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-status-popover.tsx +++ b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-status-popover.tsx @@ -15,7 +15,7 @@ import { Input } from '@/components/ui/input' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { cn } from '@/lib/class-names' import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskPageGitHubCloseAction } from '@/components/task-page/github/task-page-github-status-actions' +import type { TaskPageGitHubCloseAction } from '@/components/task-page/github/issue-status/task-page-github-status-actions' import { getStateLabel } from '@/components/github/work-item-state-presentation' import { useTranslate } from '@/i18n/use-translate' import { getStateTone } from '../load-item-details/work-item-state-badge' diff --git a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-top-columns.tsx b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-top-columns.tsx index eec2c5c370e..dc99bddc5af 100644 --- a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-top-columns.tsx +++ b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section-top-columns.tsx @@ -2,7 +2,7 @@ import React from 'react' import { FolderKanban } from 'lucide-react' import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskPageGitHubCloseAction } from '@/components/task-page/github/task-page-github-status-actions' +import type { TaskPageGitHubCloseAction } from '@/components/task-page/github/issue-status/task-page-github-status-actions' import { useTranslate } from '@/i18n/use-translate' import { GHEditSectionStatusPopover } from './gh-edit-section-status-popover' import { GHEditSectionAssigneesColumn } from './gh-edit-section-assignees' diff --git a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.assignee-identity.test.tsx b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.assignee-identity.test.tsx deleted file mode 100644 index 3dcd00abfeb..00000000000 --- a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.assignee-identity.test.tsx +++ /dev/null @@ -1,304 +0,0 @@ -// @vitest-environment happy-dom - -import { act, useState } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { GitHubItemDialogProjectOrigin } from '../load-item-details/github-item-dialog-types' -import { GHEditSection } from './gh-edit-section' -import { resetTaskPageGitHubMutationRegistryForTests } from '@/components/task-page/github/task-page-github-work-item-mutation-registry' - -const mocks = vi.hoisted(() => ({ - update: vi.fn(), - patchWorkItem: vi.fn(), - patchProjectRowContent: vi.fn(), - recordFeatureInteraction: vi.fn(), - onMutated: vi.fn() -})) - -vi.mock('@/store', () => { - const state = { - patchWorkItem: mocks.patchWorkItem, - patchProjectRowContent: mocks.patchProjectRowContent, - recordFeatureInteraction: mocks.recordFeatureInteraction, - repos: [], - settings: null - } - return { - useAppStore: Object.assign((selector: (value: typeof state) => unknown) => selector(state), { - getState: () => state - }) - } -}) - -vi.mock('@/hooks/useIssueMetadata', async () => { - const { useImmediateMutation } = await import('@/hooks/useImmediateMutation') - return { useImmediateMutation } -}) -vi.mock('@/hooks/useGitHubRepoMetadata', () => ({ - useRepoLabels: () => ({ data: [] }), - useRepoAssignees: () => ({ data: [] }) -})) -vi.mock('@/hooks/useGitHubSlugMetadata', () => ({ - useRepoLabelsBySlug: () => ({ data: [] }), - useRepoAssigneesBySlug: () => ({ data: [] }) -})) -vi.mock('@/components/github/github-duplicate-issue-candidates', () => ({ - useGitHubDuplicateIssueCandidates: () => [] -})) -vi.mock('@/components/github/github-work-item-edit-mutations', () => ({ - runIssueUpdate: mocks.update -})) -vi.mock('@/i18n/i18n', () => ({ translate: (_key: string, fallback: string) => fallback })) -vi.mock('./gh-edit-section-horizontal', () => ({ GHEditSectionHorizontal: () => null })) -vi.mock('./gh-edit-section-top-columns', () => ({ - GHEditSectionTopColumns: ({ - localAssignees, - onAssigneeToggle, - localState, - localLabels, - onStateChange, - onLabelToggle - }: { - localAssignees: string[] - onAssigneeToggle: (login: string) => void - localState: GitHubWorkItem['state'] - localLabels: string[] - onStateChange: (state: 'open' | 'closed') => void - onLabelToggle: (label: string) => void - }) => ( - <> - {localAssignees.join(',')} - {localState} - {localLabels.join(',')} - - - - - - ) -})) - -const item: GitHubWorkItem = { - id: 'issue:5', - repoId: 'registered-repo', - type: 'issue', - number: 5, - title: 'Issue five', - state: 'open', - url: 'https://github.com/upstream/widgets/issues/5', - labels: [], - updatedAt: '', - author: null -} -let root: Root -let container: HTMLDivElement - -async function render( - nextItem = item, - assignees: string[] = [], - projectOrigin?: GitHubItemDialogProjectOrigin -): Promise { - await act(async () => { - root.render( - {}} - onLabelsChange={() => {}} - onMutated={mocks.onMutated} - assignees={assignees} - onUse={() => {}} - layout="top-columns" - /> - ) - }) -} - -async function click(label = 'Assign candidate'): Promise { - const button = [...container.querySelectorAll('button')].find( - (candidate) => candidate.textContent === label - ) - expect(button).toBeDefined() - await act(async () => button?.click()) -} - -function selected(): string | null { - return container.querySelector('[data-field="assignees"]')?.textContent ?? null -} - -let updateParentSelection: (nextItem: GitHubWorkItem) => void = () => {} - -function ParentSelection({ item }: { item: GitHubWorkItem }): React.JSX.Element { - const [localState, setLocalState] = useState(item.state) - const [localLabels, setLocalLabels] = useState(item.labels) - updateParentSelection = (nextItem) => { - setLocalState(nextItem.state) - setLocalLabels(nextItem.labels) - } - return ( - {}} - layout="top-columns" - /> - ) -} - -async function renderParent(nextItem: GitHubWorkItem): Promise { - await act(async () => { - updateParentSelection(nextItem) - root.render() - }) -} - -describe('opened issue assignee ownership', () => { - beforeEach(() => { - vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true) - vi.clearAllMocks() - resetTaskPageGitHubMutationRegistryForTests() - updateParentSelection = () => {} - mocks.update.mockResolvedValue({ ok: true }) - container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) - }) - - afterEach(() => { - act(() => root.unmount()) - container.remove() - vi.unstubAllGlobals() - resetTaskPageGitHubMutationRegistryForTests() - }) - - it('retires an optimistic selection across fork and return navigation', async () => { - await render() - await click() - expect(selected()).toBe('candidate-user') - await render(item, []) - expect(selected()).toBe('candidate-user') - - await render({ ...item, url: 'https://github.com/fork/widgets/issues/5' }, ['fork-user']) - expect(selected()).toBe('fork-user') - await render(item, ['authoritative-upstream-user']) - expect(selected()).toBe('authoritative-upstream-user') - }) - - it.each(['Assign candidate', 'Toggle original'])( - 'does not roll a late failed %s edit into another opened issue', - async (button) => { - let rejectUpdate: (reason: Error) => void = () => {} - mocks.update.mockImplementation( - () => - new Promise((_resolve, reject) => { - rejectUpdate = reject - }) - ) - await render(item, ['original-user']) - await click(button) - await render({ ...item, id: 'issue:6', number: 6 }, ['other-issue-user']) - expect(selected()).toBe('other-issue-user') - await act(async () => rejectUpdate(new Error('held edit failed'))) - expect(selected()).toBe('other-issue-user') - } - ) - - it('still rolls back a failed edit while its issue remains open', async () => { - mocks.update.mockRejectedValue(new Error('edit failed')) - await render(item, ['original-user']) - await click() - expect(selected()).toBe('original-user') - await render(item, ['refetched-user']) - expect(selected()).toBe('refetched-user') - }) - - it('keeps a late Project-row rollback scoped to the captured row', async () => { - const projectOrigin: GitHubItemDialogProjectOrigin = { - owner: 'upstream', - repo: 'widgets', - number: 5, - type: 'issue', - projectId: 'project', - projectItemId: 'upstream-row', - cacheKey: 'upstream-cache' - } - let rejectUpdate: (reason: Error) => void = () => {} - mocks.update.mockImplementation( - () => - new Promise((_resolve, reject) => { - rejectUpdate = reject - }) - ) - await render(item, ['original-user'], projectOrigin) - await click() - await render(item, ['fork-user'], { - ...projectOrigin, - owner: 'fork', - projectItemId: 'fork-row', - cacheKey: 'fork-cache' - }) - expect(selected()).toBe('fork-user') - await act(async () => rejectUpdate(new Error('held Project edit failed'))) - expect(selected()).toBe('fork-user') - expect(mocks.patchProjectRowContent).toHaveBeenLastCalledWith( - 'upstream-cache', - 'upstream-row', - { assignees: ['original-user'] } - ) - }) - - it.each([ - { button: 'Toggle label', field: 'labels', expected: 'fork-only-label' }, - { button: 'Close issue', field: 'state', expected: 'closed' } - ])( - 'a late failed $field edit cannot overwrite the new parent selection', - async ({ button, field, expected }) => { - let rejectUpdate: (reason: Error) => void = () => {} - mocks.update.mockImplementation( - () => - new Promise((_resolve, reject) => { - rejectUpdate = reject - }) - ) - await renderParent({ ...item, labels: ['upstream-original-label'] }) - await click(button) - await renderParent({ - ...item, - state: 'closed', - url: 'https://github.com/fork/widgets/issues/5', - labels: ['fork-only-label'] - }) - expect(container.querySelector(`[data-field="${field}"]`)?.textContent).toBe(expected) - await act(async () => rejectUpdate(new Error('held edit failed'))) - expect(container.querySelector(`[data-field="${field}"]`)?.textContent).toBe(expected) - // A failed edit without a tracked owner never touched other copies, so it has nothing to undo there. - expect(mocks.patchWorkItem).not.toHaveBeenCalled() - } - ) - - it.each([ - { button: 'Toggle label', field: 'labels', expected: 'upstream-original-label' }, - { button: 'Close issue', field: 'state', expected: 'open' } - ])( - 'a same-item failed $field edit still restores its prior parent value', - async ({ button, field, expected }) => { - mocks.update.mockRejectedValue(new Error('same issue edit failed')) - await renderParent({ ...item, labels: ['upstream-original-label'] }) - await click(button) - expect(container.querySelector(`[data-field="${field}"]`)?.textContent).toBe(expected) - } - ) -}) diff --git a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.tsx b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.tsx index 605f411c3c2..5705223ec65 100644 --- a/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.tsx +++ b/src/renderer/src/components/github-item-dialog/edit-item-fields/gh-edit-section.tsx @@ -11,7 +11,7 @@ import { getTaskPageGitHubDuplicateTargetErrorMessage, validateTaskPageGitHubDuplicateTarget, type TaskPageGitHubCloseAction -} from '@/components/task-page/github/task-page-github-status-actions' +} from '@/components/task-page/github/issue-status/task-page-github-status-actions' import { parseGitHubItemRepository as parseOwnerRepoFromItemUrl } from '../../../../../shared/github/work-items/work-item-target' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/github-item-dialog/land-pull-request/pr-actions-panel.tsx b/src/renderer/src/components/github-item-dialog/land-pull-request/pr-actions-panel.tsx index 25b2f8e94ea..10964d3108d 100644 --- a/src/renderer/src/components/github-item-dialog/land-pull-request/pr-actions-panel.tsx +++ b/src/renderer/src/components/github-item-dialog/land-pull-request/pr-actions-panel.tsx @@ -22,7 +22,7 @@ import { getPullRequestMergeMethodLabel, getPullRequestStateConfirmationTitle } from '@/components/github/pull-request-confirmation-copy' -import { assertTaskPageGitHubDialogStateAuthority } from '@/components/task-page/github/task-page-github-dialog-state-authority' +import { assertTaskPageGitHubDialogStateAuthority } from '@/components/task-page/github/mutations/task-page-github-dialog-state-authority' import { resolvePullRequestRepo } from '@/components/github/github-work-item-identity' import { notifyWorkItemDetailsMutation } from '@/components/github/github-work-item-comment-mutations' import { runPullRequestStateUpdate } from '@/components/github/github-work-item-edit-mutations' diff --git a/src/renderer/src/components/github/work-item-state-presentation.tsx b/src/renderer/src/components/github/work-item-state-presentation.tsx index 425312e3c59..2174d933430 100644 --- a/src/renderer/src/components/github/work-item-state-presentation.tsx +++ b/src/renderer/src/components/github/work-item-state-presentation.tsx @@ -3,7 +3,7 @@ import { GitHubUserAvatar } from '@/components/github/github-user-avatar' import type { GitHubAssignableUser } from '../../../../shared/github/pull-requests/pull-request-types' import type { GitHubWorkItem } from '../../../../shared/github/work-items/work-item-types' import { formatUiRelativeTimeFromDate } from '@/i18n/relative-time-format' -import { getTaskPageGitHubWorkItemStateLabel } from '@/components/task-page/github/task-page-github-work-item-status' +import { getTaskPageGitHubWorkItemStateLabel } from '@/components/task-page/github/listing/task-page-github-work-item-status' import type { Translate } from '@/i18n/use-translate' export function formatRelativeTime(input: string, intlLocale?: string): string { diff --git a/src/renderer/src/components/task-page/Content.tsx b/src/renderer/src/components/task-page/Content.tsx index b9700f80d28..e496b877a95 100644 --- a/src/renderer/src/components/task-page/Content.tsx +++ b/src/renderer/src/components/task-page/Content.tsx @@ -3,7 +3,7 @@ import PullRequestPage from '@/components/PullRequestPage' import GitHubItemDialog from '@/components/GitHubItemDialog' import { useTranslate } from '@/i18n/use-translate' import ProjectViewWrapper from '@/components/github-project/ProjectViewWrapper' -import { TaskPageGitHubList } from './github/List' +import { TaskPageGitHubList } from './github/listing/List' import { TaskPageGitLabTodoList } from './gitlab/TodoList' import { TaskPageGitLabItemList } from './gitlab/ItemList' import { TaskPageJiraContent } from './jira/Content' diff --git a/src/renderer/src/components/task-page/ListChrome.tsx b/src/renderer/src/components/task-page/ListChrome.tsx index 74fd024d47b..5dd58fd1373 100644 --- a/src/renderer/src/components/task-page/ListChrome.tsx +++ b/src/renderer/src/components/task-page/ListChrome.tsx @@ -2,7 +2,7 @@ import type { TaskPageComposerActionsModel } from './use-task-page-composer-acti import { cn } from '@/lib/class-names' import { TaskPageSourceBar } from './SourceBar' import { AlertCircle } from 'lucide-react' -import { TaskPageGitHubModeControls } from './github/ModeControls' +import { TaskPageGitHubModeControls } from './github/listing/ModeControls' import { TaskPageProviderFilters } from './ProviderFilters' export function TaskPageListChrome({ model diff --git a/src/renderer/src/components/task-page/ProviderFilters.tsx b/src/renderer/src/components/task-page/ProviderFilters.tsx index 9c21eb8c47d..b2043802b05 100644 --- a/src/renderer/src/components/task-page/ProviderFilters.tsx +++ b/src/renderer/src/components/task-page/ProviderFilters.tsx @@ -1,5 +1,5 @@ import type { TaskPageComposerActionsModel } from './use-task-page-composer-actions' -import { TaskPageGitHubFilters } from './github/Filters' +import { TaskPageGitHubFilters } from './github/listing/Filters' import { TaskPageLinearFilters } from './linear/Filters' import { TaskPageJiraFilters } from './jira/Filters' import { TaskPageGitLabFilters } from './gitlab/Filters' diff --git a/src/renderer/src/components/task-page/Surface.tsx b/src/renderer/src/components/task-page/Surface.tsx index 2737627ec2c..736001ade30 100644 --- a/src/renderer/src/components/task-page/Surface.tsx +++ b/src/renderer/src/components/task-page/Surface.tsx @@ -1,6 +1,6 @@ import type { TaskPageComposerActionsModel } from './use-task-page-composer-actions' import { TaskPageFrame } from './Frame' -import { TaskPageGitHubIssueDialog } from './github/IssueDialog' +import { TaskPageGitHubIssueDialog } from './github/issue-creation/IssueDialog' import { TaskPageLinearProjectDialog } from './linear/ProjectDialog' import { TaskPageLinearIssueDialog } from './linear/IssueDialog' import { TaskPageJiraIssueDialog } from './jira/IssueDialog' diff --git a/src/renderer/src/components/task-page/TaskPage.tsx b/src/renderer/src/components/task-page/TaskPage.tsx index 783f360c0d6..c8d42e8d023 100644 --- a/src/renderer/src/components/task-page/TaskPage.tsx +++ b/src/renderer/src/components/task-page/TaskPage.tsx @@ -4,10 +4,10 @@ import { useTaskPageRepoSelection } from './use-task-page-repo-selection' import { useTaskPageRuntimeHosts } from './use-task-page-runtime-hosts' import { useTaskPageSourceAvailability } from './use-task-page-source-availability' import { useTaskPageProviderState } from './use-task-page-provider-state' -import { useTaskPageGitHubListState } from './github/use-task-page-github-list-state' +import { useTaskPageGitHubListState } from './github/listing/use-task-page-github-list-state' import { useTaskPageGitHubDetail } from './github/use-task-page-github-detail' -import { useTaskPageGitHubCacheReconciliation } from './github/use-task-page-github-cache-reconciliation' -import { useTaskPageGitHubIssueDraft } from './github/use-task-page-github-issue-draft' +import { useTaskPageGitHubCacheReconciliation } from './github/cache/use-task-page-github-cache-reconciliation' +import { useTaskPageGitHubIssueDraft } from './github/issue-creation/use-task-page-github-issue-draft' import { useTaskPageDetailRouting } from './use-task-page-detail-routing' import { useTaskPageLinearViewState } from './linear/use-task-page-linear-view-state' import { useTaskPageJiraListState } from './jira/use-task-page-jira-list-state' @@ -19,16 +19,16 @@ import { useTaskPageLinearListProjection } from './linear/use-task-page-linear-l import { useTaskPageLinearBoard } from './linear/use-task-page-linear-board' import { useTaskPageJiraListProjection } from './jira/use-task-page-jira-list-projection' import { useTaskPageLinearCreationState } from './linear/use-task-page-linear-creation-state' -import { useTaskPageGitHubMutationState } from './github/use-task-page-github-mutation-state' +import { useTaskPageGitHubMutationState } from './github/mutations/use-task-page-github-mutation-state' import { useTaskPageJiraCreationState } from './jira/use-task-page-jira-creation-state' import { useTaskPageJiraCreationMetadata } from './jira/use-task-page-jira-creation-metadata' -import { useTaskPageGitHubListProjection } from './github/use-task-page-github-list-projection' -import { useTaskPageGitHubSearchPagination } from './github/use-task-page-github-search-pagination' -import { useTaskPageGitHubLandingRefresh } from './github/use-task-page-github-landing-refresh' -import { useTaskPageGitHubQuietRefresh } from './github/use-task-page-github-quiet-refresh' +import { useTaskPageGitHubListProjection } from './github/listing/use-task-page-github-list-projection' +import { useTaskPageGitHubSearchPagination } from './github/listing/use-task-page-github-search-pagination' +import { useTaskPageGitHubLandingRefresh } from './github/refresh/use-task-page-github-landing-refresh' +import { useTaskPageGitHubQuietRefresh } from './github/refresh/use-task-page-github-quiet-refresh' import { useTaskPageSearchActions } from './use-task-page-search-actions' import { useTaskPageWorkspaceActions } from './use-task-page-workspace-actions' -import { useTaskPageGitHubIssueCreation } from './github/use-task-page-github-issue-creation' +import { useTaskPageGitHubIssueCreation } from './github/issue-creation/use-task-page-github-issue-creation' import { useTaskPageLinearProjectCreation } from './linear/use-task-page-linear-project-creation' import { useTaskPageLinearIssueCreation } from './linear/use-task-page-linear-issue-creation' import { useTaskPageJiraIssueCreation } from './jira/use-task-page-jira-issue-creation' diff --git a/src/renderer/src/components/task-page/github/task-page-github-resume-cache.ts b/src/renderer/src/components/task-page/github/cache/task-page-github-resume-cache.ts similarity index 96% rename from src/renderer/src/components/task-page/github/task-page-github-resume-cache.ts rename to src/renderer/src/components/task-page/github/cache/task-page-github-resume-cache.ts index 3306b23b7a6..4a8f2b11eb5 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-resume-cache.ts +++ b/src/renderer/src/components/task-page/github/cache/task-page-github-resume-cache.ts @@ -1,4 +1,4 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' export const TASK_PAGE_GITHUB_RESUME_CACHE_LIMIT = 5 export const TASK_PAGE_GITHUB_RESUME_CACHE_TTL_MS = 10 * 60_000 diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-cache-reconciliation.ts b/src/renderer/src/components/task-page/github/cache/use-task-page-github-cache-reconciliation.ts similarity index 93% rename from src/renderer/src/components/task-page/github/use-task-page-github-cache-reconciliation.ts rename to src/renderer/src/components/task-page/github/cache/use-task-page-github-cache-reconciliation.ts index 6bdf228e00e..926d11ae214 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-cache-reconciliation.ts +++ b/src/renderer/src/components/task-page/github/cache/use-task-page-github-cache-reconciliation.ts @@ -1,11 +1,11 @@ -import type { TaskPageGitHubDetailModel } from './use-task-page-github-detail' +import type { TaskPageGitHubDetailModel } from '../use-task-page-github-detail' import { useCallback, useMemo, useEffect, useRef, useState } from 'react' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' import { patchTaskPageGitHubWorkItemPages, overlayPendingOnTaskPagePages -} from '@/components/task-page/github/task-page-github-work-item-mutation-pages' +} from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-pages' import { type TaskPageRepoSourceState, buildTaskPageRepoSourceState, diff --git a/src/renderer/src/components/task-page/github/use-task-page-work-items-cache-selection.ts b/src/renderer/src/components/task-page/github/cache/use-task-page-work-items-cache-selection.ts similarity index 92% rename from src/renderer/src/components/task-page/github/use-task-page-work-items-cache-selection.ts rename to src/renderer/src/components/task-page/github/cache/use-task-page-work-items-cache-selection.ts index 7e432e11be5..9ab4d2dfc1a 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-work-items-cache-selection.ts +++ b/src/renderer/src/components/task-page/github/cache/use-task-page-work-items-cache-selection.ts @@ -5,9 +5,9 @@ import { getTaskPageRepoCacheRows, createTaskPageWorkItemsCacheSelector } from '@/components/task-page/task-page-cache-selectors' -import { isGitHubWorkItemsQueryTooLarge } from '../../../../../shared/github/work-items/work-items-query-bounds' -import type { Repo } from '../../../../../shared/repo-types' -import { getTaskPageRepoCacheInput } from '../task-page-source-context' +import { isGitHubWorkItemsQueryTooLarge } from '../../../../../../shared/github/work-items/work-items-query-bounds' +import type { Repo } from '../../../../../../shared/repo-types' +import { getTaskPageRepoCacheInput } from '../../task-page-source-context' /** Selected rows' current-query cache entries, plus the entries the source displays read (see the selector). */ export function useTaskPageWorkItemsCacheSelection( diff --git a/src/renderer/src/components/task-page/github/IssueDialog.tsx b/src/renderer/src/components/task-page/github/issue-creation/IssueDialog.tsx similarity index 98% rename from src/renderer/src/components/task-page/github/IssueDialog.tsx rename to src/renderer/src/components/task-page/github/issue-creation/IssueDialog.tsx index 5df25094af5..60a86c92cf7 100644 --- a/src/renderer/src/components/task-page/github/IssueDialog.tsx +++ b/src/renderer/src/components/task-page/github/issue-creation/IssueDialog.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { useRef, useState } from 'react' import { getGitHubRuntimeRepoId } from '@/lib/work-items/github/github-source-runtime-context' import { @@ -12,7 +12,7 @@ import { import { isScreenSubmitShortcut } from '@/lib/keyboard/screen-submit-shortcut' import { useTranslate } from '@/i18n/use-translate' import { sameGitHubOwnerRepo } from '@/components/github/IssueSourceIndicator' -import { getRepoExecutionHostId } from '../../../../../shared/execution-host' +import { getRepoExecutionHostId } from '../../../../../../shared/execution-host' import { repoMatchesHostIdentity } from '@/store/repos/repo-host-identity' import IssueSourceSelector from '@/components/github/IssueSourceSelector' import { diff --git a/src/renderer/src/components/task-page/github/IssueSelectors.tsx b/src/renderer/src/components/task-page/github/issue-creation/IssueSelectors.tsx similarity index 97% rename from src/renderer/src/components/task-page/github/IssueSelectors.tsx rename to src/renderer/src/components/task-page/github/issue-creation/IssueSelectors.tsx index 3e8f289e0fd..b3327c2fba5 100644 --- a/src/renderer/src/components/task-page/github/IssueSelectors.tsx +++ b/src/renderer/src/components/task-page/github/issue-creation/IssueSelectors.tsx @@ -4,8 +4,8 @@ import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover import { Button } from '@/components/ui/button' import { LoaderCircle, Check } from 'lucide-react' import { cn } from '@/lib/class-names' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import { GitHubAssigneeAvatar } from './Avatars' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import { GitHubAssigneeAvatar } from '../listing/Avatars' export function GitHubIssueLabelSelector({ labels, selectedLabels, diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-issue-creation.ts b/src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-creation.ts similarity index 97% rename from src/renderer/src/components/task-page/github/use-task-page-github-issue-creation.ts rename to src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-creation.ts index 1c96f6cdba2..36ee1b6a085 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-issue-creation.ts +++ b/src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-creation.ts @@ -1,10 +1,10 @@ -import type { TaskPageWorkspaceActionsModel } from '../use-task-page-workspace-actions' +import type { TaskPageWorkspaceActionsModel } from '../../use-task-page-workspace-actions' import { useCallback } from 'react' import { callRuntimeRpc } from '@/runtime/rpc/calls/runtime-rpc-call' import { useAppStore } from '@/store/index' import { toast } from 'sonner' import { translate } from '@/i18n/i18n' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' import { codeHostErrorText } from '@/lib/source-control/remote/code-host-error-copy' import { openAppHttpLink } from '@/lib/browser/navigation/app-http-link-open' type TaskPageGitHubIssueCreationInput = Pick< diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-issue-draft.ts b/src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-draft.ts similarity index 94% rename from src/renderer/src/components/task-page/github/use-task-page-github-issue-draft.ts rename to src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-draft.ts index 86f78ea3bce..cc09af77d7e 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-issue-draft.ts +++ b/src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-draft.ts @@ -1,6 +1,6 @@ -import type { TaskPageGitHubCacheReconciliationModel } from './use-task-page-github-cache-reconciliation' +import type { TaskPageGitHubCacheReconciliationModel } from '../cache/use-task-page-github-cache-reconciliation' import { useState, useMemo, useEffect } from 'react' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' import { useAppStore } from '@/store/index' import { getGitHubRepoRoutingTarget } from '@/lib/work-items/github/github-source-runtime-context' import { useRepoLabels, useRepoAssignees } from '@/hooks/useGitHubRepoMetadata' @@ -8,7 +8,7 @@ import { resolveVanishedNewIssueRepoReset, isNewIssueDraftContentful } from '@/components/task-page/task-page-new-issue-draft' -import { getTaskPageRepoSourceContext } from '../../../lib/work-items/task-repo-source-context' +import { getTaskPageRepoSourceContext } from '../../../../lib/work-items/task-repo-source-context' export function useTaskPageGitHubIssueDraft(model: TaskPageGitHubCacheReconciliationModel) { const { settings, repos, selectedRepos } = model const [newIssueOpen, setNewIssueOpen] = useState(false) diff --git a/src/renderer/src/components/task-page/github/DuplicatePicker.tsx b/src/renderer/src/components/task-page/github/issue-status/DuplicatePicker.tsx similarity index 97% rename from src/renderer/src/components/task-page/github/DuplicatePicker.tsx rename to src/renderer/src/components/task-page/github/issue-status/DuplicatePicker.tsx index 7d6b03e5b5f..a7c5148069b 100644 --- a/src/renderer/src/components/task-page/github/DuplicatePicker.tsx +++ b/src/renderer/src/components/task-page/github/issue-status/DuplicatePicker.tsx @@ -2,7 +2,7 @@ import type React from 'react' import { CheckCircle2, ChevronLeft, CircleDot, Copy, Search } from 'lucide-react' import { useTranslate } from '@/i18n/use-translate' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' diff --git a/src/renderer/src/components/task-page/github/StatusCell.tsx b/src/renderer/src/components/task-page/github/issue-status/StatusCell.tsx similarity index 93% rename from src/renderer/src/components/task-page/github/StatusCell.tsx rename to src/renderer/src/components/task-page/github/issue-status/StatusCell.tsx index 5fdb7f29398..a08d0c80fac 100644 --- a/src/renderer/src/components/task-page/github/StatusCell.tsx +++ b/src/renderer/src/components/task-page/github/issue-status/StatusCell.tsx @@ -1,34 +1,34 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { GitHubIssueUpdate } from '../../../../../shared/issue-mutation-types' -import type { Repo } from '../../../../../shared/repo-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { GitHubIssueUpdate } from '../../../../../../shared/issue-mutation-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' import React, { useState, useMemo, useCallback } from 'react' import { createTaskPageGitHubStatusStateDraft, resolveTaskPageGitHubStatusStateDraft, updateTaskPageGitHubStatusLocalState -} from '@/components/task-page/github/task-page-github-status-state' +} from '@/components/task-page/github/issue-status/task-page-github-status-state' import { useAppStore } from '@/store/index' import { useShallow } from 'zustand/react/shallow' import { getGitHubRepoRoutingTarget } from '@/lib/work-items/github/github-source-runtime-context' -import { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' +import { parseGitHubIssueOrPRLink } from '../../../../../../shared/github/repositories/links' import { getTaskPageGitHubDuplicateCandidates, validateTaskPageGitHubDuplicateTarget, type TaskPageGitHubCloseAction, buildTaskPageGitHubCloseUpdate, getTaskPageGitHubDuplicateTargetErrorMessage -} from '@/components/task-page/github/task-page-github-status-actions' +} from '@/components/task-page/github/issue-status/task-page-github-status-actions' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' import { callRuntimeRpc } from '@/runtime/rpc/calls/runtime-rpc-call' import { gitHubSlugApiForTarget } from '@/runtime/repository-github/github-slug-owner-api' -import { githubProjectHost } from '../../../../../shared/github/projects/project-identity' -import { TaskPageGitHubWorkItemStateBadge } from '@/components/task-page/github/task-page-github-work-item-status-badge' +import { githubProjectHost } from '../../../../../../shared/github/projects/project-identity' +import { TaskPageGitHubWorkItemStateBadge } from '@/components/task-page/github/listing/task-page-github-work-item-status-badge' import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover' import { cn } from '@/lib/class-names' import { CircleDot, ChevronDown, Copy, CheckCircle2, Ban, ChevronRight } from 'lucide-react' -import type { TaskPageGitHubWorkItemMutationRunner } from '../linear/task-page-linear-jira-list-model' +import type { TaskPageGitHubWorkItemMutationRunner } from '../../linear/task-page-linear-jira-list-model' import { TaskPageGitHubDuplicatePicker } from './DuplicatePicker' import { useGitHubDuplicateIssueCandidates } from '@/components/github/github-duplicate-issue-candidates' diff --git a/src/renderer/src/components/task-page/github/task-page-github-status-actions.ts b/src/renderer/src/components/task-page/github/issue-status/task-page-github-status-actions.ts similarity index 94% rename from src/renderer/src/components/task-page/github/task-page-github-status-actions.ts rename to src/renderer/src/components/task-page/github/issue-status/task-page-github-status-actions.ts index 01628da6fef..e565281a0c1 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-status-actions.ts +++ b/src/renderer/src/components/task-page/github/issue-status/task-page-github-status-actions.ts @@ -1,8 +1,8 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' import type { GitHubIssueCloseReason, GitHubIssueUpdate -} from '../../../../../shared/issue-mutation-types' +} from '../../../../../../shared/issue-mutation-types' export type TaskPageGitHubCloseAction = | { stateReason: Exclude } diff --git a/src/renderer/src/components/task-page/github/task-page-github-status-state.ts b/src/renderer/src/components/task-page/github/issue-status/task-page-github-status-state.ts similarity index 91% rename from src/renderer/src/components/task-page/github/task-page-github-status-state.ts rename to src/renderer/src/components/task-page/github/issue-status/task-page-github-status-state.ts index 7ebd889cafd..d69ff9669e3 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-status-state.ts +++ b/src/renderer/src/components/task-page/github/issue-status/task-page-github-status-state.ts @@ -1,4 +1,4 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' type GitHubStatusItem = Pick diff --git a/src/renderer/src/components/task-page/github/AssigneesCell.tsx b/src/renderer/src/components/task-page/github/listing/AssigneesCell.tsx similarity index 93% rename from src/renderer/src/components/task-page/github/AssigneesCell.tsx rename to src/renderer/src/components/task-page/github/listing/AssigneesCell.tsx index 6d923beb901..a573bc3441b 100644 --- a/src/renderer/src/components/task-page/github/AssigneesCell.tsx +++ b/src/renderer/src/components/task-page/github/listing/AssigneesCell.tsx @@ -1,22 +1,22 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { Repo } from '../../../../../shared/repo-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' import React, { useMemo, useState, useCallback } from 'react' import { useAppStore } from '@/store/index' import { useShallow } from 'zustand/react/shallow' import { getGitHubRepoRoutingTarget } from '@/lib/work-items/github/github-source-runtime-context' -import { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' +import { parseGitHubIssueOrPRLink } from '../../../../../../shared/github/repositories/links' import { useRepoAssigneesBySlug } from '@/hooks/useGitHubSlugMetadata' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' import { callRuntimeRpc } from '@/runtime/rpc/calls/runtime-rpc-call' import { gitHubSlugApiForTarget } from '@/runtime/repository-github/github-slug-owner-api' -import { githubProjectHost } from '../../../../../shared/github/projects/project-identity' +import { githubProjectHost } from '../../../../../../shared/github/projects/project-identity' import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover' import { cn } from '@/lib/class-names' import { LoaderCircle, ChevronDown, Check } from 'lucide-react' -import type { TaskPageGitHubWorkItemMutationRunner } from '../linear/task-page-linear-jira-list-model' +import type { TaskPageGitHubWorkItemMutationRunner } from '../../linear/task-page-linear-jira-list-model' import { GitHubAssigneeAvatar } from './Avatars' import { GitHubUserAvatar } from '@/components/github/github-user-avatar' import { codeHostErrorText } from '@/lib/source-control/remote/code-host-error-copy' diff --git a/src/renderer/src/components/task-page/github/Avatars.tsx b/src/renderer/src/components/task-page/github/listing/Avatars.tsx similarity index 84% rename from src/renderer/src/components/task-page/github/Avatars.tsx rename to src/renderer/src/components/task-page/github/listing/Avatars.tsx index 6305ad6c57e..1719d7267fc 100644 --- a/src/renderer/src/components/task-page/github/Avatars.tsx +++ b/src/renderer/src/components/task-page/github/listing/Avatars.tsx @@ -1,8 +1,8 @@ -import type { GitHubPRPrimaryReviewer } from '../../../../../shared/github/pull-requests/pr-reviewer-display' +import type { GitHubPRPrimaryReviewer } from '../../../../../../shared/github/pull-requests/pr-reviewer-display' import React from 'react' import { GitHubUserAvatar } from '@/components/github/github-user-avatar' import { Users } from 'lucide-react' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' export function ReviewChipAvatar({ reviewer, avatarHost diff --git a/src/renderer/src/components/task-page/github/Filters.tsx b/src/renderer/src/components/task-page/github/listing/Filters.tsx similarity index 99% rename from src/renderer/src/components/task-page/github/Filters.tsx rename to src/renderer/src/components/task-page/github/listing/Filters.tsx index a57878bb346..b64ff20fe0e 100644 --- a/src/renderer/src/components/task-page/github/Filters.tsx +++ b/src/renderer/src/components/task-page/github/listing/Filters.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { getGitHubPRStateChips, getGitHubPRWhoChips, @@ -15,7 +15,7 @@ import { resolveNewIssueOpenSeed } from '@/components/task-page/task-page-new-is import { useAppStore } from '@/store/index' import { useShallow } from 'zustand/react/shallow' import { getGitHubRepoRoutingTarget } from '@/lib/work-items/github/github-source-runtime-context' -import { hasUpstreamCandidateDivergence, hasDivergentSources } from '../task-page-draft-storage' +import { hasUpstreamCandidateDivergence, hasDivergentSources } from '../../task-page-draft-storage' import IssueSourceIndicator from '@/components/github/IssueSourceIndicator' import IssueSourceSelector, { issueSourceChipClass } from '@/components/github/IssueSourceSelector' import RepoBadgeLabel from '@/components/repo/RepoBadgeLabel' diff --git a/src/renderer/src/components/task-page/github/List.tsx b/src/renderer/src/components/task-page/github/listing/List.tsx similarity index 98% rename from src/renderer/src/components/task-page/github/List.tsx rename to src/renderer/src/components/task-page/github/listing/List.tsx index c90b4e4d7d3..42374fe3136 100644 --- a/src/renderer/src/components/task-page/github/List.tsx +++ b/src/renderer/src/components/task-page/github/listing/List.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { useAppStore } from '@/store/index' import { cn } from '@/lib/class-names' import { @@ -7,12 +7,12 @@ import { GITHUB_TASK_STICKY_TITLE_HEADER_CLASS, GITHUB_TASK_STICKY_ID_CELL_CLASS, GITHUB_TASK_STICKY_TITLE_CELL_CLASS -} from '../task-page-source-context' +} from '../../task-page-source-context' import { useTranslate } from '@/i18n/use-translate' import { Button } from '@/components/ui/button' import { LoaderCircle } from 'lucide-react' import { TaskPageGitHubRows } from './Rows' -import { PaginationBar } from '../PaginationBar' +import { PaginationBar } from '../../PaginationBar' import { supersedeGitHubListScrollRestore } from './task-page-github-list-scroll-restore' export function TaskPageGitHubList({ model diff --git a/src/renderer/src/components/task-page/github/ModeControls.tsx b/src/renderer/src/components/task-page/github/listing/ModeControls.tsx similarity index 98% rename from src/renderer/src/components/task-page/github/ModeControls.tsx rename to src/renderer/src/components/task-page/github/listing/ModeControls.tsx index 5b4d6e83a12..4fe5e971b50 100644 --- a/src/renderer/src/components/task-page/github/ModeControls.tsx +++ b/src/renderer/src/components/task-page/github/listing/ModeControls.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { cn } from '@/lib/class-names' import TaskProjectSourceCombobox from '@/components/task-project-source-combobox' import { normalizeTaskRepoSelection } from '@/components/task-page/task-page-default-repo-selection' diff --git a/src/renderer/src/components/task-page/github/Rows.tsx b/src/renderer/src/components/task-page/github/listing/Rows.tsx similarity index 96% rename from src/renderer/src/components/task-page/github/Rows.tsx rename to src/renderer/src/components/task-page/github/listing/Rows.tsx index 62de36cb12c..186a5297722 100644 --- a/src/renderer/src/components/task-page/github/Rows.tsx +++ b/src/renderer/src/components/task-page/github/listing/Rows.tsx @@ -1,13 +1,13 @@ -import { getTaskPageRepoSourceContext } from '../../../lib/work-items/task-repo-source-context' -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import { getTaskPageRepoSourceContext } from '../../../../lib/work-items/task-repo-source-context' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { getWorkItemRepo } from '@/lib/work-items/work-item-repo-row' import { findGithubWorkItemWorkspaceAttachment } from '@/lib/work-items/github/github-work-item-workspace-attachment' import { getWorktreeAttachmentLabel } from '@/lib/workspace/display/worktree-attachment-label' -import { formatPRDelta } from '../task-page-pr-delta-summary' +import { formatPRDelta } from '../../task-page-pr-delta-summary' import { isTaskPageGitHubDraftPR, getTaskPageGitHubPRIconTone -} from '@/components/task-page/github/task-page-github-work-item-status' +} from '@/components/task-page/github/listing/task-page-github-work-item-status' import { GitPullRequestDraft, GitPullRequest, @@ -25,16 +25,16 @@ import { GITHUB_TASK_STICKY_ID_CELL_CLASS, GITHUB_TASK_STICKY_TITLE_CELL_CLASS, formatRelativeTime -} from '../task-page-source-context' +} from '../../task-page-source-context' import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' import { useIntlLocale, useTranslate } from '@/i18n/use-translate' -import { TaskPageGitHubWorkItemStateBadge } from '@/components/task-page/github/task-page-github-work-item-status-badge' +import { TaskPageGitHubWorkItemStateBadge } from '@/components/task-page/github/listing/task-page-github-work-item-status-badge' import RepoBadgeLabel from '@/components/repo/RepoBadgeLabel' import { GHAssigneesCell } from './AssigneesCell' -import { PRReviewCell } from './ReviewCell' -import { PRChecksCell } from './ChecksCell' -import { PRMergeCell } from './MergeCell' -import { GHStatusCell } from './StatusCell' +import { PRReviewCell } from '../pull-requests/ReviewCell' +import { PRChecksCell } from '../pull-requests/ChecksCell' +import { PRMergeCell } from '../pull-requests/MergeCell' +import { GHStatusCell } from '../issue-status/StatusCell' import { DropdownMenu, DropdownMenuTrigger, diff --git a/src/renderer/src/components/task-page/github/task-page-github-list-scroll-restore.ts b/src/renderer/src/components/task-page/github/listing/task-page-github-list-scroll-restore.ts similarity index 100% rename from src/renderer/src/components/task-page/github/task-page-github-list-scroll-restore.ts rename to src/renderer/src/components/task-page/github/listing/task-page-github-list-scroll-restore.ts diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-status-badge.tsx b/src/renderer/src/components/task-page/github/listing/task-page-github-work-item-status-badge.tsx similarity index 89% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-status-badge.tsx rename to src/renderer/src/components/task-page/github/listing/task-page-github-work-item-status-badge.tsx index 5e0319ee08a..ca46349da1c 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-status-badge.tsx +++ b/src/renderer/src/components/task-page/github/listing/task-page-github-work-item-status-badge.tsx @@ -4,7 +4,7 @@ import { getTaskPageGitHubWorkItemStateLabel, getTaskPageGitHubWorkItemStateTone, type GitHubWorkItemStatusItem -} from '@/components/task-page/github/task-page-github-work-item-status' +} from '@/components/task-page/github/listing/task-page-github-work-item-status' import { useTranslate } from '@/i18n/use-translate' export function TaskPageGitHubWorkItemStateBadge({ diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-status.ts b/src/renderer/src/components/task-page/github/listing/task-page-github-work-item-status.ts similarity index 96% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-status.ts rename to src/renderer/src/components/task-page/github/listing/task-page-github-work-item-status.ts index 139ad4c6410..0229ce7eebd 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-status.ts +++ b/src/renderer/src/components/task-page/github/listing/task-page-github-work-item-status.ts @@ -1,6 +1,6 @@ import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' export type GitHubWorkItemStatusItem = Pick diff --git a/src/renderer/src/components/task-page/github/task-page-pr-chips.test.ts b/src/renderer/src/components/task-page/github/listing/task-page-pr-chips.test.ts similarity index 100% rename from src/renderer/src/components/task-page/github/task-page-pr-chips.test.ts rename to src/renderer/src/components/task-page/github/listing/task-page-pr-chips.test.ts diff --git a/src/renderer/src/components/task-page/github/task-page-pr-chips.ts b/src/renderer/src/components/task-page/github/listing/task-page-pr-chips.ts similarity index 91% rename from src/renderer/src/components/task-page/github/task-page-pr-chips.ts rename to src/renderer/src/components/task-page/github/listing/task-page-pr-chips.ts index f5102a46bd6..650765e22ea 100644 --- a/src/renderer/src/components/task-page/github/task-page-pr-chips.ts +++ b/src/renderer/src/components/task-page/github/listing/task-page-pr-chips.ts @@ -3,10 +3,10 @@ import { parseTaskQuery, serializeTaskQuery, withQualifier -} from '../../../../../shared/work-items/task-query' -import type { ParsedTaskQuery } from '../../../../../shared/work-items/task-query' -import { getTaskPresetQuery } from '../../../../../shared/work-items/task-preset-query' -import type { TaskViewPresetId } from '../../../../../shared/ui-chrome-types' +} from '../../../../../../shared/work-items/task-query' +import type { ParsedTaskQuery } from '../../../../../../shared/work-items/task-query' +import { getTaskPresetQuery } from '../../../../../../shared/work-items/task-preset-query' +import type { TaskViewPresetId } from '../../../../../../shared/ui-chrome-types' export type GitHubPRStateChip = 'open' | 'merged' | 'closed' | 'all' export type GitHubPRWhoChip = 'everyone' | 'mine' | 'reviewing' diff --git a/src/renderer/src/components/task-page/github/task-page-preset-highlight.ts b/src/renderer/src/components/task-page/github/listing/task-page-preset-highlight.ts similarity index 71% rename from src/renderer/src/components/task-page/github/task-page-preset-highlight.ts rename to src/renderer/src/components/task-page/github/listing/task-page-preset-highlight.ts index 6fc9097da8d..2793368b7b7 100644 --- a/src/renderer/src/components/task-page/github/task-page-preset-highlight.ts +++ b/src/renderer/src/components/task-page/github/listing/task-page-preset-highlight.ts @@ -1,6 +1,6 @@ -import type { ParsedTaskQuery } from '../../../../../shared/work-items/task-query' -import type { GitHubTaskKind } from '../../../../../shared/work-items/task-github-search' -import type { TaskViewPresetId } from '../../../../../shared/ui-chrome-types' +import type { ParsedTaskQuery } from '../../../../../../shared/work-items/task-query' +import type { GitHubTaskKind } from '../../../../../../shared/work-items/task-github-search' +import type { TaskViewPresetId } from '../../../../../../shared/ui-chrome-types' export function deriveGitHubTaskPreset( kind: GitHubTaskKind, query: ParsedTaskQuery diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-collection-state.ts b/src/renderer/src/components/task-page/github/listing/use-task-page-github-collection-state.ts similarity index 86% rename from src/renderer/src/components/task-page/github/use-task-page-github-collection-state.ts rename to src/renderer/src/components/task-page/github/listing/use-task-page-github-collection-state.ts index f2792452c49..ee3799e4a1c 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-collection-state.ts +++ b/src/renderer/src/components/task-page/github/listing/use-task-page-github-collection-state.ts @@ -1,12 +1,12 @@ import { useState, useCallback, useMemo, useSyncExternalStore, type SetStateAction } from 'react' -import type { TaskPageProviderStateModel } from '../use-task-page-provider-state' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import { sortWorkItemsByNumber } from '../../../../../shared/work-items/work-item-order' -import { stripRepoQualifiers } from '../../../../../shared/work-items/task-query' +import type { TaskPageProviderStateModel } from '../../use-task-page-provider-state' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import { sortWorkItemsByNumber } from '../../../../../../shared/work-items/work-item-order' +import { stripRepoQualifiers } from '../../../../../../shared/work-items/task-query' import type { GitHubSourceCollections } from '@/store/github/sources/source-collections' import { githubCollectionScope } from '@/store/github/sources/source-preference-ownership' -import { getTaskPageRepoSourceContext } from '../task-page-source-context' -import { buildTaskPageGitHubResumeContextKey } from './task-page-github-resume-cache' +import { getTaskPageRepoSourceContext } from '../../../../lib/work-items/task-repo-source-context' +import { buildTaskPageGitHubResumeContextKey } from '../cache/task-page-github-resume-cache' type CollectionInput = Pick< TaskPageProviderStateModel, diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-list-pending.ts b/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-pending.ts similarity index 95% rename from src/renderer/src/components/task-page/github/use-task-page-github-list-pending.ts rename to src/renderer/src/components/task-page/github/listing/use-task-page-github-list-pending.ts index 3636d205d42..60c4063fb2e 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-list-pending.ts +++ b/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-pending.ts @@ -1,5 +1,5 @@ import { useAppStore } from '@/store/index' -import type { TaskPageJiraCreationMetadataModel } from '../jira/use-task-page-jira-creation-metadata' +import type { TaskPageJiraCreationMetadataModel } from '../../jira/use-task-page-jira-creation-metadata' // Why the list projection's input model: the landing run's later-stage model carries these same fields. type LandingRequestInput = Pick< diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-list-projection.ts b/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-projection.ts similarity index 90% rename from src/renderer/src/components/task-page/github/use-task-page-github-list-projection.ts rename to src/renderer/src/components/task-page/github/listing/use-task-page-github-list-projection.ts index 1945976b170..a45c6318b84 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-list-projection.ts +++ b/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-projection.ts @@ -1,12 +1,12 @@ import { useCallback, useMemo } from 'react' import { useGitHubTaskItems } from '@/hooks/useGitHubTaskItems' -import type { TaskPageJiraCreationMetadataModel } from '../jira/use-task-page-jira-creation-metadata' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import { taskPageGitHubItemKey } from '@/components/task-page/github/task-page-github-work-item-mutation-keys' -import { deriveAdvertisedTotalPages } from '../../../../../shared/work-items/work-item-empty-page' -import { GITHUB_PR_TASK_GRID_CLASS, GITHUB_TASK_GRID_CLASS } from '../task-page-source-context' +import type { TaskPageJiraCreationMetadataModel } from '../../jira/use-task-page-jira-creation-metadata' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import { taskPageGitHubItemKey } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-keys' +import { deriveAdvertisedTotalPages } from '../../../../../../shared/work-items/work-item-empty-page' +import { GITHUB_PR_TASK_GRID_CLASS, GITHUB_TASK_GRID_CLASS } from '../../task-page-source-context' import { useTaskPageGitHubListPending } from './use-task-page-github-list-pending' -import { useTaskPagePRCheckHydration } from './use-task-page-pr-check-hydration' +import { useTaskPagePRCheckHydration } from '../pull-requests/use-task-page-pr-check-hydration' export function useTaskPageGitHubListProjection(model: TaskPageJiraCreationMetadataModel) { const { tasksFiltering, diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-list-state.ts b/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-state.ts similarity index 97% rename from src/renderer/src/components/task-page/github/use-task-page-github-list-state.ts rename to src/renderer/src/components/task-page/github/listing/use-task-page-github-list-state.ts index c014170602d..e443699a30a 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-list-state.ts +++ b/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-state.ts @@ -1,13 +1,13 @@ import { useTaskPageGitHubCollectionState } from './use-task-page-github-collection-state' -import type { TaskPageProviderStateModel } from '../use-task-page-provider-state' +import type { TaskPageProviderStateModel } from '../../use-task-page-provider-state' import { useState, useRef, useEffect, useLayoutEffect, useSyncExternalStore } from 'react' -import type { TaskViewPresetId } from '../../../../../shared/ui-chrome-types' +import type { TaskViewPresetId } from '../../../../../../shared/ui-chrome-types' import { PER_REPO_FETCH_LIMIT, CROSS_REPO_DISPLAY_LIMIT, getTaskPagePerRepoLimit -} from '../../../../../shared/work-items/work-item-pagination' -import { taskPageGitHubResumeCache } from '@/components/task-page/github/task-page-github-resume-cache' +} from '../../../../../../shared/work-items/work-item-pagination' +import { taskPageGitHubResumeCache } from '@/components/task-page/github/cache/task-page-github-resume-cache' import { githubTaskBinding } from '@/lib/work-items/github/github-task-observation' import { useAppStore } from '@/store/index' import type { GitHubListRestoreWrite } from './task-page-github-list-scroll-restore' diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-search-pagination.ts b/src/renderer/src/components/task-page/github/listing/use-task-page-github-search-pagination.ts similarity index 93% rename from src/renderer/src/components/task-page/github/use-task-page-github-search-pagination.ts rename to src/renderer/src/components/task-page/github/listing/use-task-page-github-search-pagination.ts index c122de5c9e1..6b51e915d29 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-search-pagination.ts +++ b/src/renderer/src/components/task-page/github/listing/use-task-page-github-search-pagination.ts @@ -1,18 +1,18 @@ import type { TaskPageGitHubListProjectionModel } from './use-task-page-github-list-projection' import { useCallback, useEffect } from 'react' -import { stripRepoQualifiers } from '../../../../../shared/work-items/task-query' -import { taskPageToGitHubApiPage } from '../../../../../shared/work-items/work-item-pagination' +import { stripRepoQualifiers } from '../../../../../../shared/work-items/task-query' +import { taskPageToGitHubApiPage } from '../../../../../../shared/work-items/work-item-pagination' import { resolveEmptyPageOutcome, applyEmptyPageClamp, applyWindowPageLimit -} from '../../../../../shared/work-items/work-item-empty-page' +} from '../../../../../../shared/work-items/work-item-empty-page' import { toast } from 'sonner' import { translate } from '@/i18n/i18n' -import { overlayPendingOnTaskPagePages } from '@/components/task-page/github/task-page-github-work-item-mutation-pages' -import { scopeGitHubTaskSearch } from '../../../../../shared/work-items/task-github-search' +import { overlayPendingOnTaskPagePages } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-pages' +import { scopeGitHubTaskSearch } from '../../../../../../shared/work-items/task-github-search' import { useGitHubTaskSearchCommit } from '@/components/use-github-task-search-commit' -import { getTaskPageRepoSourceContext } from '../../../lib/work-items/task-repo-source-context' +import { getTaskPageRepoSourceContext } from '../../../../lib/work-items/task-repo-source-context' export function useTaskPageGitHubSearchPagination(model: TaskPageGitHubListProjectionModel) { const { setTaskResumeState, diff --git a/src/renderer/src/components/task-page/github/task-page-github-confirmed-client-values.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-confirmed-client-values.ts similarity index 90% rename from src/renderer/src/components/task-page/github/task-page-github-confirmed-client-values.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-confirmed-client-values.ts index 074ecc96171..7c93f208259 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-confirmed-client-values.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-confirmed-client-values.ts @@ -1,5 +1,5 @@ -import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../shared/github/repositories/repository-identity' -import { githubRepoIdentityKey } from '../../../../../shared/github/repositories/repository-identity-key' +import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../../shared/github/repositories/repository-identity' +import { githubRepoIdentityKey } from '../../../../../../shared/github/repositories/repository-identity-key' import { taskPageGitHubLastConfirmedKey } from './task-page-github-work-item-mutation-keys' const lastConfirmedClientValues = new Map< diff --git a/src/renderer/src/components/task-page/github/task-page-github-dialog-state-authority.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-dialog-state-authority.ts similarity index 88% rename from src/renderer/src/components/task-page/github/task-page-github-dialog-state-authority.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-dialog-state-authority.ts index 3761ec4101d..66a23c18c64 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-dialog-state-authority.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-dialog-state-authority.ts @@ -1,9 +1,9 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../shared/github/repositories/repository-identity' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../../shared/github/repositories/repository-identity' import { getTaskSourceCacheScope, type TaskSourceContext -} from '../../../../../shared/work-items/task-source-context' +} from '../../../../../../shared/work-items/task-source-context' import { deleteLastConfirmedClientValue, getLastConfirmedClientValue @@ -13,7 +13,7 @@ import { notifyTaskPageGitHubMutationRegistry, setLastConfirmedClientValue } from './task-page-github-work-item-mutation-registry' -import { markTaskPageGitHubFamiliesDirty } from './task-page-github-work-item-quiet-state' +import { markTaskPageGitHubFamiliesDirty } from '../refresh/task-page-github-work-item-quiet-state' import { taskPageGitHubItemKey } from './task-page-github-work-item-mutation-keys' function markStateFamilyDirty(repoId: string, itemId: string): void { diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-filter-membership.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-filter-membership.ts similarity index 91% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-filter-membership.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-filter-membership.ts index a234297feed..da4522b0415 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-filter-membership.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-filter-membership.ts @@ -1,6 +1,6 @@ -import type { ParsedTaskQuery } from '../../../../../shared/work-items/task-query' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { ParsedTaskQuery } from '../../../../../../shared/work-items/task-query' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' import type { StickyHideEntry } from './task-page-github-work-item-registry-types' function includesLogin(users: readonly GitHubAssignableUser[] | undefined, login: string): boolean { diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-composition.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-composition.ts similarity index 94% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-composition.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-composition.ts index d35a96e0bc9..5dc0be181cc 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-composition.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-composition.ts @@ -1,8 +1,8 @@ -import { projectPRCheckOwner } from '../../../../../shared/github/checks/complete-pr-check-owner' -import type { ParsedTaskQuery } from '../../../../../shared/work-items/task-query' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' +import { projectPRCheckOwner } from '../../../../../../shared/github/checks/complete-pr-check-owner' +import type { ParsedTaskQuery } from '../../../../../../shared/work-items/task-query' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import { parseGitHubIssueOrPRLink } from '../../../../../../shared/github/repositories/links' import { recomputeTaskPageGitHubItemSoftHide, shouldSoftHideTaskPageGitHubWorkItem diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-keys.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-keys.ts similarity index 100% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-keys.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-keys.ts diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-lifecycle.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-lifecycle.ts similarity index 95% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-lifecycle.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-lifecycle.ts index 97342909657..199227b5411 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-lifecycle.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-lifecycle.ts @@ -1,6 +1,6 @@ -import type { ParsedTaskQuery } from '../../../../../shared/work-items/task-query' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import type { ParsedTaskQuery } from '../../../../../../shared/work-items/task-query' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' import { applyTaskPageGitHubListOps } from './task-page-github-work-item-mutation-patches' import { familiesFromPendingOp, @@ -19,7 +19,7 @@ import { setLastConfirmedClientValue, isTaskPageGitHubMutationQueryKeyCurrent } from './task-page-github-work-item-mutation-registry' -import { markTaskPageGitHubFamiliesDirty } from './task-page-github-work-item-quiet-state' +import { markTaskPageGitHubFamiliesDirty } from '../refresh/task-page-github-work-item-quiet-state' import { taskPageGitHubItemKey } from './task-page-github-work-item-mutation-keys' import type { TaskPageGitHubMutationKey } from './task-page-github-work-item-registry-types' import type { TaskPageGitHubPatchWorkItem } from './task-page-github-work-item-mutation-types' diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-pages.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-pages.ts similarity index 93% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-pages.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-pages.ts index 9e30d7a143b..37da8932dfe 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-pages.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-pages.ts @@ -1,7 +1,7 @@ -import { projectPRCheckOwner } from '../../../../../shared/github/checks/complete-pr-check-owner' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' -import { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' +import { projectPRCheckOwner } from '../../../../../../shared/github/checks/complete-pr-check-owner' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' +import { parseGitHubIssueOrPRLink } from '../../../../../../shared/github/repositories/links' import { getRegistryMergedTaskPageGitHubWorkItem } from './task-page-github-work-item-mutation-composition' import { getStickyHideEntry, diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-patches.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-patches.ts similarity index 95% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-patches.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-patches.ts index d3a1659df87..b2ef2397e98 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-patches.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-patches.ts @@ -1,6 +1,6 @@ -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskPageGitHubCloseAction } from './task-page-github-status-actions' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { TaskPageGitHubCloseAction } from '../issue-status/task-page-github-status-actions' import { taskPageGitHubListOpKey } from './task-page-github-work-item-mutation-keys' import type { PendingListOp, diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-registry.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-registry.ts similarity index 93% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-registry.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-registry.ts index 41472fa4635..7fbad1b3089 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-registry.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-registry.ts @@ -4,8 +4,8 @@ import { getLastConfirmedClientValue, setLastConfirmedClientValue as storeLastConfirmedClientValue } from './task-page-github-confirmed-client-values' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../shared/github/repositories/repository-identity' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../../shared/github/repositories/repository-identity' import type { PendingOp, StickyHideEntry, @@ -17,7 +17,7 @@ import { taskPageGitHubItemKey, taskPageGitHubSnapshotKey } from './task-page-github-work-item-mutation-keys' -import { clearTaskPageGitHubQuietStates } from './task-page-github-work-item-quiet-state' +import { clearTaskPageGitHubQuietStates } from '../refresh/task-page-github-work-item-quiet-state' type Listener = () => void const listeners = new Set() const pendingByKey = new Map() @@ -257,15 +257,3 @@ export function gcStickyHidesAbsentFromPages( } } } -export function resetTaskPageGitHubMutationRegistryForTests(): void { - pendingByKey.clear() - generations.clear() - confirmedSnapshots.clear() - clearLastConfirmedClientValues() - itemSourceScopeByItemKey.clear() - stickyHideByItemKey.clear() - softHiddenItemKeys.clear() - clearTaskPageGitHubQuietStates() - mutationQueryKey = null - listeners.clear() -} diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-types.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-types.ts similarity index 71% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-types.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-types.ts index 0d59ae7b9c4..e09ec3e4cb8 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-types.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-types.ts @@ -1,7 +1,7 @@ -import type { ParsedTaskQuery } from '../../../../../shared/work-items/task-query' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' -import type { GitHubPatchWorkItemOptions } from '../../../store/github/work-items/work-item-patch-options' +import type { ParsedTaskQuery } from '../../../../../../shared/work-items/task-query' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' +import type { GitHubPatchWorkItemOptions } from '../../../../store/github/work-items/work-item-patch-options' import type { TaskPageGitHubMutationIntent } from './task-page-github-work-item-mutation-patches' import type { TaskPageGitHubMutationKey } from './task-page-github-work-item-registry-types' diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutations.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutations.ts similarity index 88% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-mutations.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutations.ts index ff4f5ec992b..01f552912cc 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutations.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutations.ts @@ -1,8 +1,8 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' import { getTaskSourceCacheScope, type TaskSourceContext -} from '../../../../../shared/work-items/task-source-context' +} from '../../../../../../shared/work-items/task-source-context' import { buildTaskPageGitHubWorkItemMutationPatch, type TaskPageGitHubMutationIntent @@ -11,13 +11,11 @@ import { familiesFromPendingOp, getRegistryMergedTaskPageGitHubWorkItem, recomputeSoftHideForItem, - rebuildSoftHiddenKeysFromPendingAndSticky, stripFamilyPendingFromList } from './task-page-github-work-item-mutation-composition' import { getConfirmedListSnapshot, getPendingTaskPageGitHubOp, - getStickyHideEntry, listPendingTaskPageGitHubOpsForItem, nextTaskPageGitHubMutationGeneration, notifyTaskPageGitHubMutationRegistry, @@ -34,7 +32,6 @@ import type { BeginTaskPageGitHubWorkItemMutationArgs, BeginTaskPageGitHubWorkItemMutationResult } from './task-page-github-work-item-mutation-types' -import type { ParsedTaskQuery } from '../../../../../shared/work-items/task-query' function resolveSourceScope(sourceContext?: TaskSourceContext | null): string | null { if (sourceContext?.provider === 'github') { @@ -163,16 +160,3 @@ export function beginTaskPageGitHubWorkItemMutation( export function isTaskPageGitHubMutationPendingKey(key: TaskPageGitHubMutationKey): boolean { return getPendingTaskPageGitHubOp(key) !== undefined } - -export function getTaskPageGitHubStickyHideForTests(itemKey: string) { - return getStickyHideEntry(itemKey) -} - -export function rebuildSoftHiddenFromItemsForTests(args: { - query: ParsedTaskQuery - queryKey: string - viewerLogin: string | null - items: readonly GitHubWorkItem[] -}): void { - rebuildSoftHiddenKeysFromPendingAndSticky(args) -} diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-registry-types.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-registry-types.ts similarity index 78% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-registry-types.ts rename to src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-registry-types.ts index 26cbb52c7a7..292caf9ffda 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-registry-types.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-registry-types.ts @@ -1,5 +1,5 @@ -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' export type TaskPageGitHubListFamily = 'assignees' | 'reviewRequests' export type TaskPageGitHubMutationKey = { diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-mutation-state.ts b/src/renderer/src/components/task-page/github/mutations/use-task-page-github-mutation-state.ts similarity index 87% rename from src/renderer/src/components/task-page/github/use-task-page-github-mutation-state.ts rename to src/renderer/src/components/task-page/github/mutations/use-task-page-github-mutation-state.ts index d4310cf6cad..cd0a1f6e7a8 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-mutation-state.ts +++ b/src/renderer/src/components/task-page/github/mutations/use-task-page-github-mutation-state.ts @@ -1,18 +1,18 @@ -import type { TaskPageLinearCreationStateModel } from '../linear/use-task-page-linear-creation-state' -import { getGitHubTaskKind } from '../../../../../shared/work-items/task-github-search' +import type { TaskPageLinearCreationStateModel } from '../../linear/use-task-page-linear-creation-state' +import { getGitHubTaskKind } from '../../../../../../shared/work-items/task-github-search' import { useMemo, useLayoutEffect, useEffect, useCallback, useRef } from 'react' -import { parseTaskQuery } from '../../../../../shared/work-items/task-query' +import { parseTaskQuery } from '../../../../../../shared/work-items/task-query' import { setTaskPageGitHubMutationQueryKey, getTaskPageGitHubConfirmedAuthorityItemKeys -} from '@/components/task-page/github/task-page-github-work-item-mutation-registry' -import { getOrCreateQuietRevalidateState } from '@/components/task-page/github/task-page-github-work-item-quiet-state' -import type { TaskPageGitHubPatchWorkItem } from '@/components/task-page/github/task-page-github-work-item-mutation-types' +} from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-registry' +import { getOrCreateQuietRevalidateState } from '@/components/task-page/github/refresh/task-page-github-work-item-quiet-state' +import type { TaskPageGitHubPatchWorkItem } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-types' import { useAppStore } from '@/store/index' import { useTaskPageGitHubWorkItemMutation } from '@/hooks/useTaskPageGitHubWorkItemMutation' -import { rebuildSoftHiddenKeysFromPendingAndSticky } from '@/components/task-page/github/task-page-github-work-item-mutation-composition' -import { getTaskPageRepoSourceContext } from '../../../lib/work-items/task-repo-source-context' -import { useTaskPageSourceDisplay } from '../use-task-page-source-display' +import { rebuildSoftHiddenKeysFromPendingAndSticky } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-composition' +import { getTaskPageRepoSourceContext } from '../../../../lib/work-items/task-repo-source-context' +import { useTaskPageSourceDisplay } from '../../use-task-page-source-display' export function useTaskPageGitHubMutationState(model: TaskPageLinearCreationStateModel) { const { selectedRepos, diff --git a/src/renderer/src/components/task-page/github/pr-check-hydration.test.tsx b/src/renderer/src/components/task-page/github/pr-check-hydration.test.tsx deleted file mode 100644 index 7bc7faeedbb..00000000000 --- a/src/renderer/src/components/task-page/github/pr-check-hydration.test.tsx +++ /dev/null @@ -1,195 +0,0 @@ -import { Suspense, startTransition, useState } from 'react' -// @vitest-environment happy-dom -import { act, cleanup, render, renderHook, screen } from '@testing-library/react' -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskPageJiraCreationMetadataModel } from '../jira/use-task-page-jira-creation-metadata' -import { GITHUB_COMPLETE_PR_CHECK_OBSERVATION_CAPABILITY as capability } from '../../../../../shared/source-control/runtime/source-control-runtime-capabilities' -import { setLocalRuntimeCapabilitiesForTests } from '@/runtime/hosts/capabilities/local-runtime-capabilities' -import { useTaskPageGitHubListProjection } from './use-task-page-github-list-projection' - -const { lookup } = vi.hoisted(() => ({ lookup: vi.fn() })) -vi.mock('@/store', async () => { - const { create } = await import('zustand') - return { useAppStore: create(() => ({ runtimeStatusByEnvironmentId: new Map() })) } -}) -vi.mock('@/hooks/useGitHubTaskItems', () => ({ - useGitHubTaskItems: (items: GitHubWorkItem[]) => items -})) -vi.mock('./use-task-page-github-list-pending', () => ({ - useTaskPageGitHubListPending: () => false -})) -vi.mock('@/lib/work-items/github/github-work-item-source-lookup', () => ({ - lookupGitHubWorkItemByOwnerRepoForSource: lookup -})) -const repo = { - id: 'repo-1', - path: '/repo', - displayName: 'Repo', - badgeColor: '', - addedAt: 1, - kind: 'git' as const -} -const prRepo = { owner: 'acme', repo: 'widgets', host: 'github.com' } -function item(number: number): GitHubWorkItem { - return { - id: `pr-${number}`, - repoId: repo.id, - number, - type: 'pr', - title: 'PR', - state: 'open', - url: `https://github.com/acme/widgets/pull/${number}`, - labels: [], - author: null, - updatedAt: '2026-11-04', - headSha: 'head', - prRepo - } -} -function model(items: GitHubWorkItem[], patch = vi.fn()) { - const input = { - repos: [repo], - selectedRepos: [repo], - fetchPRChecks: vi.fn().mockResolvedValue([]), - taskSource: 'github', - githubMode: 'items', - tasksFiltering: false, - githubPageSize: 100, - pages: [items], - currentPage: 0, - countedTotalPages: 1, - provenPageLimit: 1, - patchTaskPageWorkItemRows: patch, - perRepoSourceState: [], - activeGithubTaskKind: 'prs', - githubWorkItemMutation: { softHiddenItemKeys: new Set() }, - selectedReposKey: 'repo', - taskRefreshNonce: 0, - workItemsInvalidationNonce: 0, - appliedTaskSearch: '', - paginationGenerationRef: { current: 1 } - } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the fixture supplies all fields the projection hook reads. - return input as unknown as TaskPageJiraCreationMetadataModel -} -beforeEach(() => { - lookup.mockReset() - setLocalRuntimeCapabilitiesForTests([capability]) -}) -afterEach(() => { - cleanup() - setLocalRuntimeCapabilitiesForTests(null) -}) - -it('publishes exact-owner counts, rejects a newer owner replacement, and retires source changes', async () => { - const row = item(42) - let current = row - const patch = vi.fn((_key, values, accepts: (item: GitHubWorkItem) => boolean) => { - if (accepts(current)) { - current = { ...current, ...values } - } - }) - const input = model([row], patch) - const read = Promise.withResolvers() - lookup.mockReturnValue(read.promise) - const view = renderHook(() => useTaskPageGitHubListProjection(input)) - const summary = { - state: 'failure' as const, - total: 101, - passed: 100, - failed: 1, - cancelled: 0, - pending: 0, - neutral: 0 - } - await act(async () => { - read.resolve({ ...row, checksSummary: summary }) - await read.promise - }) - expect(current.checksSummary).toEqual(summary) - // A late hydration read cannot replace a newer full observation on the same head. - input.taskRefreshNonce++ - input.paginationGenerationRef.current++ - const second = Promise.withResolvers() - lookup.mockReturnValue(second.promise) - view.rerender() - current = { ...row, checksSummary: { ...summary, failed: 2, total: 102 } } - await act(async () => { - second.resolve({ ...row, checksSummary: summary }) - await second.promise - }) - expect(current.checksSummary?.failed).toBe(2) - const third = Promise.withResolvers() - lookup.mockReturnValue(third.promise) - input.taskRefreshNonce++ - view.rerender() - view.unmount() - const previousCalls = patch.mock.calls.length - await act(async () => { - third.resolve({ ...row, checksSummary: summary }) - await third.promise - }) - expect(patch.mock.calls.length).toBe(previousCalls) -}) - -it.each([true, false])( - 'only a committed query replacement retires a pending read (suspend=%s)', - async (suspend) => { - const row = item(42) - let current = row - const patch = vi.fn((_key, values, accepts: (item: GitHubWorkItem) => boolean) => { - if (accepts(current)) { - current = { ...current, ...values } - } - }) - const original = Promise.withResolvers() - const replacement = Promise.withResolvers() - const blocked = Promise.withResolvers() - const summary = { - state: 'failure' as const, - total: 1, - passed: 0, - failed: 1, - cancelled: 0, - pending: 0, - neutral: 0 - } - lookup.mockReturnValueOnce(original.promise).mockReturnValueOnce(replacement.promise) - const first = model([row], patch) - const second = { ...first, appliedTaskSearch: 'replacement' } - let navigate!: (next: boolean) => void - function Consumer({ next }: { next: boolean }) { - useTaskPageGitHubListProjection(next ? second : first) - if (next && suspend) { - throw blocked.promise - } - return {next ? 'Replacement query' : 'Original query'} - } - function Harness() { - const [next, setNext] = useState(false) - navigate = setNext - return ( - Waiting}> - - - ) - } - const view = render() - await act(async () => { - startTransition(() => navigate(true)) - }) - expect(screen.getByText(suspend ? 'Original query' : 'Replacement query')).toBeTruthy() - await act(async () => { - original.resolve({ ...row, checksSummary: summary }) - await original.promise - }) - expect(current.checksSummary).toEqual(suspend ? summary : undefined) - view.unmount() - await act(async () => { - replacement.resolve({ ...row, checksSummary: summary }) - blocked.resolve() - await replacement.promise - }) - } -) diff --git a/src/renderer/src/components/task-page/github/ChecksCell.tsx b/src/renderer/src/components/task-page/github/pull-requests/ChecksCell.tsx similarity index 89% rename from src/renderer/src/components/task-page/github/ChecksCell.tsx rename to src/renderer/src/components/task-page/github/pull-requests/ChecksCell.tsx index 86b3b6cdb83..5e970953788 100644 --- a/src/renderer/src/components/task-page/github/ChecksCell.tsx +++ b/src/renderer/src/components/task-page/github/pull-requests/ChecksCell.tsx @@ -1,11 +1,11 @@ -import { getProviderChecksOutcome } from '../../../../../shared/source-control/checks/provider-check-summary' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import { getProviderChecksOutcome } from '../../../../../../shared/source-control/checks/provider-check-summary' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' import React, { useRef, useEffect } from 'react' import { useTranslate } from '@/i18n/use-translate' import { CheckCircle2, AlertCircle, Clock3, Minus } from 'lucide-react' import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' import { cn } from '@/lib/class-names' -import { getChecksPillTone, getChecksLabel } from '../task-page-checks-pill' +import { getChecksPillTone, getChecksLabel } from '../../task-page-checks-pill' export function PRChecksCell({ item, onOpen, diff --git a/src/renderer/src/components/task-page/github/MergeCell.tsx b/src/renderer/src/components/task-page/github/pull-requests/MergeCell.tsx similarity index 93% rename from src/renderer/src/components/task-page/github/MergeCell.tsx rename to src/renderer/src/components/task-page/github/pull-requests/MergeCell.tsx index 90adc647649..66d3830ecff 100644 --- a/src/renderer/src/components/task-page/github/MergeCell.tsx +++ b/src/renderer/src/components/task-page/github/pull-requests/MergeCell.tsx @@ -1,7 +1,7 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { GitHubPRMergeMethod } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { Repo } from '../../../../../shared/repo-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { GitHubPRMergeMethod } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' import React, { useState } from 'react' import { useConfirmationDialog } from '@/components/confirmation-dialog-context' import { useAppStore } from '@/store/index' @@ -13,7 +13,7 @@ import { getPullRequestMergeMethodLabel } from '@/components/github/pull-request-confirmation-copy' import { presentGitHubPRMergeState } from '@/components/github-pr-merge-state' -import { resolveGitHubPRMergeMethods } from '../../../../../shared/github/pull-requests/merge/pull-request-merge-methods' +import { resolveGitHubPRMergeMethods } from '../../../../../../shared/github/pull-requests/merge/pull-request-merge-methods' import { callRuntimeRpc } from '@/runtime/rpc/calls/runtime-rpc-call' import { DropdownMenu, @@ -25,7 +25,7 @@ import { import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' import { cn } from '@/lib/class-names' import { LoaderCircle, GitMerge, ChevronDown, ExternalLink } from 'lucide-react' -import type { TaskPageGitHubWorkItemMutationRunner } from '../linear/task-page-linear-jira-list-model' +import type { TaskPageGitHubWorkItemMutationRunner } from '../../linear/task-page-linear-jira-list-model' import { resolveTaskPullRequestRepo } from './task-page-github-review-model' import { openAppHttpLink } from '@/lib/browser/navigation/app-http-link-open' export function PRMergeCell({ diff --git a/src/renderer/src/components/task-page/github/ReviewCell.tsx b/src/renderer/src/components/task-page/github/pull-requests/ReviewCell.tsx similarity index 95% rename from src/renderer/src/components/task-page/github/ReviewCell.tsx rename to src/renderer/src/components/task-page/github/pull-requests/ReviewCell.tsx index 210f99b7647..175ce368376 100644 --- a/src/renderer/src/components/task-page/github/ReviewCell.tsx +++ b/src/renderer/src/components/task-page/github/pull-requests/ReviewCell.tsx @@ -1,7 +1,7 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { Repo } from '../../../../../shared/repo-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' import React, { useState, useMemo, useRef, useCallback } from 'react' import { useAppStore } from '@/store/index' import { useShallow } from 'zustand/react/shallow' @@ -16,16 +16,16 @@ import { getGitHubPRPrimaryReviewer, getGitHubPRReviewerRows, getGitHubPRReviewLabel -} from '../../../../../shared/github/pull-requests/pr-reviewer-display' +} from '../../../../../../shared/github/pull-requests/pr-reviewer-display' import { cn } from '@/lib/class-names' import { ChevronDown } from 'lucide-react' import { Popover, PopoverTrigger } from '@/components/ui/popover' -import type { TaskPageGitHubWorkItemMutationRunner } from '../linear/task-page-linear-jira-list-model' +import type { TaskPageGitHubWorkItemMutationRunner } from '../../linear/task-page-linear-jira-list-model' import { mergeReviewerSuggestions, resolveTaskPullRequestRepo } from './task-page-github-review-model' -import { ReviewChipAvatar } from './Avatars' +import { ReviewChipAvatar } from '../listing/Avatars' import { createTaskPageGitHubReviewerActions } from './task-page-github-reviewer-actions' import { TaskPageGitHubReviewerPicker } from './ReviewerPicker' export function PRReviewCell({ diff --git a/src/renderer/src/components/task-page/github/ReviewerPicker.tsx b/src/renderer/src/components/task-page/github/pull-requests/ReviewerPicker.tsx similarity index 97% rename from src/renderer/src/components/task-page/github/ReviewerPicker.tsx rename to src/renderer/src/components/task-page/github/pull-requests/ReviewerPicker.tsx index 1517f28a104..fd256c94948 100644 --- a/src/renderer/src/components/task-page/github/ReviewerPicker.tsx +++ b/src/renderer/src/components/task-page/github/pull-requests/ReviewerPicker.tsx @@ -6,8 +6,8 @@ import { cn } from '@/lib/class-names' import { Input } from '@/components/ui/input' import { PopoverContent } from '@/components/ui/popover' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { Repo } from '../../../../../shared/repo-types' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { Repo } from '../../../../../../shared/repo-types' import { GitHubUserAvatar } from '@/components/github/github-user-avatar' type ReviewerPickerProps = { diff --git a/src/renderer/src/components/task-page/github/task-page-github-review-model.tsx b/src/renderer/src/components/task-page/github/pull-requests/task-page-github-review-model.tsx similarity index 83% rename from src/renderer/src/components/task-page/github/task-page-github-review-model.tsx rename to src/renderer/src/components/task-page/github/pull-requests/task-page-github-review-model.tsx index 1b86933b814..7c5b8851f06 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-review-model.tsx +++ b/src/renderer/src/components/task-page/github/pull-requests/task-page-github-review-model.tsx @@ -1,9 +1,9 @@ -import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../shared/github/repositories/repository-identity' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' +import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../../shared/github/repositories/repository-identity' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' import { sameGitHubOwnerRepo } from '@/components/github/IssueSourceIndicator' -import { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' -import { githubProjectHost } from '../../../../../shared/github/projects/project-identity' +import { parseGitHubIssueOrPRLink } from '../../../../../../shared/github/repositories/links' +import { githubProjectHost } from '../../../../../../shared/github/projects/project-identity' export function sameOptionalGitHubOwnerRepo( left: GitHubOwnerRepo | null | undefined, right: GitHubOwnerRepo | null | undefined diff --git a/src/renderer/src/components/task-page/github/task-page-github-reviewer-actions.ts b/src/renderer/src/components/task-page/github/pull-requests/task-page-github-reviewer-actions.ts similarity index 90% rename from src/renderer/src/components/task-page/github/task-page-github-reviewer-actions.ts rename to src/renderer/src/components/task-page/github/pull-requests/task-page-github-reviewer-actions.ts index 7a12f17a108..46bf6f0376c 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-reviewer-actions.ts +++ b/src/renderer/src/components/task-page/github/pull-requests/task-page-github-reviewer-actions.ts @@ -2,20 +2,20 @@ import type { Dispatch, SetStateAction } from 'react' import { translate } from '@/i18n/i18n' import { callRuntimeRpc } from '@/runtime/rpc/calls/runtime-rpc-call' -import type { RuntimeClientTarget } from '../../../../../shared/runtime-client-target' -import type { TaskPageGitHubMutationIntent } from '@/components/task-page/github/task-page-github-work-item-mutation-patches' +import type { RuntimeClientTarget } from '../../../../../../shared/runtime-client-target' +import type { TaskPageGitHubMutationIntent } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-patches' import { normalizeGitHubReviewerLogins, parseGitHubReviewerInputLogins -} from '../../../../../shared/github/pull-requests/pr-reviewer-display' +} from '../../../../../../shared/github/pull-requests/pr-reviewer-display' import { toast } from 'sonner' -import type { GitHubAssignableUser } from '../../../../../shared/github/pull-requests/pull-request-types' -import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../shared/github/repositories/repository-identity' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { Repo } from '../../../../../shared/repo-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' -import type { TaskPageGitHubWorkItemMutationRunner } from '../linear/task-page-linear-jira-list-model' +import type { GitHubAssignableUser } from '../../../../../../shared/github/pull-requests/pull-request-types' +import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../../../shared/github/repositories/repository-identity' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' +import type { TaskPageGitHubWorkItemMutationRunner } from '../../linear/task-page-linear-jira-list-model' import { buildRequestedReviewUsers } from './task-page-github-review-model' type ReviewerActionsInput = { diff --git a/src/renderer/src/components/task-page/github/use-task-page-pr-check-hydration.ts b/src/renderer/src/components/task-page/github/pull-requests/use-task-page-pr-check-hydration.ts similarity index 93% rename from src/renderer/src/components/task-page/github/use-task-page-pr-check-hydration.ts rename to src/renderer/src/components/task-page/github/pull-requests/use-task-page-pr-check-hydration.ts index 5db2fe96aac..ce313a40bb7 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-pr-check-hydration.ts +++ b/src/renderer/src/components/task-page/github/pull-requests/use-task-page-pr-check-hydration.ts @@ -10,7 +10,7 @@ import { readLocalRuntimeCapabilityObservation, subscribeLocalRuntimeCapabilityObservation } from '@/runtime/hosts/capabilities/local-runtime-capabilities' -import { useAppStore } from '@/store' +import { useAppStore } from '@/store/index' import { capturePRCheckContract, type PRCheckContract @@ -22,13 +22,13 @@ import { acceptPRCheckObservation, projectPRCheckOwner, completePRCheckOwnerScope -} from '../../../../../shared/github/checks/complete-pr-check-owner' +} from '../../../../../../shared/github/checks/complete-pr-check-owner' import { getWorkItemRepo } from '@/lib/work-items/work-item-repo-row' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskPageJiraCreationMetadataModel } from '../jira/use-task-page-jira-creation-metadata' -import { deriveTaskPagePRCheckSummary } from '../task-page-pr-check-summary' -import { PR_CHECKS_EAGER_PREFETCH_LIMIT } from '../task-page-source-context' -import { getTaskPageRepoSourceContext } from '../../../lib/work-items/task-repo-source-context' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { TaskPageJiraCreationMetadataModel } from '../../jira/use-task-page-jira-creation-metadata' +import { deriveTaskPagePRCheckSummary } from '../../task-page-pr-check-summary' +import { PR_CHECKS_EAGER_PREFETCH_LIMIT } from '../../task-page-source-context' +import { getTaskPageRepoSourceContext } from '../../../../lib/work-items/task-repo-source-context' import { sameOptionalGitHubOwnerRepo } from './task-page-github-review-model' import { recomputeOn } from '@/lib/recompute-on' export function useTaskPagePRCheckHydration( diff --git a/src/renderer/src/components/task-page/github/task-page-github-landing-refresh-model.ts b/src/renderer/src/components/task-page/github/refresh/task-page-github-landing-refresh-model.ts similarity index 92% rename from src/renderer/src/components/task-page/github/task-page-github-landing-refresh-model.ts rename to src/renderer/src/components/task-page/github/refresh/task-page-github-landing-refresh-model.ts index 2bc528e952a..8c184e8af62 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-landing-refresh-model.ts +++ b/src/renderer/src/components/task-page/github/refresh/task-page-github-landing-refresh-model.ts @@ -1,4 +1,4 @@ -import type { TaskPageGitHubSearchPaginationModel } from './use-task-page-github-search-pagination' +import type { TaskPageGitHubSearchPaginationModel } from '../listing/use-task-page-github-search-pagination' /** The parts of the Tasks page model a landing refresh reads and drives. */ export type TaskPageGitHubLandingRefreshModel = Pick< diff --git a/src/renderer/src/components/task-page/github/task-page-github-landing-refresh-run.tsx b/src/renderer/src/components/task-page/github/refresh/task-page-github-landing-refresh-run.tsx similarity index 93% rename from src/renderer/src/components/task-page/github/task-page-github-landing-refresh-run.tsx rename to src/renderer/src/components/task-page/github/refresh/task-page-github-landing-refresh-run.tsx index 4119be279bf..00fa8240564 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-landing-refresh-run.tsx +++ b/src/renderer/src/components/task-page/github/refresh/task-page-github-landing-refresh-run.tsx @@ -1,22 +1,22 @@ import type { TaskPageGitHubLandingRefreshModel } from './task-page-github-landing-refresh-model' -import { stripRepoQualifiers } from '../../../../../shared/work-items/task-query' +import { stripRepoQualifiers } from '../../../../../../shared/work-items/task-query' import { useAppStore } from '@/store/index' import { taskPageGitHubResumeCache, TASK_PAGE_GITHUB_RESUME_FRESH_MS -} from '@/components/task-page/github/task-page-github-resume-cache' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import { getTaskPageRepoSourceContext } from '../../../lib/work-items/task-repo-source-context' -import { sortWorkItemsByNumber } from '../../../../../shared/work-items/work-item-order' -import { taskPageToGitHubApiPage } from '../../../../../shared/work-items/work-item-pagination' +} from '@/components/task-page/github/cache/task-page-github-resume-cache' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import { getTaskPageRepoSourceContext } from '../../../../lib/work-items/task-repo-source-context' +import { sortWorkItemsByNumber } from '../../../../../../shared/work-items/work-item-order' +import { taskPageToGitHubApiPage } from '../../../../../../shared/work-items/work-item-pagination' import { materializeTaskPageItemList, overlayPendingOnTaskPagePages, reapplyPendingTaskPageGitHubMutationsToCache -} from '@/components/task-page/github/task-page-github-work-item-mutation-pages' -import { clearTaskPageGitHubAuthorityThroughGeneration } from '@/components/task-page/github/task-page-github-work-item-authority-refresh' -import { getOrCreateQuietRevalidateState } from '@/components/task-page/github/task-page-github-work-item-quiet-state' -import { resolveEmptyPageOutcome } from '../../../../../shared/work-items/work-item-empty-page' +} from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-pages' +import { clearTaskPageGitHubAuthorityThroughGeneration } from '@/components/task-page/github/refresh/task-page-github-work-item-authority-refresh' +import { getOrCreateQuietRevalidateState } from '@/components/task-page/github/refresh/task-page-github-work-item-quiet-state' +import { resolveEmptyPageOutcome } from '../../../../../../shared/work-items/work-item-empty-page' import { deriveTaskPageGitHubWorkItemsFetchOptions, shouldReplaceTaskPageItemsAfterRefresh, @@ -24,7 +24,7 @@ import { reconcileTaskPagePagesAfterLandingRefresh } from '@/components/task-page/task-page-cache-selectors' import { translate } from '@/i18n/i18n' -import { taskPageGitHubLandingRequestKey } from './use-task-page-github-list-pending' +import { taskPageGitHubLandingRequestKey } from '../listing/use-task-page-github-list-pending' export function runTaskPageGitHubLandingRefresh(model: TaskPageGitHubLandingRefreshModel) { const { fetchWorkItemsAcrossRepos, diff --git a/src/renderer/src/components/task-page/github/task-page-github-quiet-refresh-run.tsx b/src/renderer/src/components/task-page/github/refresh/task-page-github-quiet-refresh-run.tsx similarity index 92% rename from src/renderer/src/components/task-page/github/task-page-github-quiet-refresh-run.tsx rename to src/renderer/src/components/task-page/github/refresh/task-page-github-quiet-refresh-run.tsx index afd6b0e815b..18065a15548 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-quiet-refresh-run.tsx +++ b/src/renderer/src/components/task-page/github/refresh/task-page-github-quiet-refresh-run.tsx @@ -3,32 +3,32 @@ import { getOrCreateQuietRevalidateState, beginTaskPageQuietRevalidateRun, finishTaskPageQuietRevalidateRun -} from '@/components/task-page/github/task-page-github-work-item-quiet-state' -import { taskPageGitHubItemKey } from '@/components/task-page/github/task-page-github-work-item-mutation-keys' -import { getTaskPageGitHubConfirmedAuthorityItemKeys } from '@/components/task-page/github/task-page-github-work-item-mutation-registry' +} from '@/components/task-page/github/refresh/task-page-github-work-item-quiet-state' +import { taskPageGitHubItemKey } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-keys' +import { getTaskPageGitHubConfirmedAuthorityItemKeys } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-registry' import { clearTaskPageGitHubAuthorityAbsentFromLoadedItems, getTaskPageGitHubRevalidatableAuthorityItemKeys -} from '@/components/task-page/github/task-page-github-work-item-authority-refresh' +} from '@/components/task-page/github/refresh/task-page-github-work-item-authority-refresh' import { isTaskPageQuietRevalidateScopeCurrent, isTaskPageQuietRevalidateRunCurrent, processTaskPageQuietRevalidateSettle, getTaskPageQuietRevalidateBackoffAttempt -} from '@/components/task-page/github/task-page-github-work-item-quiet-revalidate' +} from '@/components/task-page/github/refresh/task-page-github-work-item-quiet-revalidate' import { reapplyPendingTaskPageGitHubMutationsToCache, reconcileTaskPagePagesAfterQuietRefresh -} from '@/components/task-page/github/task-page-github-work-item-mutation-pages' +} from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-pages' import { LAG_WALL_BUDGET_MS, MAX_LAG_TRAILS, LAG_BACKOFF_MS -} from '@/components/task-page/github/task-page-github-work-item-quiet-adopt' -import { stripRepoQualifiers } from '../../../../../shared/work-items/task-query' -import { getTaskPageRepoSourceContext } from '../../../lib/work-items/task-repo-source-context' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import { taskPageToGitHubApiPage } from '../../../../../shared/work-items/work-item-pagination' +} from '@/components/task-page/github/refresh/task-page-github-work-item-quiet-adopt' +import { stripRepoQualifiers } from '../../../../../../shared/work-items/task-query' +import { getTaskPageRepoSourceContext } from '../../../../lib/work-items/task-repo-source-context' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import { taskPageToGitHubApiPage } from '../../../../../../shared/work-items/work-item-pagination' import { useAppStore } from '@/store/index' export function runTaskPageGitHubQuietRefresh(model: TaskPageGitHubQuietRefreshPreludeModel) { const { diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-authority-refresh.ts b/src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-authority-refresh.ts similarity index 94% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-authority-refresh.ts rename to src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-authority-refresh.ts index fcde70a7fe9..24223196de1 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-authority-refresh.ts +++ b/src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-authority-refresh.ts @@ -1,4 +1,4 @@ -import { taskPageGitHubFamilyDirtyKey } from './task-page-github-work-item-mutation-keys' +import { taskPageGitHubFamilyDirtyKey } from '../mutations/task-page-github-work-item-mutation-keys' import { clearConfirmedAuthorityForItem, deleteConfirmedListSnapshot, @@ -11,11 +11,11 @@ import { notifyTaskPageGitHubMutationRegistry, resolveItemSourceScope, updateSoftHiddenItemKey -} from './task-page-github-work-item-mutation-registry' +} from '../mutations/task-page-github-work-item-mutation-registry' import { deleteLastConfirmedClientValue, getLastConfirmedClientValue -} from './task-page-github-confirmed-client-values' +} from '../mutations/task-page-github-confirmed-client-values' import { getQuietRevalidateState } from './task-page-github-work-item-quiet-state' import { MAX_LAG_TRAILS } from './task-page-github-work-item-quiet-adopt' diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-quiet-adopt.ts b/src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-quiet-adopt.ts similarity index 87% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-quiet-adopt.ts rename to src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-quiet-adopt.ts index 418beba587f..8b21d57f6eb 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-quiet-adopt.ts +++ b/src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-quiet-adopt.ts @@ -1,26 +1,29 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' -import { parseGitHubIssueOrPRLink } from '../../../../../shared/github/repositories/links' -import { loginSetOfUsers, loginSetsEqual } from './task-page-github-work-item-mutation-patches' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' +import { parseGitHubIssueOrPRLink } from '../../../../../../shared/github/repositories/links' +import { + loginSetOfUsers, + loginSetsEqual +} from '../mutations/task-page-github-work-item-mutation-patches' import { familiesFromPendingOp, freezeTaskPageGitHubUsers -} from './task-page-github-work-item-mutation-composition' +} from '../mutations/task-page-github-work-item-mutation-composition' import { getConfirmedListSnapshot, listPendingTaskPageGitHubOpsForItem, deleteConfirmedListSnapshot -} from './task-page-github-work-item-mutation-registry' +} from '../mutations/task-page-github-work-item-mutation-registry' import { getLastConfirmedClientValue, deleteLastConfirmedClientValue -} from './task-page-github-confirmed-client-values' +} from '../mutations/task-page-github-confirmed-client-values' import { getOrCreateQuietRevalidateState } from './task-page-github-work-item-quiet-state' import { taskPageGitHubFamilyDirtyKey, taskPageGitHubItemKey -} from './task-page-github-work-item-mutation-keys' -import type { TaskPageGitHubPatchWorkItem } from './task-page-github-work-item-mutation-types' +} from '../mutations/task-page-github-work-item-mutation-keys' +import type { TaskPageGitHubPatchWorkItem } from '../mutations/task-page-github-work-item-mutation-types' export const MAX_LAG_TRAILS = 5 export const LAG_WALL_BUDGET_MS = 90_000 diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-quiet-revalidate.ts b/src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-quiet-revalidate.ts similarity index 92% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-quiet-revalidate.ts rename to src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-quiet-revalidate.ts index b25c4d16daf..23896a8a183 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-quiet-revalidate.ts +++ b/src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-quiet-revalidate.ts @@ -1,5 +1,5 @@ -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' import { adoptQuietSearchFieldsForItem, MAX_LAG_TRAILS @@ -12,10 +12,10 @@ import { hasPendingTaskPageGitHubOpsForItem, notifyTaskPageGitHubMutationRegistry, resolveItemSourceScope -} from './task-page-github-work-item-mutation-registry' +} from '../mutations/task-page-github-work-item-mutation-registry' import { getOrCreateQuietRevalidateState } from './task-page-github-work-item-quiet-state' -import { taskPageGitHubItemKey } from './task-page-github-work-item-mutation-keys' -import type { TaskPageGitHubPatchWorkItem } from './task-page-github-work-item-mutation-types' +import { taskPageGitHubItemKey } from '../mutations/task-page-github-work-item-mutation-keys' +import type { TaskPageGitHubPatchWorkItem } from '../mutations/task-page-github-work-item-mutation-types' export type TaskPageQuietRevalidateScope = { queryKey: string; generation: number } diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-quiet-state.ts b/src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-quiet-state.ts similarity index 96% rename from src/renderer/src/components/task-page/github/task-page-github-work-item-quiet-state.ts rename to src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-quiet-state.ts index 2d9891020b5..49c9fed0347 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-quiet-state.ts +++ b/src/renderer/src/components/task-page/github/refresh/task-page-github-work-item-quiet-state.ts @@ -1,4 +1,4 @@ -import { taskPageGitHubFamilyDirtyKey } from './task-page-github-work-item-mutation-keys' +import { taskPageGitHubFamilyDirtyKey } from '../mutations/task-page-github-work-item-mutation-keys' /** Identity token for the caller driving one quiet run; compared by reference, never read. */ export type QuietRevalidateRunOwner = Record diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-landing-refresh.ts b/src/renderer/src/components/task-page/github/refresh/use-task-page-github-landing-refresh.ts similarity index 90% rename from src/renderer/src/components/task-page/github/use-task-page-github-landing-refresh.ts rename to src/renderer/src/components/task-page/github/refresh/use-task-page-github-landing-refresh.ts index ea8c4cbffd4..238bd8ec870 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-landing-refresh.ts +++ b/src/renderer/src/components/task-page/github/refresh/use-task-page-github-landing-refresh.ts @@ -1,4 +1,4 @@ -import type { TaskPageGitHubSearchPaginationModel } from './use-task-page-github-search-pagination' +import type { TaskPageGitHubSearchPaginationModel } from '../listing/use-task-page-github-search-pagination' import { useEffect } from 'react' import { runTaskPageGitHubLandingRefresh } from './task-page-github-landing-refresh-run' export function useTaskPageGitHubLandingRefresh(model: TaskPageGitHubSearchPaginationModel) { diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-quiet-refresh-effect.ts b/src/renderer/src/components/task-page/github/refresh/use-task-page-github-quiet-refresh-effect.ts similarity index 100% rename from src/renderer/src/components/task-page/github/use-task-page-github-quiet-refresh-effect.ts rename to src/renderer/src/components/task-page/github/refresh/use-task-page-github-quiet-refresh-effect.ts diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-quiet-refresh.ts b/src/renderer/src/components/task-page/github/refresh/use-task-page-github-quiet-refresh.ts similarity index 94% rename from src/renderer/src/components/task-page/github/use-task-page-github-quiet-refresh.ts rename to src/renderer/src/components/task-page/github/refresh/use-task-page-github-quiet-refresh.ts index 732e11642e5..d9ae71b01f4 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-quiet-refresh.ts +++ b/src/renderer/src/components/task-page/github/refresh/use-task-page-github-quiet-refresh.ts @@ -1,8 +1,8 @@ import type { TaskPageGitHubLandingRefreshModel } from './use-task-page-github-landing-refresh' import { useMountedRef } from '@/hooks/useMountedRef' import { useRef } from 'react' -import type { QuietRevalidateRunOwner } from '@/components/task-page/github/task-page-github-work-item-quiet-state' -import { advanceTaskPageQuietRevalidateScope } from '@/components/task-page/github/task-page-github-work-item-quiet-revalidate' +import type { QuietRevalidateRunOwner } from '@/components/task-page/github/refresh/task-page-github-work-item-quiet-state' +import { advanceTaskPageQuietRevalidateScope } from '@/components/task-page/github/refresh/task-page-github-work-item-quiet-revalidate' import { useTaskPageGitHubQuietRefreshEffect } from './use-task-page-github-quiet-refresh-effect' export type TaskPageGitHubQuietRefreshPreludeModel = ReturnType< typeof useTaskPageGitHubQuietRefreshPrelude diff --git a/src/renderer/src/components/task-page/github/task-page-github-list-scroll-restore.test.ts b/src/renderer/src/components/task-page/github/task-page-github-list-scroll-restore.test.ts deleted file mode 100644 index 1985bd0e804..00000000000 --- a/src/renderer/src/components/task-page/github/task-page-github-list-scroll-restore.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -// @vitest-environment happy-dom - -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' - -import { - startGitHubListScrollRestore, - supersedeGitHubListScrollRestore, - type GitHubListRestoreWrite -} from './task-page-github-list-scroll-restore' - -type FakeResizeObserver = { - targets: Set - notify: () => void -} - -const observers: FakeResizeObserver[] = [] - -/** Notifies every observer watching `target`, mirroring a real content/box resize. */ -function resize(target: Element): void { - for (const observer of observers) { - if (observer.targets.has(target)) { - observer.notify() - } - } -} - -/** Scroll container whose scrollTop clamps to `maxScrollTop`, like a real overflow box. */ -function createScrollList(maxScrollTop: number): { - element: HTMLDivElement - rows: HTMLDivElement - setMaxScrollTop: (next: number) => void -} { - const element = document.createElement('div') - const rows = document.createElement('div') - element.append(rows) - let max = maxScrollTop - let value = 0 - Object.defineProperty(element, 'scrollTop', { - configurable: true, - get: () => value, - set: (next: number) => { - value = Math.max(0, Math.min(next, max)) - } - }) - return { - element, - rows, - setMaxScrollTop: (next: number) => { - max = next - value = Math.min(value, next) - } - } -} - -function ref(current: T): { current: T } { - return { current } -} - -describe('GitHub task list scroll restore', () => { - beforeEach(() => { - vi.useFakeTimers() - observers.length = 0 - vi.stubGlobal( - 'ResizeObserver', - class { - private readonly entry: FakeResizeObserver - constructor(callback: () => void) { - this.entry = { targets: new Set(), notify: callback } - observers.push(this.entry) - } - observe(target: Element): void { - this.entry.targets.add(target) - } - disconnect(): void { - this.entry.targets.clear() - } - } - ) - }) - afterEach(() => { - vi.useRealTimers() - vi.unstubAllGlobals() - }) - - describe('user takeover', () => { - it('ends a restore the list can never satisfy when the user scrolls', () => { - const list = createScrollList(0) - const pendingRestoreRef = ref(360) - const restoreWriteRef = ref(null) - - startGitHubListScrollRestore({ - target: 360, - scrollElementRef: ref(list.element), - pendingRestoreRef, - restoreWriteRef, - onScrollTopApplied: () => {} - }) - - list.setMaxScrollTop(900) - list.element.scrollTop = 120 - expect( - supersedeGitHubListScrollRestore({ scrollTop: 120, pendingRestoreRef, restoreWriteRef }) - ).toBe(true) - expect(pendingRestoreRef.current).toBeNull() - expect(restoreWriteRef.current).toBeNull() - - // Without the takeover the next resize would drag the user back to the old offset. - resize(list.rows) - expect(list.element.scrollTop).toBe(120) - }) - }) -}) diff --git a/src/renderer/src/components/task-page/github/use-task-page-github-detail.ts b/src/renderer/src/components/task-page/github/use-task-page-github-detail.ts index da60f83304a..64e273c77ce 100644 --- a/src/renderer/src/components/task-page/github/use-task-page-github-detail.ts +++ b/src/renderer/src/components/task-page/github/use-task-page-github-detail.ts @@ -1,4 +1,4 @@ -import type { TaskPageGitHubListStateModel } from './use-task-page-github-list-state' +import type { TaskPageGitHubListStateModel } from './listing/use-task-page-github-list-state' import { useAppStore } from '@/store/index' import { getWorkItemRepo } from '@/lib/work-items/work-item-repo-row' import { githubTaskBinding } from '@/lib/work-items/github/github-task-observation' @@ -6,11 +6,11 @@ import { useState, useMemo, useLayoutEffect, useCallback, useEffect } from 'reac import type { ItemDialogTab } from '@/components/github/github-work-item-identity' import { stripRepoQualifiers } from '../../../../../shared/work-items/task-query' import { createTaskPageDialogWorkItemReader } from '@/components/task-page/task-page-dialog-work-item' -import { useTaskPageWorkItemsCacheSelection } from './use-task-page-work-items-cache-selection' +import { useTaskPageWorkItemsCacheSelection } from './cache/use-task-page-work-items-cache-selection' import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' import type { GitLabWorkItem } from '../../../../../shared/gitlab-types' import { getTaskPageRepoSourceContext } from '../../../lib/work-items/task-repo-source-context' -import { startGitHubListScrollRestore } from './task-page-github-list-scroll-restore' +import { startGitHubListScrollRestore } from './listing/task-page-github-list-scroll-restore' function getTaskPageScrollTop( scrollRef: React.RefObject, diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts b/src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts index 17173609a47..f7075dc9606 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts +++ b/src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts @@ -1,4 +1,4 @@ -import type { TaskPageGitHubMutationStateModel } from '../github/use-task-page-github-mutation-state' +import type { TaskPageGitHubMutationStateModel } from '../github/mutations/use-task-page-github-mutation-state' import { useState, useRef, useEffect } from 'react' import type { JiraIssueType, JiraCreateField } from '../../../../../shared/jira/jira-types' import { useTaskCreationDraftRetention } from '@/components/use-task-creation-draft-retention' diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-jira-list-model.tsx b/src/renderer/src/components/task-page/linear/task-page-linear-jira-list-model.tsx index 45d7e9d57d0..9101632e7c9 100644 --- a/src/renderer/src/components/task-page/linear/task-page-linear-jira-list-model.tsx +++ b/src/renderer/src/components/task-page/linear/task-page-linear-jira-list-model.tsx @@ -13,7 +13,7 @@ import React from 'react' import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@/components/ui/collapsible' import { AlertCircle, ChevronDown, ChevronRight } from 'lucide-react' import { Button } from '@/components/ui/button' -import type { TaskPageGitHubMutationIntent } from '@/components/task-page/github/task-page-github-work-item-mutation-patches' +import type { TaskPageGitHubMutationIntent } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-patches' import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' import type { LinearGroupSection } from './task-page-linear-issue-model' import { compareNumericLocaleText } from '../../../../../shared/locale-text-collators' diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-project-creation.ts b/src/renderer/src/components/task-page/linear/use-task-page-linear-project-creation.ts index e1b353e3ceb..d3fe6b4509b 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-project-creation.ts +++ b/src/renderer/src/components/task-page/linear/use-task-page-linear-project-creation.ts @@ -2,7 +2,7 @@ import { taskProviderErrorMessage, taskProviderErrorText } from '@/lib/work-items/task-provider-error-copy' -import type { TaskPageGitHubIssueCreationModel } from '../github/use-task-page-github-issue-creation' +import type { TaskPageGitHubIssueCreationModel } from '../github/issue-creation/use-task-page-github-issue-creation' import { useCallback } from 'react' import { linearCreateProject } from '@/runtime/linear/runtime-linear-project-client' import { toast } from 'sonner' diff --git a/src/renderer/src/components/task-page/task-page-github-pr-chips.test.tsx b/src/renderer/src/components/task-page/task-page-github-pr-chips.test.tsx deleted file mode 100644 index eea0382c4b9..00000000000 --- a/src/renderer/src/components/task-page/task-page-github-pr-chips.test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -// @vitest-environment happy-dom -import '@testing-library/jest-dom/vitest' -import { act, fireEvent, render, screen } from '@testing-library/react' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { useAppStore } from '@/store' -import { GITHUB_TASK_SEARCH_IDLE_MS } from '@/components/use-github-task-search-commit' -import { createGlobalSettingsFixture } from '../../../../shared/global-settings-test-fixture' -import { - Harness, - installSourceDisplayTestLifecycle, - repo, - view -} from './use-task-page-source-display.test-fixture' - -vi.mock('@/components/github/PRFilterDropdowns', () => ({ default: () => null })) - -installSourceDisplayTestLifecycle() - -afterEach(() => { - vi.useRealTimers() -}) - -function chip(name: string) { - return screen.getByRole('button', { name }) -} - -describe('GitHub PR state × who chips', () => { - it('saves Open × Mine as the Mine preset after the persist effect and idle commit', () => { - vi.useFakeTimers() - useAppStore.setState({ - repos: [repo], - settings: createGlobalSettingsFixture({ defaultRepoSelection: [repo.id] }), - taskPageData: { taskSource: 'github' } - }) - render() - act(() => { - view.setTaskSearchInput('is:pr is:merged') - view.setAppliedTaskSearch('is:pr is:merged') - view.setTaskResumeApplied(true) - }) - expect(chip('Merged')).toHaveAttribute('aria-pressed', 'true') - - fireEvent.click(chip('Open')) - fireEvent.click(chip('Mine')) - act(() => { - vi.advanceTimersByTime(GITHUB_TASK_SEARCH_IDLE_MS + 1) - }) - - expect(useAppStore.getState().taskResumeState).toMatchObject({ - githubItemsPreset: 'my-prs', - githubItemsQuery: 'author:@me is:pr is:open' - }) - expect(view.taskSearchInput).toBe('author:@me is:pr is:open') - expect(chip('Open')).toHaveAttribute('aria-pressed', 'true') - expect(chip('Mine')).toHaveAttribute('aria-pressed', 'true') - - fireEvent.click(chip('Merged')) - expect(useAppStore.getState().taskResumeState).toMatchObject({ - githubItemsPreset: null, - githubItemsQuery: 'is:pr is:merged author:@me' - }) - }) -}) diff --git a/src/renderer/src/components/task-page/task-page-localized-options.tsx b/src/renderer/src/components/task-page/task-page-localized-options.tsx index 87be90c903f..7113f309372 100644 --- a/src/renderer/src/components/task-page/task-page-localized-options.tsx +++ b/src/renderer/src/components/task-page/task-page-localized-options.tsx @@ -16,7 +16,7 @@ import { } from '../../../../shared/linear/issue-view-resume-state' import { getTaskPresetQuery } from '../../../../shared/work-items/task-preset-query' import type { GitLabMRInvolvement } from '../../../../shared/gitlab-types' -import type { GitHubPRStateChip, GitHubPRWhoChip } from './github/task-page-pr-chips' +import type { GitHubPRStateChip, GitHubPRWhoChip } from './github/listing/task-page-pr-chips' import type { TaskProvider } from '../../../../shared/work-items/task-providers' import type { TaskViewPresetId } from '../../../../shared/ui-chrome-types' diff --git a/src/renderer/src/components/task-page/use-task-page-detail-routing.ts b/src/renderer/src/components/task-page/use-task-page-detail-routing.ts index c54ba66e5a3..3b0fc51893d 100644 --- a/src/renderer/src/components/task-page/use-task-page-detail-routing.ts +++ b/src/renderer/src/components/task-page/use-task-page-detail-routing.ts @@ -5,7 +5,7 @@ import { registerLinearIssueConsumer } from '@/store/slices/linear/linear-issue-write-publication' import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' -import type { TaskPageGitHubIssueDraftModel } from './github/use-task-page-github-issue-draft' +import type { TaskPageGitHubIssueDraftModel } from './github/issue-creation/use-task-page-github-issue-draft' import { useState, useMemo, useCallback, useLayoutEffect, useRef } from 'react' import type { LinearIssue } from '../../../../shared/linear/issue-types' import type { JiraIssue } from '../../../../shared/jira/jira-types' diff --git a/src/renderer/src/components/task-page/use-task-page-search-actions.ts b/src/renderer/src/components/task-page/use-task-page-search-actions.ts index ab1cc14a3df..7f028bf0411 100644 --- a/src/renderer/src/components/task-page/use-task-page-search-actions.ts +++ b/src/renderer/src/components/task-page/use-task-page-search-actions.ts @@ -1,11 +1,11 @@ -import type { TaskPageGitHubQuietRefreshModel } from './github/use-task-page-github-quiet-refresh' +import type { TaskPageGitHubQuietRefreshModel } from './github/refresh/use-task-page-github-quiet-refresh' import React, { useCallback, useEffect } from 'react' import type { PRFilterChange } from '@/components/github/PRFilterSections' import { scopeGitHubTaskSearch, getDefaultPresetForGitHubTaskKind } from '../../../../shared/work-items/task-github-search' -import { matchGitHubPRPreset, patchGitHubTaskQuery } from './github/task-page-pr-chips' +import { matchGitHubPRPreset, patchGitHubTaskQuery } from './github/listing/task-page-pr-chips' import type { TaskViewPresetId } from '../../../../shared/ui-chrome-types' import { toast } from 'sonner' import { translate } from '@/i18n/i18n' diff --git a/src/renderer/src/components/task-page/use-task-page-source-display.test-fixture.tsx b/src/renderer/src/components/task-page/use-task-page-source-display.test-fixture.tsx deleted file mode 100644 index d576373a064..00000000000 --- a/src/renderer/src/components/task-page/use-task-page-source-display.test-fixture.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import { cleanup } from '@testing-library/react' -import { useLayoutEffect } from 'react' -import { useTaskPageGitHubSearchPagination } from './github/use-task-page-github-search-pagination' -import { useTaskPageGitHubLandingRefresh } from './github/use-task-page-github-landing-refresh' -import { taskPageGitHubResumeCache } from './github/task-page-github-resume-cache' -import { afterEach, beforeEach, vi, type Mock } from 'vitest' -import type { AppState } from '@/store/types' -import { TooltipProvider } from '@/components/ui/tooltip' -import { useAppStore } from '@/store' -import { i18n } from '@/i18n/i18n' -import type { WorkItemsCacheSources } from '@/store/github/cache/cache-model' -import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../shared/github/repositories/repository-identity' -import type { Repo } from '../../../../shared/repo-types' -import { getGitHubTaskKind } from '../../../../shared/work-items/task-github-search' -import { parseTaskQuery } from '../../../../shared/work-items/task-query' -import { useTaskPageStoreBindings } from './use-task-page-store-bindings' -import { useTaskPageRepoSelection } from './use-task-page-repo-selection' -import { useTaskPageRuntimeHosts } from './use-task-page-runtime-hosts' -import { useTaskPageSourceAvailability } from './use-task-page-source-availability' -import { useTaskPageProviderState } from './use-task-page-provider-state' -import { useTaskPageGitHubListState } from './github/use-task-page-github-list-state' -import { useTaskPageGitHubDetail } from './github/use-task-page-github-detail' -import { useTaskPageGitHubCacheReconciliation } from './github/use-task-page-github-cache-reconciliation' -import { useTaskPageSourceDisplay } from './use-task-page-source-display' -import { TaskPageGitHubFilters } from './github/Filters' -import { useTaskPageSearchActions } from './use-task-page-search-actions' -import { TaskPageGitHubList } from './github/List' -import { useTaskPageGitHubListProjection } from './github/use-task-page-github-list-projection' -import type { TaskPageComposerActionsModel } from './use-task-page-composer-actions' - -export const Q1 = 'is:issue is:open' -export const Q2 = 'is:issue label:bug' -export const upstream = { owner: 'upstream-owner', repo: 'project' } -export const fork = { owner: 'fork-owner', repo: 'project', host: 'ghe.example.com' } -export const sources = (issues: GitHubOwnerRepo | null, prs = issues): WorkItemsCacheSources => ({ - issues, - prs, - originCandidate: fork, - upstreamCandidate: upstream -}) -// Saved project identity is the upstream; the list's resolved source can differ. -export const repo: Repo = { - id: 'repo', - path: '/repo', - displayName: 'Project', - badgeColor: '#000', - addedAt: 1, - executionHostId: 'local', - upstream -} -export const sshTwin: Repo = { - ...repo, - path: '/ssh/repo', - connectionId: 'box', - executionHostId: 'ssh:box' -} - -const initialStore = useAppStore.getState() -const initialApiDescriptor = Object.getOwnPropertyDescriptor(window, 'api') -// listWorkItems answers by repo path and query; a missing answer fails the fetch. -export const listAnswers = new Map() -export const setIssueSourcePreference: Mock = - vi.fn() - -function useMountedSourceDisplay(runRequests = false) { - const store = useTaskPageStoreBindings() - const selection = useTaskPageRepoSelection(store) - const hosts = useTaskPageRuntimeHosts(selection) - const availability = useTaskPageSourceAvailability(hosts) - const provider = useTaskPageProviderState(availability) - const list = useTaskPageGitHubListState(provider) - const detail = useTaskPageGitHubDetail(list) - const reconciled = useTaskPageGitHubCacheReconciliation(detail) - // Stage 21 derives the kind the same way before handing off to the source display. - const kindModel = Object.assign(reconciled, { - activeGithubTaskKind: getGitHubTaskKind( - reconciled.activeTaskPreset, - reconciled.appliedTaskSearch - ) - }) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: pagination reads the stage-8 list state, source kind and store bindings present here; intervening provider stages add no GitHub pagination inputs. - const paginationInput = kindModel as unknown as typeof kindModel & - Parameters[0] - const withKind = useTaskPageGitHubSearchPagination(paginationInput) - // Stage 26 as mounted in production; without requests its run stays inert, as before the resume step. - useTaskPageGitHubLandingRefresh({ - ...withKind, - taskResumeApplied: runRequests && withKind.taskResumeApplied, - githubWorkItemMutationQueryKey: 'mounted-source-flow' - }) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: every field the source display reads comes from stages 1-8 or the kind above; stages 9-20 add none of them. - const sourceDisplayInput = withKind as unknown as typeof withKind & - Parameters[0] - return useTaskPageSourceDisplay(sourceDisplayInput) -} -type MountedSourceDisplay = ReturnType -export let view: MountedSourceDisplay -const NO_ROW_MUTATIONS = { softHiddenItemKeys: new Set() } -// A child, so the list's own subscriptions never count as Harness renders. -function MountedList({ model }: { model: MountedSourceDisplay }) { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the projection reads stage 1-8 state and the kind; of stages 9-23 it reads only row mutations, stubbed with none hidden. - const listInput = { ...model, githubWorkItemMutation: NO_ROW_MUTATIONS } as unknown as Parameters< - typeof useTaskPageGitHubListProjection - >[0] - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: List reads stage 1-8 state, refs and the projection; Rows, the only reader of later stages, is stubbed. - const listModel = useTaskPageGitHubListProjection( - listInput - ) as unknown as TaskPageComposerActionsModel - return ( - <> - {String(listModel.githubListPending)} - - - ) -} -/** Harness renders since the last reset; a test resets it to count one update's renders. */ -export const harnessRenders = { count: 0 } -export function Harness({ - runRequests = false, - list = false -}: { - runRequests?: boolean - list?: boolean -}) { - harnessRenders.count += 1 - const model = useMountedSourceDisplay(runRequests) - // Tests read the committed model between acts. - useLayoutEffect(() => { - view = model - }) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the filter actions read the store bindings, search state and kind mounted here; modal and dialog fields only gate the find shortcut. - const searchInput = { ...model } as unknown as Parameters[0] - const searchActions = useTaskPageSearchActions(searchInput) - const filtersFields = { - ...model, - applyPRFilterChange: searchActions.applyPRFilterChange, - handleSetDefaultTaskPreset: searchActions.handleSetDefaultTaskPreset, - setIssueSourcePreference: runRequests - ? useAppStore.getState().setIssueSourcePreference - : setIssueSourcePreference, - appliedTaskQuery: parseTaskQuery(model.appliedTaskSearch), - loadedGitHubAuthorLogins: [], - primaryGithubFilterSlug: null, - githubTasksBusy: false, - newIssueTargetRepo: null - } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the mounted stage-8 model plus these stubs cover every field Filters reads; later-stage handlers are never fired here. - const filtersModel = filtersFields as unknown as TaskPageComposerActionsModel - return ( - - - - {model.pages[model.currentPage]?.map((row) => row.title).join(',') ?? ''} - - {model.countedTotalPages ?? 'unknown'} - {list ? : null} - - ) -} - -/** Each mounted source-display test starts from a clean store, preload API and resume cache. */ -export function installSourceDisplayTestLifecycle() { - beforeEach(() => { - taskPageGitHubResumeCache.clear() - listAnswers.clear() - setIssueSourcePreference.mockReset() - Object.defineProperty(window, 'api', { - configurable: true, - value: { - gh: { - countWorkItems: vi.fn().mockResolvedValue(0), - viewer: vi.fn().mockResolvedValue(null), - listWorkItems: vi.fn(async ({ repoPath, query = '' }) => { - const answer = listAnswers.get(`${repoPath}\0${query}`) - if (!answer) { - throw new Error('list failed') - } - return { items: [], sources: answer } - }) - }, - repos: { update: vi.fn(async ({ repoId }) => ({ ...repo, id: repoId })) }, - ui: { set: vi.fn().mockResolvedValue(undefined) } - } - }) - useAppStore.setState({ ...initialStore, workItemsCache: {} }, true) - }) - afterEach(async () => { - await i18n.changeLanguage('en') - cleanup() - useAppStore.setState(initialStore, true) - if (initialApiDescriptor) { - Object.defineProperty(window, 'api', initialApiDescriptor) - } else { - Reflect.deleteProperty(window, 'api') - } - }) -} diff --git a/src/renderer/src/hooks/useTaskPageGitHubWorkItemMutation.ts b/src/renderer/src/hooks/useTaskPageGitHubWorkItemMutation.ts index 3b6d9700e84..cb570e9068c 100644 --- a/src/renderer/src/hooks/useTaskPageGitHubWorkItemMutation.ts +++ b/src/renderer/src/hooks/useTaskPageGitHubWorkItemMutation.ts @@ -2,7 +2,7 @@ import { getWorkItemRepo } from '@/lib/work-items/work-item-repo-row' import { buildTaskPageGitHubWorkItemMutationPatch, type TaskPageGitHubMutationIntent -} from '@/components/task-page/github/task-page-github-work-item-mutation-patches' +} from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-patches' import { useCallback, useEffect, useRef, useState } from 'react' import { toast } from 'sonner' import { parseExecutionHostId } from '../../../shared/execution-host' @@ -14,18 +14,18 @@ import { useAppStore } from '@/store' import { beginTaskPageGitHubWorkItemMutation, canStartTaskPageGitHubWorkItemMutation -} from '@/components/task-page/github/task-page-github-work-item-mutations' +} from '@/components/task-page/github/mutations/task-page-github-work-item-mutations' import { confirmTaskPageGitHubWorkItemMutation, rollbackTaskPageGitHubWorkItemMutation -} from '@/components/task-page/github/task-page-github-work-item-mutation-lifecycle' +} from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-lifecycle' -import type { TaskPageGitHubPatchWorkItem } from '@/components/task-page/github/task-page-github-work-item-mutation-types' +import type { TaskPageGitHubPatchWorkItem } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-types' import { getTaskPageGitHubSoftHiddenItemKeys, subscribeTaskPageGitHubMutationRegistry, setTaskPageGitHubMutationQueryKey -} from '@/components/task-page/github/task-page-github-work-item-mutation-registry' +} from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-registry' import { useMountedRef } from './useMountedRef' import { codeHostErrorText } from '@/lib/source-control/remote/code-host-error-copy' diff --git a/src/renderer/src/store/github/sources/github-source-lifetime.test.tsx b/src/renderer/src/store/github/sources/github-source-lifetime.test.tsx deleted file mode 100644 index f79887fd371..00000000000 --- a/src/renderer/src/store/github/sources/github-source-lifetime.test.tsx +++ /dev/null @@ -1,167 +0,0 @@ -// @vitest-environment happy-dom -import { cleanup } from '@testing-library/react' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { useStore } from 'zustand' -import { createTestStore, mockApi, resetRemoteRuntimeMocks } from '../github-slice-test-harness' -import { createRepoUpdateActions } from '../../repos/repo-update' -import { taskPageGitHubResumeCache } from '@/components/task-page/github/task-page-github-resume-cache' -import type { AppState } from '../../types' -import { createGlobalSettingsFixture } from '../../../../../shared/global-settings-test-fixture' -import type { Repo } from '../../../../../shared/repo-types' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' - -const holder = vi.hoisted((): { store: ReturnType | null } => ({ - store: null -})) -vi.mock('@/store/index', () => ({ - useAppStore: Object.assign( - (selector: (state: AppState) => T) => useStore(holder.store!, selector), - { - getState: () => holder.store!.getState(), - subscribe: (listener: (state: AppState) => void) => holder.store!.subscribe(listener) - } - ) -})) -vi.mock('sonner', () => ({ toast: { error: vi.fn(), success: vi.fn() } })) - -const origin = { owner: 'fork', repo: 'project' } -const upstream = { owner: 'parent', repo: 'project' } -const repo: Repo = { - id: 'source-repo', - path: '/source-repo', - displayName: 'Project', - addedAt: 1, - badgeColor: '', - executionHostId: 'local' -} -function row(owner: string, number = 12): GitHubWorkItem { - return { - id: `issue:${number}`, - repoId: repo.id, - type: 'issue', - number, - title: owner, - state: 'open', - url: `https://github.com/${owner}/project/issues/${number}`, - author: null, - labels: [], - assignees: [], - updatedAt: '2026-11-01T00:00:00Z' - } -} -const envelope = (owner: string) => ({ - items: [row(owner)], - sources: { - issues: { owner, repo: 'project' }, - prs: { owner, repo: 'project' }, - originCandidate: origin, - upstreamCandidate: upstream - } -}) -const args = [{ repoId: repo.id, path: repo.path }] -const flush = async () => { - for (let i = 0; i < 8; i++) { - await Promise.resolve() - } -} -function setup() { - const store = createTestStore() - store.setState({ - repos: [repo], - projects: [], - projectHostSetups: [], - settings: createGlobalSettingsFixture(), - fetchReposForAllHosts: vi.fn().mockResolvedValue(undefined), - ...createRepoUpdateActions(store.setState, store.getState) - }) - holder.store = store - return store -} -beforeEach(() => { - vi.stubGlobal('window', document.defaultView) - Object.defineProperty(window, 'api', { configurable: true, value: mockApi }) - vi.clearAllMocks() - resetRemoteRuntimeMocks() - mockApi.repos.update.mockResolvedValue(undefined) - mockApi.gh.listWorkItems.mockResolvedValue(envelope('parent')) -}) -afterEach(() => { - cleanup() - holder.store = null -}) - -describe('GitHub source collection lifetime', () => { - it('rejects inverted A1/B/A2 list deliveries including page N', async () => { - const store = setup() - const a1 = Promise.withResolvers>() - mockApi.gh.listWorkItems.mockReturnValueOnce(a1.promise) - const old = store.getState().fetchWorkItems(repo.id, repo.path, 20, '') - await flush() - await store.getState().setIssueSourcePreference(repo.id, repo.path, 'origin', 'local') - const b = Promise.withResolvers>() - mockApi.gh.listWorkItems.mockReturnValueOnce(b.promise) - const page = store.getState().fetchWorkItemsNextPage(args, 20, 20, '', 1) - await flush() - await store.getState().setIssueSourcePreference(repo.id, repo.path, 'upstream', 'local') - mockApi.gh.listWorkItems.mockResolvedValueOnce(envelope('parent')) - const a2 = await store.getState().fetchWorkItemsAcrossRepos(args, 20, 20, '') - b.resolve(envelope('fork')) - a1.resolve(envelope('parent')) - expect(await old).toBeUndefined() - expect(await page).toBeUndefined() - expect(a2?.items.map((item) => item.url)).toEqual([row('parent').url]) - expect( - store - .getState() - .getCachedWorkItems(repo.id, 20, '', repo.path) - ?.map((item) => item.url) - ).toEqual([row('parent').url]) - }) -}) - -describe('source mapping and estimate retirement', () => { - it('retires every query and resume page when origin alone changes, and cannot revive a removed owner through a pending save', async () => { - const store = setup() - await store.getState().fetchWorkItems(repo.id, repo.path, 20, '') - mockApi.gh.countWorkItems.mockResolvedValue(40) - await store.getState().countWorkItemsAcrossRepos(args, '', 20) - const owner = store.getState().githubSourceCollections - const oldOwner = owner.context(repo) - taskPageGitHubResumeCache.write('old-context', 2, [row('parent')], undefined, [oldOwner]) - const oldQuery = Promise.withResolvers>() - mockApi.gh.listWorkItems.mockReturnValueOnce(oldQuery.promise) - const old = store.getState().fetchWorkItems(repo.id, repo.path, 20, 'label:old') - await flush() - mockApi.gh.listWorkItems.mockResolvedValueOnce({ - ...envelope('parent'), - sources: { - ...envelope('parent').sources, - originCandidate: { owner: 'new-fork', repo: 'project' } - } - }) - await store.getState().fetchWorkItems(repo.id, repo.path, 20, 'label:new') - oldQuery.resolve(envelope('parent')) - expect(await old).toBeUndefined() - expect(store.getState().getCachedWorkItems(repo.id, 20, '', repo.path)).toBeNull() - expect(taskPageGitHubResumeCache.read('old-context', 2)).toBeNull() - expect(owner.context(repo)).not.toBe(oldOwner) - const save = Promise.withResolvers() - mockApi.repos.update.mockReturnValueOnce(save.promise) - const saving = store.getState().setIssueSourcePreference(repo.id, repo.path, 'origin', 'local') - const waiting = owner.awaitSave(store.getState().repos[0]) - const oldCatalog = owner.catalogSnapshot() - store.setState({ repos: [] }) - const removed = await waiting - expect(removed !== 'save-failed' && removed()).toBe(false) - store.setState({ repos: [{ ...repo, issueSourcePreference: 'upstream' }] }) - const replacement = owner.context(store.getState().repos[0]) - save.resolve({ ...repo, issueSourcePreference: 'origin' }) - await saving - expect(store.getState().repos[0].issueSourcePreference).toBe('upstream') - expect(owner.context(store.getState().repos[0])).toBe(replacement) - expect( - owner.adopt({ ...repo, issueSourcePreference: 'origin' }, oldCatalog).issueSourcePreference - ).toBe('upstream') - expect(store.getState().getCachedWorkItems(repo.id, 20, '', repo.path)).toBeNull() - }) -}) diff --git a/src/renderer/src/store/github/sources/source-collections.ts b/src/renderer/src/store/github/sources/source-collections.ts index 6d56e2f4591..bb24d84e6ce 100644 --- a/src/renderer/src/store/github/sources/source-collections.ts +++ b/src/renderer/src/store/github/sources/source-collections.ts @@ -9,7 +9,7 @@ import { nextProviderRequestId } from '../requests/request-coordination' import { withBoundedCacheEntry, evictStaleEntries } from '../cache/cache-policy' import { foldReportedCandidates } from './source-candidate-evidence' import { workItemsCacheKey } from '../cache/cache-identity' -import { taskPageGitHubResumeCache } from '@/components/task-page/github/task-page-github-resume-cache' +import { taskPageGitHubResumeCache } from '@/components/task-page/github/cache/task-page-github-resume-cache' import { GitHubSourceOwnership, githubCollectionScope, diff --git a/src/renderer/src/store/github/task-records/github-task-records.test.ts b/src/renderer/src/store/github/task-records/github-task-records.test.ts deleted file mode 100644 index ea149195500..00000000000 --- a/src/renderer/src/store/github/task-records/github-task-records.test.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { answered } from '../requests/answered-request.test-fixture' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { Repo } from '../../../../../shared/repo-types' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import { createTestStore, mockApi, resetRemoteRuntimeMocks } from '../github-slice-test-harness' -import { getTaskPageRepoSourceContext } from '@/lib/work-items/task-repo-source-context' -import { resetTaskPageGitHubMutationRegistryForTests } from '@/components/task-page/github/task-page-github-work-item-mutation-registry' - -const lookup = vi.hoisted(() => vi.fn()) -vi.mock('@/lib/work-items/github/github-work-item-source-lookup', () => ({ - lookupGitHubWorkItemByOwnerRepoForSource: lookup -})) -const repo: Repo = { - id: 'repo', - path: '/repo', - displayName: 'Repo', - badgeColor: 'blue', - addedAt: 1, - ghAccount: { host: 'github.com', user: 'alice' } -} -const row: GitHubWorkItem = { - id: 'issue:42', - repoId: 'repo', - type: 'issue', - number: 42, - title: 'Original', - state: 'open', - labels: [], - assignees: [], - url: 'https://github.com/acme/widgets/issues/42', - author: null, - updatedAt: '2026-10-31T12:00:02Z' -} -function deferred() { - let resolve!: (value: T) => void - let reject!: (error: Error) => void - const promise = new Promise((r, fail) => { - resolve = r - reject = fail - }) - return { promise, resolve, reject } -} -async function flush() { - await Promise.resolve() - await Promise.resolve() - await Promise.resolve() -} -function setup(repos = [repo]) { - const store = createTestStore() - store.setState({ repos }) - const cache = store.getState().githubTaskCache - const binding = cache.capture(repos[0])! - return { store, cache, binding } -} -const sources = { - issues: { owner: 'acme', repo: 'widgets' }, - prs: null, - originCandidate: null, - upstreamCandidate: null -} -/** A Tasks row cached by the real fetch, plus a binding captured for its source. */ -async function tasksRow(store: ReturnType, item: GitHubWorkItem = row) { - mockApi.gh.listWorkItems.mockResolvedValue({ items: [item], sources }) - const sourceContext = getTaskPageRepoSourceContext(repo, 'github') - await store.getState().fetchWorkItems(repo.id, repo.path, 20, '', { sourceContext }) - const copy = () => Object.values(store.getState().workItemsCache).flatMap((e) => e.data ?? [])[0] - const capture = () => store.getState().githubTaskCache.capture(repo, sourceContext)! - const search = async () => - answered( - await store - .getState() - .fetchWorkItems(repo.id, repo.path, 20, '', { sourceContext, force: true }) - )[0] - return { copy, capture, search } -} - -/** An account switch and back: the old binding is disposed and a new incarnation replaces it. */ -function rebind(store: ReturnType) { - store.setState({ repos: [{ ...repo, ghAccount: { host: 'github.com', user: 'bob' } }] }) - store.setState({ repos: [repo] }) -} - -function taskFacts(item: GitHubWorkItem) { - return { title: item.title, state: item.state, labels: item.labels } -} - -describe('shared GitHub task records through production store actions', () => { - beforeEach(() => { - vi.clearAllMocks() - resetRemoteRuntimeMocks() - resetTaskPageGitHubMutationRegistryForTests() - // This mock represents the already-shipped safe local item route. - Object.assign(mockApi.gh, { workItemByOwnerRepo: vi.fn() }) - lookup.mockReset() - lookup.mockResolvedValue(row) - }) - - it('rejects pre-edit canonical C and coalesces one replacement after B settles', async () => { - const { cache, binding } = setup() - const c = deferred() - const c2 = deferred() - lookup.mockReturnValue(c2.promise) - const read = cache.readCanonical(row, binding, () => c.promise) - const b = cache.operation(row, binding, { labels: { new: true } }) - b(true) - expect(lookup).not.toHaveBeenCalled() - c.resolve(row) - expect(await read).toBe(false) - - c2.resolve({ ...row, labels: ['new'] }) - await flush() - expect(cache.projectItem(row).labels).toEqual(['new']) - }) - - it('disposes an account incarnation and rejects its deferred result after A to B to A', async () => { - const { store, cache, binding } = setup() - const response = deferred() - const read = cache.readCanonical(row, binding, () => response.promise) - const listener = vi.fn() - cache.subscribe(listener) - store.setState({ repos: [{ ...repo, ghAccount: { host: 'github.com', user: 'bob' } }] }) - store.setState({ repos: [repo] }) - response.resolve({ ...row, title: 'Old Alice' }) - expect(await read).toBe(false) - expect(cache.current(binding)).toBe(false) - expect(cache.projectItem(row).title).toBe('Original') - expect(listener).toHaveBeenCalled() - expect(cache.capture(repo)!.incarnation).not.toBe(binding.incarnation) - }) - - it('establishes from the read after the last of two overlapping edits', async () => { - const { store, cache } = setup() - const { capture } = await tasksRow(store) - const r1 = deferred(), - r2 = deferred() - lookup.mockReturnValueOnce(r1.promise).mockReturnValueOnce(r2.promise) - cache.operation(row, capture(), { labels: { a: true } })(true) - cache.operation(row, capture(), { labels: { b: true } })(true) - - r1.resolve({ ...row, labels: ['a'], updatedAt: '2026-10-31T12:00:03Z' }) - await flush() - - expect(cache.projectItem(row).labels).toEqual(['a', 'b']) - const after = { - ...row, - title: 'After B', - labels: ['a', 'b'], - updatedAt: '2026-10-31T12:00:04Z' - } - r2.resolve(after) - await flush() - expect(taskFacts(cache.projectItem(row))).toEqual(taskFacts(after)) - cache.acceptSearch([after], capture()) - await flush() - }) - - it('leaves copies to a replacement incarnation and reconciles them with its own read', async () => { - const { store, cache } = setup() - const { copy, capture } = await tasksRow(store) - await cache.readCanonical(row, capture(), async () => row) - const old = cache.operation(row, capture(), { labels: { old: true } }) - expect(copy().labels).toEqual([]) - rebind(store) - await tasksRow(store) - const next = cache.operation(copy(), capture(), { labels: { new: true } }) - expect(cache.projectItem(copy()).labels).toEqual(['new']) - expect(old(false).labels).toEqual([]) - lookup.mockResolvedValue({ ...row, title: 'Reconciled', updatedAt: '2026-10-31T12:00:03Z' }) - next(false) - await flush() - // The late old settlement owes the replacement its own read, even though its own edit failed. - - expect(copy()).toMatchObject({ title: 'Reconciled', labels: [] }) - }) - it('lets a Refresh queued behind a discarded one read once that owner retires', async () => { - const { cache, binding } = setup() - const first = deferred() - const r1 = cache.readCanonical(row, binding, () => first.promise) - const second = vi.fn(async () => ({ ...row, title: 'Fresh' })) - const r2 = cache.readCanonical(row, binding, second) - first.resolve(null) - expect([await r1, await r2]).toEqual([false, true]) - - expect(cache.projectItem(row).title).toBe('Fresh') - }) -}) diff --git a/src/renderer/src/store/github/task-records/task-record-owners.ts b/src/renderer/src/store/github/task-records/task-record-owners.ts index 4e1752c04e8..fbecf9347c1 100644 --- a/src/renderer/src/store/github/task-records/task-record-owners.ts +++ b/src/renderer/src/store/github/task-records/task-record-owners.ts @@ -1,5 +1,5 @@ -import { deleteConfirmedListSnapshot } from '@/components/task-page/github/task-page-github-work-item-mutation-registry' -import { deleteLastConfirmedClientValue } from '@/components/task-page/github/task-page-github-confirmed-client-values' +import { deleteConfirmedListSnapshot } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-registry' +import { deleteLastConfirmedClientValue } from '@/components/task-page/github/mutations/task-page-github-confirmed-client-values' import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' import type { Repo } from '../../../../../shared/repo-types' From a3be49edd484e98580b47c12ef17c9e54a8b296c Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 04:04:33 -0800 Subject: [PATCH 083/178] test: remove Calculator smoke and redundant coverage (#42000) * test: remove weak native smoke and repeated writer coverage * test: focus packaged PTY coverage on output and exit delivery --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .github/workflows/computer-e2e.yml | 3 +- .../mobile-web-app-code-block-selection.mjs | 1 - ...rcad-packaged-node-pty.integration.test.ts | 13 +- .../native-slots/orcad-node-slot-fixture.ts | 18 +-- tests/e2e/computer-windows-store.e2e.ts | 119 ------------------ .../cross-version-stop-note-scenarios.ts | 64 +++++----- 6 files changed, 35 insertions(+), 183 deletions(-) delete mode 100644 tests/e2e/computer-windows-store.e2e.ts diff --git a/.github/workflows/computer-e2e.yml b/.github/workflows/computer-e2e.yml index 60e15f4794c..f26cfe41eec 100644 --- a/.github/workflows/computer-e2e.yml +++ b/.github/workflows/computer-e2e.yml @@ -53,7 +53,6 @@ on: - 'src/shared/computer-use-*.ts' - 'tests/e2e/computer-linux.e2e.ts' - 'tests/e2e/computer-windows.e2e.ts' - - 'tests/e2e/computer-windows-store.e2e.ts' - 'tests/e2e/computer-windows-background.e2e.ts' - 'tests/e2e/helpers/windows-semantic-controls-fixture.*' - 'tests/e2e/helpers/computer-cli-driver.ts' @@ -153,4 +152,4 @@ jobs: - run: pnpm verify:computer-native - run: pnpm build:cli - run: pnpm run build:electron-vite:parallel - - run: pnpm test:e2e:computer --reporter=verbose tests/e2e/computer-windows.e2e.ts tests/e2e/computer-windows-store.e2e.ts tests/e2e/computer-windows-background.e2e.ts + - run: pnpm test:e2e:computer --reporter=verbose tests/e2e/computer-windows.e2e.ts tests/e2e/computer-windows-background.e2e.ts diff --git a/config/scripts/mobile-web-app/shell/mobile-web-app-code-block-selection.mjs b/config/scripts/mobile-web-app/shell/mobile-web-app-code-block-selection.mjs index d747e67fab9..738f5318edf 100644 --- a/config/scripts/mobile-web-app/shell/mobile-web-app-code-block-selection.mjs +++ b/config/scripts/mobile-web-app/shell/mobile-web-app-code-block-selection.mjs @@ -12,7 +12,6 @@ async function verifyCodeBlockSelections({ readSurface }) { const cases = [ - { markdown: 'selected text', html: '

selected text

', literal: 'selected text' }, { markdown: 'first\nsecond', html: '

first
second

', literal: 'first\nsecond' }, { markdown: 'first\n\nsecond', diff --git a/src/main/orcad/native-runtime/orcad-packaged-node-pty.integration.test.ts b/src/main/orcad/native-runtime/orcad-packaged-node-pty.integration.test.ts index 386d7ea0e79..62c67deedd8 100644 --- a/src/main/orcad/native-runtime/orcad-packaged-node-pty.integration.test.ts +++ b/src/main/orcad/native-runtime/orcad-packaged-node-pty.integration.test.ts @@ -5,7 +5,6 @@ import { tmpdir } from 'node:os' import { join, resolve } from 'node:path' import { describe, expect, it } from 'vitest' import { runProcess } from '@orca/process-host' -import { NODE_RUNTIME_PIN } from '../../../shared/node-runtime-pin' import { ORCAD_NODE_PTY_DIR } from '../../../shared/orcad-artifacts' import { removeTreeSync } from '../../../shared/windows-transient-lock-removal' import { @@ -22,7 +21,7 @@ const skip = skipForMissingInputs('artifact', [ ]) describe.skipIf(skip)('packaged node-pty under the pinned Node', () => { - it('spawns, reattaches, delivers data and retires a shell from the shipped slot only', async () => { + it('spawns a shell and delivers its output and exit from the shipped slot only', async () => { const directory = await mkdtemp(join(tmpdir(), 'orca-packaged-node-pty-')) try { const entry = join(directory, 'local-pty.cjs') @@ -52,16 +51,13 @@ describe.skipIf(skip)('packaged node-pty under the pinned Node', () => { sessionId: 'packaged-node-pty', cols: 80, rows: 24, cwd: process.cwd(), shellOverride: process.platform === 'win32' ? getCmdExePath() : '/bin/sh' }) - const again = await provider.spawn({ sessionId: first.id, cols: 100, rows: 30 }) provider.write(first.id, process.platform === 'win32' ? 'echo ORCA_PACKAGED_PTY_READY & exit 17\\r' : 'printf ORCA_PACKAGED_PTY_READY; exit 17\\r') const code = await exit clearTimeout(deadline) console.log(JSON.stringify({ - version: process.versions.node, code, output: output.includes('ORCA_PACKAGED_PTY_READY'), - reattached: again.isReattach === true && again.pid === first.pid, - retired: provider.getPtyProcess(first.id) === undefined + code, output: output.includes('ORCA_PACKAGED_PTY_READY') })) })().catch(error => { clearTimeout(deadline); provider.killAll(); console.error(error); process.exitCode = 1 }) `, @@ -91,11 +87,8 @@ describe.skipIf(skip)('packaged node-pty under the pinned Node', () => { }) expect(result.code, result.stderr).toBe(0) expect(JSON.parse(result.stdout)).toEqual({ - version: NODE_RUNTIME_PIN.version, code: 17, - output: true, - reattached: true, - retired: true + output: true }) } finally { // Remove the junction before recursively deleting the test root. diff --git a/src/main/orcad/testing/native-slots/orcad-node-slot-fixture.ts b/src/main/orcad/testing/native-slots/orcad-node-slot-fixture.ts index f9f8fb83016..618729bc675 100644 --- a/src/main/orcad/testing/native-slots/orcad-node-slot-fixture.ts +++ b/src/main/orcad/testing/native-slots/orcad-node-slot-fixture.ts @@ -1,10 +1,9 @@ -import { copyFileSync, existsSync, linkSync, mkdirSync, readFileSync, readdirSync } from 'node:fs' +import { copyFileSync, existsSync, linkSync, mkdirSync, readdirSync } from 'node:fs' import { copyFile, link, mkdir, writeFile } from 'node:fs/promises' import { createRequire } from 'node:module' import type * as NodePty from 'node-pty' import { join, resolve } from 'node:path' import { it } from 'vitest' -import { runProcessSync } from '@orca/process-host' import { NODE_RUNTIME_ASSETS, SERVER_TARGETS, @@ -42,21 +41,6 @@ export function locatePinnedNodeForTests(): string | null { ) } -const crossRuntimeBunVersion = readFileSync( - new URL('../../../../../config/.bun-version', import.meta.url), - 'utf8' -).trim() - -export function locateBunForTests(): string | null { - const candidate = process.env.BUN_EXECUTABLE ?? 'bun' - try { - const result = runProcessSync({ program: candidate, args: ['--version'], timeoutMs: 10_000 }) - return result.code === 0 && result.stdout.trim() === crossRuntimeBunVersion ? candidate : null - } catch { - return null - } -} - export async function writeNodeSlotFixture( root: string, pinnedNode: string diff --git a/tests/e2e/computer-windows-store.e2e.ts b/tests/e2e/computer-windows-store.e2e.ts deleted file mode 100644 index 0c13828021e..00000000000 --- a/tests/e2e/computer-windows-store.e2e.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { describe, expect, test } from 'vitest' -import type { ComputerListAppsResult, ComputerSnapshotResult } from '../../src/shared/runtime-types' -import { - ensureOrcaRuntimeLaunched, - findRoleIndex, - parseJsonOutput, - runOrcaCli, - stopOrcaRuntime -} from './helpers/computer-driver' - -const isWindows = process.platform === 'win32' -const e2eOptIn = process.env.ORCA_COMPUTER_E2E === '1' - -describe.skipIf(!isWindows || !e2eOptIn)('computer-use Windows e2e (Calculator)', () => { - test('Calculator windows are discoverable by title and clickable', async () => { - await ensureOrcaRuntimeLaunched() - await launchCalculator() - try { - const apps = parseJsonOutput<{ result: ComputerListAppsResult }>( - (await runOrcaCli(['computer', 'list-apps', '--json'])).stdout - ) - // Windows 2025 hosts Calculator as win32calc; older images use ApplicationFrameHost. - const calculatorApp = apps.result.apps.find( - (app) => - (app.name === 'Calculator' && app.bundleId === 'ApplicationFrameHost') || - (app.name === 'win32calc' && app.bundleId === 'win32calc') - ) - expect(calculatorApp).toMatchObject({ isRunning: true }) - - const state = parseJsonOutput<{ result: ComputerSnapshotResult }>( - ( - await runOrcaCli([ - 'computer', - 'get-app-state', - '--app', - 'Calculator', - '--no-screenshot', - '--json' - ]) - ).stdout - ) - const buttonIndex = findRoleIndex( - state.result.snapshot.treeText, - /^\s*(\d+)\s+button(?:\s|$)/m - ) - // Classic Calculator exposes only pane nodes; clicking one still proves title routing. - const clickIndex = - buttonIndex >= 0 - ? buttonIndex - : findRoleIndex(state.result.snapshot.treeText, /^\s*(\d+)\s+pane(?:\s|$)/m) - expect(clickIndex, state.result.snapshot.treeText).toBeGreaterThanOrEqual(0) - const clicked = parseJsonOutput<{ result: ComputerSnapshotResult }>( - ( - await runOrcaCli([ - 'computer', - 'click', - '--app', - 'Calculator', - '--element-index', - String(clickIndex), - '--no-screenshot', - '--json' - ]) - ).stdout - ) - expect(clicked.result.snapshot.elementCount).toBeGreaterThan(0) - } finally { - await killCalculator() - await stopOrcaRuntime() - } - }) -}) - -async function launchCalculator(): Promise { - await runPowerShell('Start-Process calc.exe') - await runPowerShell( - [ - '$deadline = (Get-Date).AddSeconds(15)', - '$target = $null', - 'while ((Get-Date) -lt $deadline -and $null -eq $target) {', - ' Start-Sleep -Milliseconds 250', - ' $target = Get-Process |', - ' Where-Object { $_.MainWindowHandle -ne 0 -and $_.MainWindowTitle -eq "Calculator" } |', - ' Select-Object -First 1', - '}', - 'if ($null -eq $target) { throw "No visible Calculator window found" }' - ].join('\n') - ) -} - -async function killCalculator(): Promise { - // Why: teardown is best-effort so cleanup noise cannot mask assertion signal. - await runPowerShell( - [ - '$processes = @()', - '$processes += Get-Process -Name CalculatorApp -ErrorAction SilentlyContinue', - '$processes += Get-Process -Name win32calc -ErrorAction SilentlyContinue', - '$processes += Get-Process -Name ApplicationFrameHost -ErrorAction SilentlyContinue |', - ' Where-Object { $_.MainWindowTitle -eq "Calculator" }', - 'foreach ($process in $processes) {', - ' try { Stop-Process -Id $process.Id -Force -ErrorAction Stop } catch { }', - '}', - 'exit 0' - ].join('\n') - ).catch(() => undefined) -} - -async function runPowerShell(script: string): Promise { - const { execFile } = await import('node:child_process') - await new Promise((resolve, reject) => { - execFile('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], (error) => { - if (error) { - reject(error) - return - } - resolve() - }) - }) -} diff --git a/tests/e2e/cross-version-wire/cross-version-stop-note-scenarios.ts b/tests/e2e/cross-version-wire/cross-version-stop-note-scenarios.ts index 4a483ba849c..655b92c92a7 100644 --- a/tests/e2e/cross-version-wire/cross-version-stop-note-scenarios.ts +++ b/tests/e2e/cross-version-wire/cross-version-stop-note-scenarios.ts @@ -17,40 +17,36 @@ export function describeReleasedStopNoteProjection(port: ScenarioPort): void { const rig = createReleasedStopNoteRig(port, ref) const { page, subscribe, events, reduce, render, turn, note, seed } = rig - it.each([false, true])( - 'renders the same-revision note with a live turn end (lifecycle batch=%s)', - async (batch) => { - const before = await seed() - expect(render(before)).toMatchObject({ failure: { kind: 'cancelUnconfirmed' } }) - const prior = before.items.find((item) => item.itemId === noteId)! - const replies = await subscribe() - replies.length = 0 - await turn('interrupted', batch) - const event = events(replies).find( - (event) => - event.type === 'batch' && event.batch.items.some((item) => item.itemId === noteId) - ) - expect(event).toMatchObject({ - type: 'batch', - batch: { - items: expect.arrayContaining([ - expect.objectContaining({ itemId: turnItemId }), - expect.objectContaining({ ...prior, body: ordinary }) - ]), - removedItemIds: [] - } - }) - const after = reduce(before, replies) - expect(render(after)).toEqual({ type: 'text', text: ordinary.text }) - expect(rig.journal.itemBody(noteId)).toEqual(unconfirmed) - // The turn end is one saved sequence that touches only the turn, whichever writer made it. - const since = rig.journal.readSince(before.cursor!) - expect(since).toMatchObject({ - ok: true, - rows: [expect.objectContaining({ kind: 'outcome', itemIds: [turnItemId] })] - }) - } - ) + it('renders the same-revision note with a live turn end', async () => { + const before = await seed() + expect(render(before)).toMatchObject({ failure: { kind: 'cancelUnconfirmed' } }) + const prior = before.items.find((item) => item.itemId === noteId)! + const replies = await subscribe() + replies.length = 0 + await turn('interrupted', true) + const event = events(replies).find( + (event) => + event.type === 'batch' && event.batch.items.some((item) => item.itemId === noteId) + ) + expect(event).toMatchObject({ + type: 'batch', + batch: { + items: expect.arrayContaining([ + expect.objectContaining({ itemId: turnItemId }), + expect.objectContaining({ ...prior, body: ordinary }) + ]), + removedItemIds: [] + } + }) + const after = reduce(before, replies) + expect(render(after)).toEqual({ type: 'text', text: ordinary.text }) + expect(rig.journal.itemBody(noteId)).toEqual(unconfirmed) + const since = rig.journal.readSince(before.cursor!) + expect(since).toMatchObject({ + ok: true, + rows: [expect.objectContaining({ kind: 'outcome', itemIds: [turnItemId] })] + }) + }) it('refreshes a non-upgrade stale page after an out-of-window live correction at the terminal cursor', async () => { await turn('running') await note() From 2f9b122201e1b6c3bde7935c6c2b5df86acf517d Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 04:23:13 -0800 Subject: [PATCH 084/178] test: remove native smoke and duplicate input checks (#42007) * test: remove repeated focused Linux text insertion * test: remove repeated foreground TextEdit replacement * test: remove Notepad smoke and duplicate paste coverage * test: drop repeated numeric row and scroll-work counters --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../AttributeValueCoercionTests.swift | 1 - .../ElementActionabilityTests.swift | 6 - tests/e2e/computer-linux.e2e.ts | 23 ---- tests/e2e/computer-mac.e2e.ts | 72 ----------- tests/e2e/computer-windows.e2e.ts | 115 ++---------------- tests/e2e/helpers/notepad-document.ts | 22 +--- 6 files changed, 14 insertions(+), 225 deletions(-) diff --git a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/AttributeValueCoercionTests.swift b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/AttributeValueCoercionTests.swift index 947a4c1da61..2eb42dd2ee1 100644 --- a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/AttributeValueCoercionTests.swift +++ b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/AttributeValueCoercionTests.swift @@ -24,7 +24,6 @@ final class AttributeValueCoercionTests: XCTestCase { ("9.007199254740993e15", 9_007_199_254_740_993), ("10.0e-1", 1), ("+0.000E999999999999999999999", 0), - ("-.0e-999999999999999999999", 0), ] for (requested, expected) in cases { diff --git a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ElementActionabilityTests.swift b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ElementActionabilityTests.swift index 92a411fa7b2..fc8b629438d 100644 --- a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ElementActionabilityTests.swift +++ b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ElementActionabilityTests.swift @@ -19,7 +19,6 @@ final class ElementActionabilityTests: XCTestCase { var nodes: [Int: Node] var retriesLeft = 5 var onRetry: (FakeTree) -> Void = { _ in } - private(set) var scrollRequests = 0 init(_ nodes: [Int: Node]) { self.nodes = nodes @@ -49,7 +48,6 @@ final class ElementActionabilityTests: XCTestCase { func scrollToVisible(_ element: Int) throws -> Bool { guard let target = nodes[element]?.scrollsTo else { return false } - scrollRequests += 1 nodes[element]?.frame = target return true } @@ -100,7 +98,6 @@ final class ElementActionabilityTests: XCTestCase { guard case let .ready(point) = try ElementActionability.pointerTarget(3, windowFrame: window, ax: tree) else { return XCTFail("the button should be clickable once scrolled into view") } - XCTAssertEqual(tree.scrollRequests, 1) XCTAssertEqual(point, CGPoint(x: 50, y: 160)) } @@ -123,7 +120,6 @@ final class ElementActionabilityTests: XCTestCase { guard case let .ready(point) = try ElementActionability.pointerTarget(3, windowFrame: window, ax: tree) else { return XCTFail("the visible part of the group should take the click") } - XCTAssertEqual(tree.scrollRequests, 0) XCTAssertEqual(point, CGPoint(x: 200, y: 150)) } @@ -149,7 +145,6 @@ final class ElementActionabilityTests: XCTestCase { guard case let .ready(point) = try ElementActionability.pointerTarget(3, windowFrame: window, ax: tree) else { return XCTFail("the row should be clicked once scrolled out from under the header") } - XCTAssertEqual(tree.scrollRequests, 1) XCTAssertEqual(point, CGPoint(x: 50, y: 50)) } @@ -202,7 +197,6 @@ final class ElementActionabilityTests: XCTestCase { guard case let .ready(point) = try ElementActionability.pointerTarget(3, windowFrame: window, ax: tree) else { return XCTFail("the link should be clicked once scrolled into its container's view") } - XCTAssertEqual(tree.scrollRequests, 1) XCTAssertEqual(point, CGPoint(x: 50, y: 60)) } diff --git a/tests/e2e/computer-linux.e2e.ts b/tests/e2e/computer-linux.e2e.ts index 8da3c487d04..e001eefd01a 100644 --- a/tests/e2e/computer-linux.e2e.ts +++ b/tests/e2e/computer-linux.e2e.ts @@ -76,29 +76,6 @@ describe.skipIf(!isLinux || !e2eOptIn)('computer-use Linux e2e (gedit)', () => { await killGedit() }) - test('click and type-text insert into the focused document through AT-SPI', async () => { - await focusGeditDocument() - - const marker = ` orca-linux-type-${Date.now()}-élan-🚀` - const typed = parseJsonOutput<{ result: ComputerActionResult }>( - ( - await runOrcaCli([ - 'computer', - 'type-text', - '--app', - 'gedit', - '--text', - marker, - '--restore-window', - '--no-screenshot', - '--json' - ]) - ).stdout - ) - expect(typed.result.action?.path).toBe('accessibility') - expect(typed.result.snapshot.treeText).toContain(marker.trim()) - }) - test('paste-text mutates the test-owned document', async () => { await focusGeditDocument() diff --git a/tests/e2e/computer-mac.e2e.ts b/tests/e2e/computer-mac.e2e.ts index 15781119242..2f289b1845a 100644 --- a/tests/e2e/computer-mac.e2e.ts +++ b/tests/e2e/computer-mac.e2e.ts @@ -74,78 +74,6 @@ describe.skipIf(!isMac || !e2eOptIn)('computer-use macOS e2e (TextEdit)', () => }) }) - test('paste-text and hotkey verify TextEdit text replacement', async () => { - const first = parseJsonOutput<{ result: ComputerActionResult }>( - ( - await runOrcaCli([ - 'computer', - 'paste-text', - '--app', - 'TextEdit', - '--text', - 'orca paste first', - '--no-screenshot', - '--json' - ]) - ).stdout - ) - expect(first.result.action?.path).toBe('accessibility') - expect(first.result.action?.verification?.state).toBe('verified') - - const selectAll = parseJsonOutput<{ result: ComputerActionResult }>( - ( - await runOrcaCli([ - 'computer', - 'hotkey', - '--app', - 'TextEdit', - '--key', - 'CmdOrCtrl+A', - '--no-screenshot', - '--json' - ]) - ).stdout - ) - expect(selectAll.result.action?.actionName).toBe('AXSelectAll') - expect(selectAll.result.action?.verification?.state).toBe('verified') - - const marker = `orca paste final ${Date.now()}` - const second = parseJsonOutput<{ result: ComputerActionResult }>( - ( - await runOrcaCli([ - 'computer', - 'paste-text', - '--app', - 'TextEdit', - '--text', - marker, - '--no-screenshot', - '--json' - ]) - ).stdout - ) - expect(second.result.action?.actionName).toBe('AXReplaceSelection') - expect(second.result.action?.verification).toEqual({ - state: 'verified', - property: 'focusedText' - }) - - const after = parseJsonOutput<{ result: ComputerSnapshotResult }>( - ( - await runOrcaCli([ - 'computer', - 'get-app-state', - '--app', - 'TextEdit', - '--no-screenshot', - '--json' - ]) - ).stdout - ) - expect(after.result.snapshot.treeText).toContain(marker) - expect(after.result.snapshot.treeText).not.toContain('orca paste first') - }) - test('accessibility text actions work when TextEdit is not frontmost', async () => { const before = parseJsonOutput<{ result: ComputerSnapshotResult }>( ( diff --git a/tests/e2e/computer-windows.e2e.ts b/tests/e2e/computer-windows.e2e.ts index 1ab45b697cf..88ac6293d68 100644 --- a/tests/e2e/computer-windows.e2e.ts +++ b/tests/e2e/computer-windows.e2e.ts @@ -15,11 +15,7 @@ import { runOrcaCli, stopOrcaRuntime } from './helpers/computer-driver' -import { - editableRolePattern, - focusNotepadDocument, - waitForNotepadText -} from './helpers/notepad-document' +import { editableRolePattern, focusNotepadDocument } from './helpers/notepad-document' import { closeWindowFixture, countMagentaPixelRatio, @@ -40,83 +36,6 @@ describe.skipIf(!isWindows || !e2eOptIn)('computer-use Windows e2e (Notepad)', ( await stopOrcaRuntime() }) - test('list-apps includes the test-owned Notepad process', async () => { - const result = await runOrcaCli(['computer', 'list-apps', '--json']) - const envelope = parseJsonOutput<{ result: ComputerListAppsResult }>(result.stdout) - const pid = Number.parseInt(getNotepadAppSelector().slice(4), 10) - const notepadApp = envelope.result.apps.find((app) => app.pid === pid) - - expect(notepadApp).toMatchObject({ pid, bundleId: 'notepad' }) - expect(notepadApp?.name.toLowerCase()).toBe('notepad') - }) - - test('list-windows returns a targetable Notepad window', async () => { - const app = getNotepadAppSelector() - const result = await runOrcaCli(['computer', 'list-windows', '--app', app, '--json']) - const envelope = parseJsonOutput<{ result: ComputerListWindowsResult }>(result.stdout) - - expect(envelope.result.windows).toHaveLength(1) - expect(envelope.result.windows[0]).toMatchObject({ - index: 0, - app: expect.objectContaining({ pid: Number.parseInt(app.slice(4), 10) }), - id: expect.any(Number), - title: expect.stringContaining('Notepad'), - width: expect.any(Number), - height: expect.any(Number) - }) - }) - - test('Notepad exposes a basic accessibility tree', async () => { - const result = await runOrcaCli([ - 'computer', - 'get-app-state', - '--app', - getNotepadAppSelector(), - '--json' - ]) - const envelope = parseJsonOutput<{ result: ComputerSnapshotResult }>(result.stdout) - - expect(envelope.result.snapshot.app.name.toLowerCase()).toBe('notepad') - expect(envelope.result.snapshot.window.title).toContain('Notepad') - expect(envelope.result.snapshot.elementCount).toBeGreaterThan(0) - expect(envelope.result.snapshot.coordinateSpace).toBe('window') - expect(envelope.result.snapshot.truncation?.truncated).toBe(false) - expect(envelope.result.screenshotStatus.state).toBe('captured') - expect(envelope.result.screenshot?.format).toBe('png') - expect(envelope.result.screenshot?.data).toBeUndefined() - expect(envelope.result.screenshot?.dataOmitted).toBe(true) - expect(envelope.result.screenshot?.path).toContain('orca-computer-use') - }) - - test('paste-text mutates the test-owned document', async () => { - const app = getNotepadAppSelector() - const marker = `orca-windows-paste-${Date.now()}` - await focusNotepadDocument(app) - const action = parseJsonOutput<{ result: ComputerActionResult }>( - ( - await runOrcaCli([ - 'computer', - 'paste-text', - '--app', - app, - '--text', - marker, - '--restore-window', - '--no-screenshot', - '--json' - ]) - ).stdout - ) - expect(action.result.action?.path).toBe('clipboard') - expect(action.result.action?.verification).toMatchObject({ - state: 'unverified', - reason: 'clipboard_paste' - }) - - const after = await waitForNotepadText(app, marker) - expect(after.snapshot.treeText).toContain(marker) - }) - test('Unicode payloads survive paste-text', async () => { const app = getNotepadAppSelector() const unicode = `orca unicode café Ω 漢字 ${Date.now()}` @@ -155,22 +74,17 @@ describe.skipIf(!isWindows || !e2eOptIn)('computer-use Windows e2e (Notepad)', ( '--json' ]) - const selectAll = parseJsonOutput<{ result: ComputerActionResult }>( - ( - await runOrcaCli([ - 'computer', - 'hotkey', - '--app', - app, - '--key', - 'CmdOrCtrl+A', - '--restore-window', - '--no-screenshot', - '--json' - ]) - ).stdout - ) - expect(selectAll.result.action?.actionName).toBe('hotkey') + await runOrcaCli([ + 'computer', + 'hotkey', + '--app', + app, + '--key', + 'CmdOrCtrl+A', + '--restore-window', + '--no-screenshot', + '--json' + ]) const marker = `orca-windows-replaced-${Date.now()}` const second = parseJsonOutput<{ result: ComputerActionResult }>( @@ -272,10 +186,9 @@ describe.skipIf(!isWindows || !e2eOptIn)('computer-use Windows e2e (Notepad)', ( } }) - test('a hung window fails fast with a timeout error and does not stall later calls', async () => { + test('a hung window reports a timeout error and later calls still succeed', async () => { const hung = await launchHungWindowFixture() try { - const started = Date.now() const failure = await runOrcaCli([ 'computer', 'get-app-state', @@ -289,8 +202,6 @@ describe.skipIf(!isWindows || !e2eOptIn)('computer-use Windows e2e (Notepad)', ( ) expect(failure).toMatch(/window_not_responding/) expect(failure).toMatch(/"code":\s*"action_timeout"/) - // The provider request timeout is 30 s; the hang probe answers within its 5 s bound. - expect(Date.now() - started).toBeLessThan(15_000) const apps = parseJsonOutput<{ result: ComputerListAppsResult }>( (await runOrcaCli(['computer', 'list-apps', '--json'])).stdout diff --git a/tests/e2e/helpers/notepad-document.ts b/tests/e2e/helpers/notepad-document.ts index 3b734cecf1b..b3f1b31c9de 100644 --- a/tests/e2e/helpers/notepad-document.ts +++ b/tests/e2e/helpers/notepad-document.ts @@ -1,28 +1,8 @@ -import { expect, vi } from 'vitest' +import { expect } from 'vitest' import type { ComputerSnapshotResult } from '../../../src/shared/runtime-types' import { findRoleIndex, parseJsonOutput, runOrcaCli } from './computer-driver' export const editableRolePattern = /^\s*(\d+)\s+(document|edit|text|pane)(?:\s|$)/im -const pasteMutationTimeoutMs = 5_000 - -// Poll after clipboard paste because Notepad’s accessibility snapshot can lag the visible change. -export async function waitForNotepadText( - app: string, - marker: string -): Promise { - return vi.waitFor( - async () => { - const snapshot = parseJsonOutput<{ result: ComputerSnapshotResult }>( - (await runOrcaCli(['computer', 'get-app-state', '--app', app, '--no-screenshot', '--json'])) - .stdout - ).result - expect(snapshot.snapshot.treeText).toContain(marker) - return snapshot - }, - { timeout: pasteMutationTimeoutMs, interval: 150 } - ) -} - export async function focusNotepadDocument(app: string): Promise { const snapshot = parseJsonOutput<{ result: ComputerSnapshotResult }>( ( From 1f37db301cf341dfe852d7baaf32a296febe8c2c Mon Sep 17 00:00:00 2001 From: "buf0-bot[bot]" <252831055+buf0-bot[bot]@users.noreply.github.com> Date: Fri, 13 Nov 2026 12:25:56 +0000 Subject: [PATCH 085/178] Update README downloads badge (#42012) Co-authored-by: buf0-bot[bot] <252831055+buf0-bot[bot]@users.noreply.github.com> --- docs/assets/readme-downloads.svg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/assets/readme-downloads.svg b/docs/assets/readme-downloads.svg index b7020a28499..c085e4dac9a 100644 --- a/docs/assets/readme-downloads.svg +++ b/docs/assets/readme-downloads.svg @@ -1,5 +1,5 @@ - - downloads: 175m + + downloads: 176m @@ -15,7 +15,7 @@ downloads downloads - 175m - 175m + 176m + 176m From e81a89c5ffe3e8bf8abcc962630ee1fdbc2139ad Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 13 Nov 2026 04:31:03 -0800 Subject: [PATCH 086/178] refactor: group Jira and Linear task-page modules by domain (#42009) * refactor: group Jira and Linear task-page modules by domain * refactor: remove orphaned Linear refresh test entry --- config/focus-setting-read-baseline.txt | 20 +- config/react-compiler-bailout-baseline.txt | 36 +- config/react-compiler-uncompiled-files.txt | 2 +- .../scripts/ci/sharding/ci-shard-timings.json | 23 +- .../check-changed-code-quality.mjs | 2 +- config/tsconfig.tc.web.json | 1 - config/tsconfig.test.pane-launch.json | 1 - config/tsconfig.web.json | 1 - package.json | 2 +- .../task-page-jira-favourite-fixture.ts | 119 ----- .../src/components/jira-key-or-text-search.ts | 2 +- .../src/components/task-page/Surface.tsx | 6 +- .../src/components/task-page/TaskPage.tsx | 30 +- .../github/issue-status/StatusCell.tsx | 2 +- .../github/listing/AssigneesCell.tsx | 2 +- .../use-task-page-github-list-pending.ts | 2 +- .../use-task-page-github-list-projection.ts | 2 +- ...sk-page-github-work-item-mutation-types.ts | 23 + .../use-task-page-github-mutation-state.ts | 2 +- .../github/pull-requests/MergeCell.tsx | 2 +- .../github/pull-requests/ReviewCell.tsx | 2 +- .../task-page-github-reviewer-actions.ts | 2 +- .../use-task-page-pr-check-hydration.ts | 2 +- .../src/components/task-page/jira/Content.tsx | 12 +- .../src/components/task-page/jira/Filters.tsx | 6 +- .../jira/IssueAssigneeField.test.tsx | 68 --- .../task-page-jira-board-move-dialog.tsx | 6 +- .../{ => boards}/task-page-jira-board-move.ts | 4 +- .../task-page-jira-board-sections.ts | 4 +- .../task-page-jira-board-transitions.ts | 4 +- .../task-page-jira-board-view.tsx | 12 +- .../{ => boards}/task-page-jira-board.tsx | 2 +- .../{ => creation}/IssueAssigneeField.tsx | 4 +- .../jira/{ => creation}/IssueDialog.tsx | 4 +- .../jira/{ => creation}/IssueTitleField.tsx | 2 +- .../jira/{ => creation}/IssueUserField.tsx | 2 +- .../task-page-jira-create-fields.ts | 2 +- .../use-task-page-jira-creation-metadata.ts | 0 .../use-task-page-jira-creation-projects.ts | 4 +- .../use-task-page-jira-creation-state.ts | 6 +- .../use-task-page-jira-issue-creation.ts | 4 +- ...task-page-jira-summary-generation.test.tsx | 4 +- .../use-task-page-jira-summary-generation.ts | 8 +- .../TaskPageJiraFavouriteFilters.tsx | 2 +- .../TaskPageJiraSavedFilters.tsx | 4 +- .../TaskPageJiraStatusFilters.tsx | 6 +- .../task-page-jira-status-catalog.ts | 6 +- .../use-task-page-jira-favourite-filters.ts | 4 +- .../use-task-page-jira-project-scope.test.tsx | 2 +- .../use-task-page-jira-project-scope.ts | 13 +- .../use-task-page-jira-quick-filters.test.tsx | 59 +++ .../use-task-page-jira-quick-filters.ts | 4 +- .../use-task-page-jira-saved-filters.test.tsx | 58 +++ .../use-task-page-jira-saved-filters.ts | 6 +- .../task-page-jira-cache-selectors.ts | 4 +- .../listing/task-page-jira-error-banner.tsx | 52 ++ .../task-page-jira-issue-cells.tsx | 4 +- .../task-page-jira-issue-list.tsx | 10 +- .../task-page-jira-linked-issues.ts | 6 +- .../task-page-jira-load-state.ts | 4 +- .../{ => listing}/task-page-jira-search.ts | 9 +- .../task-page-jira-sort-controls.tsx | 4 +- .../task-page-jira-status-order.ts | 4 +- .../task-page-jira-status-tone.ts | 0 .../task-page-jira-text-fallback-notice.tsx | 0 .../use-task-page-jira-list-effects.ts | 20 +- .../use-task-page-jira-list-projection.ts | 8 +- .../task-page-jira-board-transitions.test.ts | 93 ---- ...ask-page-jira-favourite-selection.test.tsx | 89 ---- .../jira/task-page-jira-grouping.test.ts | 93 ---- .../jira/task-page-jira-quick-copy.test.tsx | 122 ----- .../jira/use-task-page-jira-list-state.ts | 14 +- .../use-task-page-jira-quick-filters.test.tsx | 114 ----- .../use-task-page-jira-saved-filters.test.tsx | 149 ------ .../components/task-page/linear/Content.tsx | 10 +- .../components/task-page/linear/IssueRows.tsx | 9 - .../task-page/linear/IssueTable.test.tsx | 83 ---- .../linear/{ => creation}/IssueAttributes.tsx | 2 +- .../linear/{ => creation}/IssueDialog.tsx | 2 +- .../linear/{ => creation}/ProjectDialog.tsx | 2 +- .../linear/{ => creation}/ProjectFields.tsx | 2 +- .../linear-attribute-count-labels.ts | 0 .../use-task-page-linear-creation-state.ts | 6 +- .../use-task-page-linear-issue-creation.ts | 2 +- .../use-task-page-linear-project-creation.ts | 2 +- ...ask-page-linear-refresh-lifecycle.test.tsx | 58 +++ ...task-page-linear-refresh-test-fixtures.tsx | 210 +++++++++ .../linear/{ => issues}/IssueBoard.tsx | 8 +- .../linear/{ => issues}/IssueBoardColumns.tsx | 6 +- .../linear/{ => issues}/IssueList.tsx | 8 +- .../linear/{ => issues}/IssueTable.tsx | 6 +- .../linear/{ => issues}/IssueToolbar.tsx | 4 +- .../task-page-linear-cache-selectors.ts | 6 +- .../task-page-linear-in-orca-issues.test.ts | 0 .../task-page-linear-in-orca-issues.ts | 14 +- .../task-page-linear-issue-empty-state.ts | 2 +- .../issues/task-page-linear-issue-grouping.ts | 109 +++++ .../task-page-linear-issue-request.ts | 6 +- .../task-page-linear-state-cell.tsx} | 90 +--- .../task-page-linear-team-selection.ts | 2 +- .../use-task-page-linear-board.ts | 36 +- .../use-task-page-linear-filter-selection.ts | 8 +- .../use-task-page-linear-in-orca-effects.ts | 4 +- .../use-task-page-linear-list-effects.ts | 22 +- .../use-task-page-linear-list-presentation.ts | 42 +- .../use-task-page-linear-list-projection.ts | 8 +- .../use-task-page-linear-list-selection.ts | 10 +- .../linear/{ => projects}/ProjectList.tsx | 2 +- .../linear/{ => projects}/ProjectOverview.tsx | 2 +- ...use-task-page-linear-collection-effects.ts | 10 +- ...page-linear-committed-publication.test.tsx | 361 -------------- .../task-page-linear-detail-query.test.tsx | 118 ----- .../task-page-linear-jira-list-model.tsx | 199 -------- .../task-page-linear-refresh-entry.test.tsx | 190 -------- ...ask-page-linear-refresh-lifecycle.test.tsx | 197 -------- ...task-page-linear-refresh-test-fixtures.tsx | 446 ------------------ .../task-page-linear-refresh-writers.test.tsx | 140 ------ ...k-page-linear-selection-decisions.test.tsx | 98 ---- .../linear/use-task-page-linear-view-state.ts | 8 +- .../linear/{ => views}/CustomViewList.tsx | 2 +- .../linear/{ => views}/CustomViewProjects.tsx | 2 +- ...se-task-page-linear-custom-view-effects.ts | 42 +- .../use-task-page-composer-actions.ts | 2 +- .../task-page/use-task-page-detail-routing.ts | 2 +- .../task-page/use-task-page-global-effects.ts | 2 +- .../task-page/use-task-page-source-display.ts | 2 +- .../use-task-creation-draft-retention.test.ts | 401 +--------------- ...ask-page-linear-refresh-authority.test.tsx | 119 ----- ...page-linear-refresh-authority.unit.test.ts | 2 - 129 files changed, 958 insertions(+), 3575 deletions(-) delete mode 100644 src/renderer/src/components/__mocks__/task-page-jira-favourite-fixture.ts delete mode 100644 src/renderer/src/components/task-page/jira/IssueAssigneeField.test.tsx rename src/renderer/src/components/task-page/jira/{ => boards}/task-page-jira-board-move-dialog.tsx (95%) rename src/renderer/src/components/task-page/jira/{ => boards}/task-page-jira-board-move.ts (89%) rename src/renderer/src/components/task-page/jira/{ => boards}/task-page-jira-board-sections.ts (95%) rename src/renderer/src/components/task-page/jira/{ => boards}/task-page-jira-board-transitions.ts (92%) rename src/renderer/src/components/task-page/jira/{ => boards}/task-page-jira-board-view.tsx (94%) rename src/renderer/src/components/task-page/jira/{ => boards}/task-page-jira-board.tsx (99%) rename src/renderer/src/components/task-page/jira/{ => creation}/IssueAssigneeField.tsx (96%) rename src/renderer/src/components/task-page/jira/{ => creation}/IssueDialog.tsx (98%) rename src/renderer/src/components/task-page/jira/{ => creation}/IssueTitleField.tsx (97%) rename src/renderer/src/components/task-page/jira/{ => creation}/IssueUserField.tsx (92%) rename src/renderer/src/components/task-page/jira/{ => creation}/task-page-jira-create-fields.ts (98%) rename src/renderer/src/components/task-page/jira/{ => creation}/use-task-page-jira-creation-metadata.ts (100%) rename src/renderer/src/components/task-page/jira/{ => creation}/use-task-page-jira-creation-projects.ts (98%) rename src/renderer/src/components/task-page/jira/{ => creation}/use-task-page-jira-creation-state.ts (97%) rename src/renderer/src/components/task-page/jira/{ => creation}/use-task-page-jira-issue-creation.ts (96%) rename src/renderer/src/components/task-page/jira/{ => creation}/use-task-page-jira-summary-generation.test.tsx (98%) rename src/renderer/src/components/task-page/jira/{ => creation}/use-task-page-jira-summary-generation.ts (96%) rename src/renderer/src/components/task-page/jira/{ => filters}/TaskPageJiraFavouriteFilters.tsx (98%) rename src/renderer/src/components/task-page/jira/{ => filters}/TaskPageJiraSavedFilters.tsx (98%) rename src/renderer/src/components/task-page/jira/{ => filters}/TaskPageJiraStatusFilters.tsx (94%) rename src/renderer/src/components/task-page/jira/{ => filters}/task-page-jira-status-catalog.ts (87%) rename src/renderer/src/components/task-page/jira/{ => filters}/use-task-page-jira-favourite-filters.ts (96%) rename src/renderer/src/components/task-page/jira/{ => filters}/use-task-page-jira-project-scope.test.tsx (97%) rename src/renderer/src/components/task-page/jira/{ => filters}/use-task-page-jira-project-scope.ts (79%) create mode 100644 src/renderer/src/components/task-page/jira/filters/use-task-page-jira-quick-filters.test.tsx rename src/renderer/src/components/task-page/jira/{ => filters}/use-task-page-jira-quick-filters.ts (96%) create mode 100644 src/renderer/src/components/task-page/jira/filters/use-task-page-jira-saved-filters.test.tsx rename src/renderer/src/components/task-page/jira/{ => filters}/use-task-page-jira-saved-filters.ts (96%) rename src/renderer/src/components/task-page/jira/{ => listing}/task-page-jira-cache-selectors.ts (91%) create mode 100644 src/renderer/src/components/task-page/jira/listing/task-page-jira-error-banner.tsx rename src/renderer/src/components/task-page/jira/{ => listing}/task-page-jira-issue-cells.tsx (97%) rename src/renderer/src/components/task-page/jira/{ => listing}/task-page-jira-issue-list.tsx (97%) rename src/renderer/src/components/task-page/jira/{ => listing}/task-page-jira-linked-issues.ts (89%) rename src/renderer/src/components/task-page/jira/{ => listing}/task-page-jira-load-state.ts (95%) rename src/renderer/src/components/task-page/jira/{ => listing}/task-page-jira-search.ts (87%) rename src/renderer/src/components/task-page/jira/{ => listing}/task-page-jira-sort-controls.tsx (98%) rename src/renderer/src/components/task-page/jira/{ => listing}/task-page-jira-status-order.ts (95%) rename src/renderer/src/components/task-page/jira/{ => listing}/task-page-jira-status-tone.ts (100%) rename src/renderer/src/components/task-page/jira/{ => listing}/task-page-jira-text-fallback-notice.tsx (100%) rename src/renderer/src/components/task-page/jira/{ => listing}/use-task-page-jira-list-effects.ts (92%) rename src/renderer/src/components/task-page/jira/{ => listing}/use-task-page-jira-list-projection.ts (93%) delete mode 100644 src/renderer/src/components/task-page/jira/task-page-jira-board-transitions.test.ts delete mode 100644 src/renderer/src/components/task-page/jira/task-page-jira-favourite-selection.test.tsx delete mode 100644 src/renderer/src/components/task-page/jira/task-page-jira-grouping.test.ts delete mode 100644 src/renderer/src/components/task-page/jira/task-page-jira-quick-copy.test.tsx delete mode 100644 src/renderer/src/components/task-page/jira/use-task-page-jira-quick-filters.test.tsx delete mode 100644 src/renderer/src/components/task-page/jira/use-task-page-jira-saved-filters.test.tsx delete mode 100644 src/renderer/src/components/task-page/linear/IssueRows.tsx delete mode 100644 src/renderer/src/components/task-page/linear/IssueTable.test.tsx rename src/renderer/src/components/task-page/linear/{ => creation}/IssueAttributes.tsx (99%) rename src/renderer/src/components/task-page/linear/{ => creation}/IssueDialog.tsx (98%) rename src/renderer/src/components/task-page/linear/{ => creation}/ProjectDialog.tsx (98%) rename src/renderer/src/components/task-page/linear/{ => creation}/ProjectFields.tsx (99%) rename src/renderer/src/components/task-page/linear/{ => creation}/linear-attribute-count-labels.ts (100%) rename src/renderer/src/components/task-page/linear/{ => creation}/use-task-page-linear-creation-state.ts (98%) rename src/renderer/src/components/task-page/linear/{ => creation}/use-task-page-linear-issue-creation.ts (99%) rename src/renderer/src/components/task-page/linear/{ => creation}/use-task-page-linear-project-creation.ts (97%) create mode 100644 src/renderer/src/components/task-page/linear/issue-refresh/task-page-linear-refresh-lifecycle.test.tsx create mode 100644 src/renderer/src/components/task-page/linear/issue-refresh/task-page-linear-refresh-test-fixtures.tsx rename src/renderer/src/components/task-page/linear/{ => issues}/IssueBoard.tsx (96%) rename src/renderer/src/components/task-page/linear/{ => issues}/IssueBoardColumns.tsx (98%) rename src/renderer/src/components/task-page/linear/{ => issues}/IssueList.tsx (95%) rename src/renderer/src/components/task-page/linear/{ => issues}/IssueTable.tsx (98%) rename src/renderer/src/components/task-page/linear/{ => issues}/IssueToolbar.tsx (98%) rename src/renderer/src/components/task-page/linear/{ => issues}/task-page-linear-cache-selectors.ts (84%) rename src/renderer/src/components/task-page/linear/{ => issues}/task-page-linear-in-orca-issues.test.ts (100%) rename src/renderer/src/components/task-page/linear/{ => issues}/task-page-linear-in-orca-issues.ts (93%) rename src/renderer/src/components/task-page/linear/{ => issues}/task-page-linear-issue-empty-state.ts (94%) create mode 100644 src/renderer/src/components/task-page/linear/issues/task-page-linear-issue-grouping.ts rename src/renderer/src/components/task-page/linear/{ => issues}/task-page-linear-issue-request.ts (92%) rename src/renderer/src/components/task-page/linear/{task-page-linear-issue-model.tsx => issues/task-page-linear-state-cell.tsx} (69%) rename src/renderer/src/components/task-page/linear/{ => issues}/task-page-linear-team-selection.ts (91%) rename src/renderer/src/components/task-page/linear/{ => issues}/use-task-page-linear-board.ts (88%) rename src/renderer/src/components/task-page/linear/{ => issues}/use-task-page-linear-filter-selection.ts (92%) rename src/renderer/src/components/task-page/linear/{ => issues}/use-task-page-linear-in-orca-effects.ts (96%) rename src/renderer/src/components/task-page/linear/{ => issues}/use-task-page-linear-list-effects.ts (92%) rename src/renderer/src/components/task-page/linear/{ => issues}/use-task-page-linear-list-presentation.ts (76%) rename src/renderer/src/components/task-page/linear/{ => issues}/use-task-page-linear-list-projection.ts (97%) rename src/renderer/src/components/task-page/linear/{ => issues}/use-task-page-linear-list-selection.ts (96%) rename src/renderer/src/components/task-page/linear/{ => projects}/ProjectList.tsx (96%) rename src/renderer/src/components/task-page/linear/{ => projects}/ProjectOverview.tsx (96%) rename src/renderer/src/components/task-page/linear/{ => projects}/use-task-page-linear-collection-effects.ts (93%) delete mode 100644 src/renderer/src/components/task-page/linear/task-page-linear-committed-publication.test.tsx delete mode 100644 src/renderer/src/components/task-page/linear/task-page-linear-detail-query.test.tsx delete mode 100644 src/renderer/src/components/task-page/linear/task-page-linear-jira-list-model.tsx delete mode 100644 src/renderer/src/components/task-page/linear/task-page-linear-refresh-entry.test.tsx delete mode 100644 src/renderer/src/components/task-page/linear/task-page-linear-refresh-lifecycle.test.tsx delete mode 100644 src/renderer/src/components/task-page/linear/task-page-linear-refresh-test-fixtures.tsx delete mode 100644 src/renderer/src/components/task-page/linear/task-page-linear-refresh-writers.test.tsx delete mode 100644 src/renderer/src/components/task-page/linear/task-page-linear-selection-decisions.test.tsx rename src/renderer/src/components/task-page/linear/{ => views}/CustomViewList.tsx (96%) rename src/renderer/src/components/task-page/linear/{ => views}/CustomViewProjects.tsx (97%) rename src/renderer/src/components/task-page/linear/{ => views}/use-task-page-linear-custom-view-effects.ts (86%) delete mode 100644 tests/e2e/task-page-linear-refresh-authority.test.tsx delete mode 100644 tests/e2e/task-page-linear-refresh-authority.unit.test.ts diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index 84559a65d66..491e47fa748 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -156,18 +156,18 @@ 1 src/renderer/src/components/tab-bar/windows-shell-menu-visibility.ts 3 src/renderer/src/components/tab-group/useTabGroupCreationCommands.ts 11 src/renderer/src/components/task-page/TaskPage.tsx -2 src/renderer/src/components/task-page/jira/IssueAssigneeField.tsx -1 src/renderer/src/components/task-page/jira/task-page-jira-status-order.ts -4 src/renderer/src/components/task-page/jira/use-task-page-jira-creation-metadata.ts -4 src/renderer/src/components/task-page/jira/use-task-page-jira-issue-creation.ts -1 src/renderer/src/components/task-page/jira/use-task-page-jira-list-effects.ts +2 src/renderer/src/components/task-page/jira/creation/IssueAssigneeField.tsx +1 src/renderer/src/components/task-page/jira/listing/task-page-jira-status-order.ts +4 src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-metadata.ts +4 src/renderer/src/components/task-page/jira/creation/use-task-page-jira-issue-creation.ts +1 src/renderer/src/components/task-page/jira/listing/use-task-page-jira-list-effects.ts 1 src/renderer/src/components/task-page/linear/Filters.tsx -4 src/renderer/src/components/task-page/linear/task-page-linear-issue-model.tsx -8 src/renderer/src/components/task-page/linear/use-task-page-linear-board.ts +4 src/renderer/src/components/task-page/linear/issues/task-page-linear-state-cell.tsx +8 src/renderer/src/components/task-page/linear/issues/use-task-page-linear-board.ts 4 src/renderer/src/components/task-page/linear/use-task-page-linear-context-state.ts -6 src/renderer/src/components/task-page/linear/use-task-page-linear-creation-state.ts -4 src/renderer/src/components/task-page/linear/use-task-page-linear-issue-creation.ts -2 src/renderer/src/components/task-page/linear/use-task-page-linear-project-creation.ts +6 src/renderer/src/components/task-page/linear/creation/use-task-page-linear-creation-state.ts +4 src/renderer/src/components/task-page/linear/creation/use-task-page-linear-issue-creation.ts +2 src/renderer/src/components/task-page/linear/creation/use-task-page-linear-project-creation.ts 5 src/renderer/src/components/task-page/linear/use-task-page-linear-view-state.ts 3 src/renderer/src/components/task-page/use-task-page-detail-routing.ts 2 src/renderer/src/components/task-page/use-task-page-provider-metadata.ts diff --git a/config/react-compiler-bailout-baseline.txt b/config/react-compiler-bailout-baseline.txt index a52ba10321a..62b0b0c36d6 100644 --- a/config/react-compiler-bailout-baseline.txt +++ b/config/react-compiler-bailout-baseline.txt @@ -592,26 +592,26 @@ 3 src/renderer/src/components/task-page/github/listing/use-task-page-github-search-pagination.ts 4 src/renderer/src/components/task-page/github/cache/use-task-page-work-items-cache-selection.ts 1 src/renderer/src/components/task-page/gitlab/use-task-page-gitlab-loading.ts -2 src/renderer/src/components/task-page/jira/task-page-jira-board-view.tsx -1 src/renderer/src/components/task-page/jira/use-task-page-jira-creation-projects.ts -33 src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts -2 src/renderer/src/components/task-page/jira/use-task-page-jira-favourite-filters.ts -2 src/renderer/src/components/task-page/jira/use-task-page-jira-issue-creation.ts -1 src/renderer/src/components/task-page/jira/use-task-page-jira-list-effects.ts -2 src/renderer/src/components/task-page/jira/use-task-page-jira-quick-filters.ts -2 src/renderer/src/components/task-page/jira/use-task-page-jira-saved-filters.ts -2 src/renderer/src/components/task-page/jira/use-task-page-jira-summary-generation.ts -1 src/renderer/src/components/task-page/linear/IssueTable.tsx -7 src/renderer/src/components/task-page/linear/use-task-page-linear-board.ts +2 src/renderer/src/components/task-page/jira/boards/task-page-jira-board-view.tsx +1 src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-projects.ts +33 src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-state.ts +2 src/renderer/src/components/task-page/jira/filters/use-task-page-jira-favourite-filters.ts +2 src/renderer/src/components/task-page/jira/creation/use-task-page-jira-issue-creation.ts +1 src/renderer/src/components/task-page/jira/listing/use-task-page-jira-list-effects.ts +2 src/renderer/src/components/task-page/jira/filters/use-task-page-jira-quick-filters.ts +2 src/renderer/src/components/task-page/jira/filters/use-task-page-jira-saved-filters.ts +2 src/renderer/src/components/task-page/jira/creation/use-task-page-jira-summary-generation.ts +1 src/renderer/src/components/task-page/linear/issues/IssueTable.tsx +7 src/renderer/src/components/task-page/linear/issues/use-task-page-linear-board.ts 3 src/renderer/src/components/task-page/linear/use-task-page-linear-context-actions.ts 1 src/renderer/src/components/task-page/linear/use-task-page-linear-context-state.ts -4 src/renderer/src/components/task-page/linear/use-task-page-linear-filter-selection.ts -1 src/renderer/src/components/task-page/linear/use-task-page-linear-issue-creation.ts -1 src/renderer/src/components/task-page/linear/use-task-page-linear-list-effects.ts -6 src/renderer/src/components/task-page/linear/use-task-page-linear-list-presentation.ts -13 src/renderer/src/components/task-page/linear/use-task-page-linear-list-projection.ts -20 src/renderer/src/components/task-page/linear/use-task-page-linear-list-selection.ts -2 src/renderer/src/components/task-page/linear/use-task-page-linear-project-creation.ts +4 src/renderer/src/components/task-page/linear/issues/use-task-page-linear-filter-selection.ts +1 src/renderer/src/components/task-page/linear/creation/use-task-page-linear-issue-creation.ts +1 src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-effects.ts +6 src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-presentation.ts +13 src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-projection.ts +20 src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-selection.ts +2 src/renderer/src/components/task-page/linear/creation/use-task-page-linear-project-creation.ts 1 src/renderer/src/components/task-page/linear/use-task-page-linear-view-state.ts 10 src/renderer/src/components/task-page/use-task-page-composer-actions.ts 3 src/renderer/src/components/task-page/use-task-page-detail-routing.ts diff --git a/config/react-compiler-uncompiled-files.txt b/config/react-compiler-uncompiled-files.txt index a78f7b1b07a..c1570bdeeab 100644 --- a/config/react-compiler-uncompiled-files.txt +++ b/config/react-compiler-uncompiled-files.txt @@ -13,7 +13,7 @@ src/renderer/src/components/settings/skills/linear/LinearAgentSkillPane.tsx src/renderer/src/components/settings/tasks/use-task-source-provider-readiness.ts src/renderer/src/components/sidebar/workspace-card/details/use-worktree-card-review-details.ts src/renderer/src/components/task-page/jira/Content.tsx -src/renderer/src/components/task-page/jira/use-task-page-jira-list-projection.ts +src/renderer/src/components/task-page/jira/listing/use-task-page-jira-list-projection.ts src/renderer/src/components/task-page/jira/use-task-page-jira-list-state.ts src/renderer/src/hooks/use-integration-provider-status-refresh.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 081ff049122..0618b09dd67 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -5092,26 +5092,16 @@ "src/renderer/src/components/task-page/github/use-task-page-github-issue-creation.test.tsx": 1769, "src/renderer/src/components/task-page/gitlab/use-task-page-gitlab-loading.test.tsx": 1504, "src/renderer/src/components/task-page/jira/Content.board-owner.test.tsx": 2582, - "src/renderer/src/components/task-page/jira/IssueAssigneeField.test.tsx": 1122, "src/renderer/src/components/task-page/jira/task-page-jira-board-detail-integration.test.tsx": 2192, - "src/renderer/src/components/task-page/jira/task-page-jira-board-transitions.test.ts": 69, "src/renderer/src/components/task-page/jira/task-page-jira-board-view.test.tsx": 2287, - "src/renderer/src/components/task-page/jira/task-page-jira-favourite-selection.test.tsx": 3326, - "src/renderer/src/components/task-page/jira/task-page-jira-grouping.test.ts": 2324, "src/renderer/src/components/task-page/jira/task-page-jira-linked-issues.test.ts": 204, - "src/renderer/src/components/task-page/jira/task-page-jira-quick-copy.test.tsx": 3457, - "src/renderer/src/components/task-page/jira/use-task-page-jira-project-scope.test.tsx": 2642, - "src/renderer/src/components/task-page/jira/use-task-page-jira-quick-filters.test.tsx": 1466, - "src/renderer/src/components/task-page/jira/use-task-page-jira-saved-filters.test.tsx": 1501, - "src/renderer/src/components/task-page/jira/use-task-page-jira-summary-generation.test.tsx": 571, - "src/renderer/src/components/task-page/linear/IssueTable.test.tsx": 11230, - "src/renderer/src/components/task-page/linear/task-page-linear-committed-publication.test.tsx": 1978, + "src/renderer/src/components/task-page/jira/filters/use-task-page-jira-project-scope.test.tsx": 2642, + "src/renderer/src/components/task-page/jira/filters/use-task-page-jira-quick-filters.test.tsx": 1466, + "src/renderer/src/components/task-page/jira/filters/use-task-page-jira-saved-filters.test.tsx": 1501, + "src/renderer/src/components/task-page/jira/creation/use-task-page-jira-summary-generation.test.tsx": 571, "src/renderer/src/components/task-page/linear/task-page-linear-creation-lifetime.test.tsx": 3315, - "src/renderer/src/components/task-page/linear/task-page-linear-in-orca-issues.test.ts": 430, - "src/renderer/src/components/task-page/linear/task-page-linear-refresh-entry.test.tsx": 2217, - "src/renderer/src/components/task-page/linear/task-page-linear-refresh-lifecycle.test.tsx": 1814, - "src/renderer/src/components/task-page/linear/task-page-linear-refresh-writers.test.tsx": 2810, - "src/renderer/src/components/task-page/linear/task-page-linear-selection-decisions.test.tsx": 1018, + "src/renderer/src/components/task-page/linear/issues/task-page-linear-in-orca-issues.test.ts": 430, + "src/renderer/src/components/task-page/linear/issue-refresh/task-page-linear-refresh-lifecycle.test.tsx": 1814, "src/renderer/src/components/task-page/task-page-cache-selectors.test.ts": 108, "src/renderer/src/components/task-page/task-page-initial-selection-scaling.test.tsx": 1953, "src/renderer/src/components/task-page/task-page-work-item-pagination.test.ts": 63, @@ -6940,7 +6930,6 @@ "tests/e2e/structured-attention-read-retirement.unit.test.ts": 6109, "tests/e2e/structured-chat-tab-lifetime.unit.test.ts": 5616, "tests/e2e/sustained-agent-typing-load-scripts.unit.test.ts": 2169, - "tests/e2e/task-page-linear-refresh-authority.unit.test.ts": 5548, "tests/e2e/terminal-perf-presentation.unit.test.ts": 60, "tests/e2e/worktree-status-warning.unit.test.ts": 2331, "tests/tools/omp-close-lifecycle.test.mjs": 403, diff --git a/config/scripts/preflight/changed-checks/check-changed-code-quality.mjs b/config/scripts/preflight/changed-checks/check-changed-code-quality.mjs index 00753773695..2052fc083a1 100644 --- a/config/scripts/preflight/changed-checks/check-changed-code-quality.mjs +++ b/config/scripts/preflight/changed-checks/check-changed-code-quality.mjs @@ -77,7 +77,7 @@ const SUPPRESSED_REACT_DOCTOR_DIAGNOSTICS = new Map([ 'react-doctor(no-adjust-state-on-prop-change)', new Set([ 'src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-draft.ts', - 'src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts' + 'src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-state.ts' ]) ], [ diff --git a/config/tsconfig.tc.web.json b/config/tsconfig.tc.web.json index 95529130196..e8b9f6350b4 100644 --- a/config/tsconfig.tc.web.json +++ b/config/tsconfig.tc.web.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.web.json", "exclude": [ - "../src/renderer/src/components/task-page/linear/task-page-linear-detail-query.test.tsx", "../src/renderer/src/runtime/provider-accounts/observations/provider-account-observation.test-fixture.ts", "../src/shared/**/*.test.ts", "../src/shared/**/*.test.tsx", diff --git a/config/tsconfig.test.pane-launch.json b/config/tsconfig.test.pane-launch.json index cdffd47c2df..cff73d0a79e 100644 --- a/config/tsconfig.test.pane-launch.json +++ b/config/tsconfig.test.pane-launch.json @@ -2,7 +2,6 @@ "extends": "./tsconfig.web.json", "include": [ "../src/main/runtime/orchestration/terminal-authority/structured-sessions/structured-chat-coordinator-mail.test.ts", - "../src/renderer/src/components/task-page/linear/task-page-linear-detail-query.test.tsx", "../src/renderer/src/runtime/observed-provider-accounts.test.tsx", "../src/renderer/src/components/status-bar/ClaudeSwitcherMenu.observation.test.tsx", "../src/renderer/src/runtime/provider-accounts/observations/provider-account-observation.test-fixture.ts", diff --git a/config/tsconfig.web.json b/config/tsconfig.web.json index b8af840abf1..d34ab3969b3 100644 --- a/config/tsconfig.web.json +++ b/config/tsconfig.web.json @@ -11,7 +11,6 @@ "../src/shared/**/*" ], "exclude": [ - "../src/renderer/src/components/task-page/linear/task-page-linear-detail-query.test.tsx", "../src/renderer/src/runtime/provider-accounts/observations/provider-account-observation.test-fixture.ts" ], "compilerOptions": { diff --git a/package.json b/package.json index cf4dd2f8e49..c022ea14cf1 100644 --- a/package.json +++ b/package.json @@ -441,7 +441,7 @@ { "files": [ "src/renderer/src/components/task-page/github/issue-creation/use-task-page-github-issue-draft.ts", - "src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts" + "src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-state.ts" ], "rules": [ "react-doctor/no-adjust-state-on-prop-change" diff --git a/src/renderer/src/components/__mocks__/task-page-jira-favourite-fixture.ts b/src/renderer/src/components/__mocks__/task-page-jira-favourite-fixture.ts deleted file mode 100644 index b724951f850..00000000000 --- a/src/renderer/src/components/__mocks__/task-page-jira-favourite-fixture.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { useMemo, useRef } from 'react' -import { vi, type Mock } from 'vitest' -import type { ExecutionHostId } from '../../../../shared/execution-host' -import type { JiraIssue } from '../../../../shared/jira/jira-types' -import type { JiraSavedFilterState } from '../../../../shared/jira/jira-saved-filters' -import type { TaskPageLinearViewStateModel } from '../task-page/linear/use-task-page-linear-view-state' -import type { TaskPageLinearCollectionEffectsModel } from '../task-page/linear/use-task-page-linear-collection-effects' -import { useTaskPageJiraListState } from '../task-page/jira/use-task-page-jira-list-state' -import { useTaskPageJiraListEffects } from '../task-page/jira/use-task-page-jira-list-effects' - -const favouriteTransport: { - catalogue: Mock - readSaved: Mock - mutateSaved: Mock - readQuick: Mock - list: Mock - search: Mock - resume: Mock -} = vi.hoisted(() => ({ - catalogue: vi.fn(), - readSaved: vi.fn(), - mutateSaved: vi.fn(), - readQuick: vi.fn(), - list: vi.fn(), - search: vi.fn(), - resume: vi.fn() -})) -export { favouriteTransport } -vi.mock('@/runtime/jira/runtime-jira-favourite-filters-client', () => ({ - jiraListFavouriteFiltersForTarget: favouriteTransport.catalogue -})) -vi.mock('@/runtime/jira/runtime-jira-saved-filter-state', () => ({ - readJiraSavedFilterState: favouriteTransport.readSaved, - mutateJiraSavedFilters: favouriteTransport.mutateSaved -})) -vi.mock('@/runtime/jira/runtime-jira-quick-filter-state', () => ({ - readJiraQuickFilterState: favouriteTransport.readQuick -})) -vi.mock('@/runtime/jira/runtime-jira-detail-client', () => ({ - jiraListPrioritiesForTarget: vi.fn() -})) -vi.mock('@/runtime/jira/runtime-jira-client', () => ({ - jiraGetProjectStatusOrder: vi.fn() -})) - -export const favourite = { - id: 'fav-1', - name: 'My Jira favourite', - jql: 'filter = 12345 ORDER BY updated DESC', - siteId: 'site-a' -} -export const named = { id: 'named', name: 'Custom named filter', jql: 'project = CUSTOM' } -export const emptySaved: JiraSavedFilterState = { filters: [named], activeFilterId: null } -const emptyIssues: JiraIssue[] = [] -const noop = () => {} - -export function resetFavouriteTransport() { - vi.resetAllMocks() - favouriteTransport.catalogue.mockImplementation(async (_target, siteId: string) => ({ - supported: true, - filters: [{ ...favourite, siteId }], - truncated: false - })) - favouriteTransport.readSaved.mockResolvedValue(emptySaved) - favouriteTransport.mutateSaved.mockImplementation(async (_target, mutation) => ({ - ...emptySaved, - activeFilterId: mutation.kind === 'select' ? mutation.id : null - })) - favouriteTransport.readQuick.mockResolvedValue({ statuses: [] }) - favouriteTransport.list.mockResolvedValue(emptyIssues) - favouriteTransport.search.mockResolvedValue(emptyIssues) -} - -export type FavouriteFixtureProps = { hostId?: ExecutionHostId; siteId?: string; enabled?: boolean } -export function useFavouriteTaskPage({ - hostId = 'runtime:owner-a', - siteId = 'site-a', - enabled = true -}: FavouriteFixtureProps = {}) { - const sourceContext = useMemo( - () => ({ - kind: 'task-source' as const, - provider: 'jira' as const, - projectId: 'folder-project', - hostId, - providerIdentity: { provider: 'jira' as const, siteId: siteId === 'all' ? null : siteId } - }), - [hostId, siteId] - ) - const model = { - accountBackedTaskSourceHostId: hostId, - jiraTaskSourceContext: sourceContext, - jiraTaskSourceScopeKey: JSON.stringify([hostId, siteId]), - taskSource: enabled ? 'jira' : 'github', - taskResumeApplied: true, - jiraConnected: true, - selectedJiraSiteId: siteId, - repos: [], - selectedRepos: [], - pageData: {}, - settings: { activeRuntimeEnvironmentId: 'different-focused-server' }, - jiraSearchPersistReadyRef: useRef(true), - selectedJiraIssueKey: null, - selectedJiraIssueFallback: null, - setSelectedJiraIssueKey: noop, - setSelectedJiraIssueFallback: noop, - setTaskResumeState: favouriteTransport.resume, - linkedJiraIssueRefs: [], - linkedJiraIssueRefsSignature: '[]', - listJiraIssues: favouriteTransport.list, - searchJiraIssues: favouriteTransport.search - } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: The actual Jira hooks only read the supplied fields from the preceding provider pipeline. - const state = useTaskPageJiraListState(model as unknown as TaskPageLinearViewStateModel) - const effects = { ...state, displayedJiraIssues: state.jiraIssues } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: All fields read by the actual Jira list effect are supplied; unrelated provider stages are omitted. - useTaskPageJiraListEffects(effects as unknown as TaskPageLinearCollectionEffectsModel) - return state -} diff --git a/src/renderer/src/components/jira-key-or-text-search.ts b/src/renderer/src/components/jira-key-or-text-search.ts index 9d5950ed62d..df6e586bad1 100644 --- a/src/renderer/src/components/jira-key-or-text-search.ts +++ b/src/renderer/src/components/jira-key-or-text-search.ts @@ -3,7 +3,7 @@ import { buildJiraIssueKeyJql, buildJiraTextMatchJql } from '../../../shared/jira/jira-search-input-jql' -import { getJiraBadRequestReason } from './task-page/jira/task-page-jira-load-state' +import { getJiraBadRequestReason } from './task-page/jira/listing/task-page-jira-load-state' export async function searchJiraIssuesByKeyOrText( input: string, diff --git a/src/renderer/src/components/task-page/Surface.tsx b/src/renderer/src/components/task-page/Surface.tsx index 736001ade30..2e2f2e28cd6 100644 --- a/src/renderer/src/components/task-page/Surface.tsx +++ b/src/renderer/src/components/task-page/Surface.tsx @@ -1,9 +1,9 @@ import type { TaskPageComposerActionsModel } from './use-task-page-composer-actions' import { TaskPageFrame } from './Frame' import { TaskPageGitHubIssueDialog } from './github/issue-creation/IssueDialog' -import { TaskPageLinearProjectDialog } from './linear/ProjectDialog' -import { TaskPageLinearIssueDialog } from './linear/IssueDialog' -import { TaskPageJiraIssueDialog } from './jira/IssueDialog' +import { TaskPageLinearProjectDialog } from './linear/creation/ProjectDialog' +import { TaskPageLinearIssueDialog } from './linear/creation/IssueDialog' +import { TaskPageJiraIssueDialog } from './jira/creation/IssueDialog' import { TaskPageGitLabDialog } from './gitlab/Dialog' import { TaskPageLinearConnectDialog } from './linear/ConnectDialog' import { TaskPageJiraConnectDialog } from './jira/ConnectDialog' diff --git a/src/renderer/src/components/task-page/TaskPage.tsx b/src/renderer/src/components/task-page/TaskPage.tsx index c8d42e8d023..8f37666a72d 100644 --- a/src/renderer/src/components/task-page/TaskPage.tsx +++ b/src/renderer/src/components/task-page/TaskPage.tsx @@ -14,14 +14,14 @@ import { useTaskPageJiraListState } from './jira/use-task-page-jira-list-state' import { useTaskPageResumeRestoration } from './use-task-page-resume-restoration' import { useTaskPageProviderMetadata } from './use-task-page-provider-metadata' import { useTaskPageGitLabLoading } from './gitlab/use-task-page-gitlab-loading' -import { useTaskPageLinearListSelection } from './linear/use-task-page-linear-list-selection' -import { useTaskPageLinearListProjection } from './linear/use-task-page-linear-list-projection' -import { useTaskPageLinearBoard } from './linear/use-task-page-linear-board' -import { useTaskPageJiraListProjection } from './jira/use-task-page-jira-list-projection' -import { useTaskPageLinearCreationState } from './linear/use-task-page-linear-creation-state' +import { useTaskPageLinearListSelection } from './linear/issues/use-task-page-linear-list-selection' +import { useTaskPageLinearListProjection } from './linear/issues/use-task-page-linear-list-projection' +import { useTaskPageLinearBoard } from './linear/issues/use-task-page-linear-board' +import { useTaskPageJiraListProjection } from './jira/listing/use-task-page-jira-list-projection' +import { useTaskPageLinearCreationState } from './linear/creation/use-task-page-linear-creation-state' import { useTaskPageGitHubMutationState } from './github/mutations/use-task-page-github-mutation-state' -import { useTaskPageJiraCreationState } from './jira/use-task-page-jira-creation-state' -import { useTaskPageJiraCreationMetadata } from './jira/use-task-page-jira-creation-metadata' +import { useTaskPageJiraCreationState } from './jira/creation/use-task-page-jira-creation-state' +import { useTaskPageJiraCreationMetadata } from './jira/creation/use-task-page-jira-creation-metadata' import { useTaskPageGitHubListProjection } from './github/listing/use-task-page-github-list-projection' import { useTaskPageGitHubSearchPagination } from './github/listing/use-task-page-github-search-pagination' import { useTaskPageGitHubLandingRefresh } from './github/refresh/use-task-page-github-landing-refresh' @@ -29,15 +29,15 @@ import { useTaskPageGitHubQuietRefresh } from './github/refresh/use-task-page-gi import { useTaskPageSearchActions } from './use-task-page-search-actions' import { useTaskPageWorkspaceActions } from './use-task-page-workspace-actions' import { useTaskPageGitHubIssueCreation } from './github/issue-creation/use-task-page-github-issue-creation' -import { useTaskPageLinearProjectCreation } from './linear/use-task-page-linear-project-creation' -import { useTaskPageLinearIssueCreation } from './linear/use-task-page-linear-issue-creation' -import { useTaskPageJiraIssueCreation } from './jira/use-task-page-jira-issue-creation' -import { useTaskPageJiraSummaryGeneration } from './jira/use-task-page-jira-summary-generation' +import { useTaskPageLinearProjectCreation } from './linear/creation/use-task-page-linear-project-creation' +import { useTaskPageLinearIssueCreation } from './linear/creation/use-task-page-linear-issue-creation' +import { useTaskPageJiraIssueCreation } from './jira/creation/use-task-page-jira-issue-creation' +import { useTaskPageJiraSummaryGeneration } from './jira/creation/use-task-page-jira-summary-generation' import { useTaskPageGlobalEffects } from './use-task-page-global-effects' -import { useTaskPageLinearListEffects } from './linear/use-task-page-linear-list-effects' -import { useTaskPageLinearInOrcaEffects } from './linear/use-task-page-linear-in-orca-effects' -import { useTaskPageLinearCollectionEffects } from './linear/use-task-page-linear-collection-effects' -import { useTaskPageJiraListEffects } from './jira/use-task-page-jira-list-effects' +import { useTaskPageLinearListEffects } from './linear/issues/use-task-page-linear-list-effects' +import { useTaskPageLinearInOrcaEffects } from './linear/issues/use-task-page-linear-in-orca-effects' +import { useTaskPageLinearCollectionEffects } from './linear/projects/use-task-page-linear-collection-effects' +import { useTaskPageJiraListEffects } from './jira/listing/use-task-page-jira-list-effects' import { useTaskPageComposerActions } from './use-task-page-composer-actions' import { TaskPageSurface } from './Surface' diff --git a/src/renderer/src/components/task-page/github/issue-status/StatusCell.tsx b/src/renderer/src/components/task-page/github/issue-status/StatusCell.tsx index a08d0c80fac..715877eb6ae 100644 --- a/src/renderer/src/components/task-page/github/issue-status/StatusCell.tsx +++ b/src/renderer/src/components/task-page/github/issue-status/StatusCell.tsx @@ -28,7 +28,7 @@ import { TaskPageGitHubWorkItemStateBadge } from '@/components/task-page/github/ import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover' import { cn } from '@/lib/class-names' import { CircleDot, ChevronDown, Copy, CheckCircle2, Ban, ChevronRight } from 'lucide-react' -import type { TaskPageGitHubWorkItemMutationRunner } from '../../linear/task-page-linear-jira-list-model' +import type { TaskPageGitHubWorkItemMutationRunner } from '../mutations/task-page-github-work-item-mutation-types' import { TaskPageGitHubDuplicatePicker } from './DuplicatePicker' import { useGitHubDuplicateIssueCandidates } from '@/components/github/github-duplicate-issue-candidates' diff --git a/src/renderer/src/components/task-page/github/listing/AssigneesCell.tsx b/src/renderer/src/components/task-page/github/listing/AssigneesCell.tsx index a573bc3441b..01b9751b7a5 100644 --- a/src/renderer/src/components/task-page/github/listing/AssigneesCell.tsx +++ b/src/renderer/src/components/task-page/github/listing/AssigneesCell.tsx @@ -16,7 +16,7 @@ import { githubProjectHost } from '../../../../../../shared/github/projects/proj import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover' import { cn } from '@/lib/class-names' import { LoaderCircle, ChevronDown, Check } from 'lucide-react' -import type { TaskPageGitHubWorkItemMutationRunner } from '../../linear/task-page-linear-jira-list-model' +import type { TaskPageGitHubWorkItemMutationRunner } from '../mutations/task-page-github-work-item-mutation-types' import { GitHubAssigneeAvatar } from './Avatars' import { GitHubUserAvatar } from '@/components/github/github-user-avatar' import { codeHostErrorText } from '@/lib/source-control/remote/code-host-error-copy' diff --git a/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-pending.ts b/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-pending.ts index 60c4063fb2e..ff26f96b099 100644 --- a/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-pending.ts +++ b/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-pending.ts @@ -1,5 +1,5 @@ import { useAppStore } from '@/store/index' -import type { TaskPageJiraCreationMetadataModel } from '../../jira/use-task-page-jira-creation-metadata' +import type { TaskPageJiraCreationMetadataModel } from '../../jira/creation/use-task-page-jira-creation-metadata' // Why the list projection's input model: the landing run's later-stage model carries these same fields. type LandingRequestInput = Pick< diff --git a/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-projection.ts b/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-projection.ts index a45c6318b84..d0760ce4678 100644 --- a/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-projection.ts +++ b/src/renderer/src/components/task-page/github/listing/use-task-page-github-list-projection.ts @@ -1,6 +1,6 @@ import { useCallback, useMemo } from 'react' import { useGitHubTaskItems } from '@/hooks/useGitHubTaskItems' -import type { TaskPageJiraCreationMetadataModel } from '../../jira/use-task-page-jira-creation-metadata' +import type { TaskPageJiraCreationMetadataModel } from '../../jira/creation/use-task-page-jira-creation-metadata' import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' import { taskPageGitHubItemKey } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-keys' import { deriveAdvertisedTotalPages } from '../../../../../../shared/work-items/work-item-empty-page' diff --git a/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-types.ts b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-types.ts index e09ec3e4cb8..0fd7f0622c4 100644 --- a/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-types.ts +++ b/src/renderer/src/components/task-page/github/mutations/task-page-github-work-item-mutation-types.ts @@ -32,3 +32,26 @@ export type BeginTaskPageGitHubWorkItemMutationResult = { families: string[] key: TaskPageGitHubMutationKey } + +export type TaskPageGitHubWorkItemMutationRunner = { + run: (input: { + item: GitHubWorkItem + intent: TaskPageGitHubMutationIntent + sourceContext?: TaskSourceContext | null + mutate: () => Promise<{ + ok?: boolean + error?: + | string + | { + message?: string + } + } | void> + successToast?: string + errorToast: string + }) => Promise<'confirmed' | 'rolled_back' | 'stale'> + isIntentPending: (input: { + item: GitHubWorkItem + intent: TaskPageGitHubMutationIntent + sourceContext?: TaskSourceContext | null + }) => boolean +} diff --git a/src/renderer/src/components/task-page/github/mutations/use-task-page-github-mutation-state.ts b/src/renderer/src/components/task-page/github/mutations/use-task-page-github-mutation-state.ts index cd0a1f6e7a8..7a7d10bfa68 100644 --- a/src/renderer/src/components/task-page/github/mutations/use-task-page-github-mutation-state.ts +++ b/src/renderer/src/components/task-page/github/mutations/use-task-page-github-mutation-state.ts @@ -1,4 +1,4 @@ -import type { TaskPageLinearCreationStateModel } from '../../linear/use-task-page-linear-creation-state' +import type { TaskPageLinearCreationStateModel } from '../../linear/creation/use-task-page-linear-creation-state' import { getGitHubTaskKind } from '../../../../../../shared/work-items/task-github-search' import { useMemo, useLayoutEffect, useEffect, useCallback, useRef } from 'react' import { parseTaskQuery } from '../../../../../../shared/work-items/task-query' diff --git a/src/renderer/src/components/task-page/github/pull-requests/MergeCell.tsx b/src/renderer/src/components/task-page/github/pull-requests/MergeCell.tsx index 66d3830ecff..2c66b87a296 100644 --- a/src/renderer/src/components/task-page/github/pull-requests/MergeCell.tsx +++ b/src/renderer/src/components/task-page/github/pull-requests/MergeCell.tsx @@ -25,7 +25,7 @@ import { import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' import { cn } from '@/lib/class-names' import { LoaderCircle, GitMerge, ChevronDown, ExternalLink } from 'lucide-react' -import type { TaskPageGitHubWorkItemMutationRunner } from '../../linear/task-page-linear-jira-list-model' +import type { TaskPageGitHubWorkItemMutationRunner } from '../mutations/task-page-github-work-item-mutation-types' import { resolveTaskPullRequestRepo } from './task-page-github-review-model' import { openAppHttpLink } from '@/lib/browser/navigation/app-http-link-open' export function PRMergeCell({ diff --git a/src/renderer/src/components/task-page/github/pull-requests/ReviewCell.tsx b/src/renderer/src/components/task-page/github/pull-requests/ReviewCell.tsx index 175ce368376..3798f9603e1 100644 --- a/src/renderer/src/components/task-page/github/pull-requests/ReviewCell.tsx +++ b/src/renderer/src/components/task-page/github/pull-requests/ReviewCell.tsx @@ -20,7 +20,7 @@ import { import { cn } from '@/lib/class-names' import { ChevronDown } from 'lucide-react' import { Popover, PopoverTrigger } from '@/components/ui/popover' -import type { TaskPageGitHubWorkItemMutationRunner } from '../../linear/task-page-linear-jira-list-model' +import type { TaskPageGitHubWorkItemMutationRunner } from '../mutations/task-page-github-work-item-mutation-types' import { mergeReviewerSuggestions, resolveTaskPullRequestRepo diff --git a/src/renderer/src/components/task-page/github/pull-requests/task-page-github-reviewer-actions.ts b/src/renderer/src/components/task-page/github/pull-requests/task-page-github-reviewer-actions.ts index 46bf6f0376c..cf1f380fb8e 100644 --- a/src/renderer/src/components/task-page/github/pull-requests/task-page-github-reviewer-actions.ts +++ b/src/renderer/src/components/task-page/github/pull-requests/task-page-github-reviewer-actions.ts @@ -15,7 +15,7 @@ import type { GitHubRepositoryIdentity as GitHubOwnerRepo } from '../../../../.. import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' import type { Repo } from '../../../../../../shared/repo-types' import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' -import type { TaskPageGitHubWorkItemMutationRunner } from '../../linear/task-page-linear-jira-list-model' +import type { TaskPageGitHubWorkItemMutationRunner } from '../mutations/task-page-github-work-item-mutation-types' import { buildRequestedReviewUsers } from './task-page-github-review-model' type ReviewerActionsInput = { diff --git a/src/renderer/src/components/task-page/github/pull-requests/use-task-page-pr-check-hydration.ts b/src/renderer/src/components/task-page/github/pull-requests/use-task-page-pr-check-hydration.ts index ce313a40bb7..e38a5fc6f82 100644 --- a/src/renderer/src/components/task-page/github/pull-requests/use-task-page-pr-check-hydration.ts +++ b/src/renderer/src/components/task-page/github/pull-requests/use-task-page-pr-check-hydration.ts @@ -25,7 +25,7 @@ import { } from '../../../../../../shared/github/checks/complete-pr-check-owner' import { getWorkItemRepo } from '@/lib/work-items/work-item-repo-row' import type { GitHubWorkItem } from '../../../../../../shared/github/work-items/work-item-types' -import type { TaskPageJiraCreationMetadataModel } from '../../jira/use-task-page-jira-creation-metadata' +import type { TaskPageJiraCreationMetadataModel } from '../../jira/creation/use-task-page-jira-creation-metadata' import { deriveTaskPagePRCheckSummary } from '../../task-page-pr-check-summary' import { PR_CHECKS_EAGER_PREFETCH_LIMIT } from '../../task-page-source-context' import { getTaskPageRepoSourceContext } from '../../../../lib/work-items/task-repo-source-context' diff --git a/src/renderer/src/components/task-page/jira/Content.tsx b/src/renderer/src/components/task-page/jira/Content.tsx index 4a7ca204a0c..3600962a014 100644 --- a/src/renderer/src/components/task-page/jira/Content.tsx +++ b/src/renderer/src/components/task-page/jira/Content.tsx @@ -7,14 +7,14 @@ import { useTranslate } from '@/i18n/use-translate' import { Button } from '@/components/ui/button' import { jiraListGridStyle } from '../../jira-list-columns' import { JiraListColumnsMenu } from '../../jira-list-columns-menu' -import { TaskPageJiraSortControls } from './task-page-jira-sort-controls' -import { TaskPageJiraErrorBanner } from '../linear/task-page-linear-jira-list-model' -import { TaskPageJiraTextFallbackNotice } from './task-page-jira-text-fallback-notice' -import { TaskPageJiraIssueList } from '@/components/task-page/jira/task-page-jira-issue-list' -import { TaskPageJiraBoardView } from '@/components/task-page/jira/task-page-jira-board-view' +import { TaskPageJiraSortControls } from './listing/task-page-jira-sort-controls' +import { TaskPageJiraErrorBanner } from './listing/task-page-jira-error-banner' +import { TaskPageJiraTextFallbackNotice } from './listing/task-page-jira-text-fallback-notice' +import { TaskPageJiraIssueList } from '@/components/task-page/jira/listing/task-page-jira-issue-list' +import { TaskPageJiraBoardView } from '@/components/task-page/jira/boards/task-page-jira-board-view' import { TaskPageJiraViewControls } from '@/components/task-page/jira/task-page-jira-view-controls' import { formatRelativeTime } from '../task-page-source-context' -import { getJiraStatusTone } from '@/components/task-page/jira/task-page-jira-status-tone' +import { getJiraStatusTone } from '@/components/task-page/jira/listing/task-page-jira-status-tone' import JiraIssueWorkspace from '@/components/JiraIssueWorkspace' import { TaskPageLinearContent } from '../linear/Content' import { diff --git a/src/renderer/src/components/task-page/jira/Filters.tsx b/src/renderer/src/components/task-page/jira/Filters.tsx index 94087bc020e..5d999ef3812 100644 --- a/src/renderer/src/components/task-page/jira/Filters.tsx +++ b/src/renderer/src/components/task-page/jira/Filters.tsx @@ -1,6 +1,6 @@ -import { TaskPageJiraStatusFilters } from './TaskPageJiraStatusFilters' -import { TaskPageJiraSavedFilters } from './TaskPageJiraSavedFilters' -import { TaskPageJiraFavouriteFilters } from './TaskPageJiraFavouriteFilters' +import { TaskPageJiraStatusFilters } from './filters/TaskPageJiraStatusFilters' +import { TaskPageJiraSavedFilters } from './filters/TaskPageJiraSavedFilters' +import { TaskPageJiraFavouriteFilters } from './filters/TaskPageJiraFavouriteFilters' import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' import { cn } from '@/lib/class-names' import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' diff --git a/src/renderer/src/components/task-page/jira/IssueAssigneeField.test.tsx b/src/renderer/src/components/task-page/jira/IssueAssigneeField.test.tsx deleted file mode 100644 index 55a723ed074..00000000000 --- a/src/renderer/src/components/task-page/jira/IssueAssigneeField.test.tsx +++ /dev/null @@ -1,68 +0,0 @@ -// @vitest-environment happy-dom -import type { ComponentProps } from 'react' -import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' -import { afterEach, expect, it, vi } from 'vitest' -import { TaskPageJiraIssueAssigneeField } from './IssueAssigneeField' - -const search = vi.hoisted(() => vi.fn()) -vi.mock('@/runtime/jira/runtime-jira-user-fields-client', () => ({ - jiraListAssignableUsersForProject: search -})) -afterEach(cleanup) - -it('drops old provider candidates before another host with identical project IDs can select them', async () => { - const onSelect = vi.fn() - const model: ComponentProps['model'] = { - settings: null, - jiraTaskSourceContext: null, - jiraStatusCurrent: true, - jiraStatus: { - connected: true, - activeSiteId: 'same-site', - viewer: { accountId: 'local-me', displayName: 'Local me', email: null } - }, - jiraCreateFields: [{ key: 'assignee', name: 'Assignee', required: false }], - newJiraIssueTargetProject: { id: '100', key: 'PRJ', name: 'Project', siteId: 'same-site' }, - newJiraIssueAssignee: null, - setNewJiraIssueAssignee: onSelect, - newJiraIssueSubmitting: false, - providerRuntimeContextKey: 'local' - } - search.mockResolvedValueOnce([{ accountId: 'local-user', displayName: 'Local user' }]) - const { rerender } = render() - fireEvent.click(screen.getByRole('button', { name: 'Assignee' })) - await screen.findByRole('button', { name: 'Local user' }) - expect(screen.getByRole('button', { name: 'Assign to me (Local me)' })).toBeTruthy() - expect(search).toHaveBeenCalledWith( - { kind: 'default-scope', settings: null }, - 'PRJ', - '', - 'same-site' - ) - - search.mockReturnValueOnce(new Promise(() => {})) - rerender( - - ) - expect(screen.queryByRole('button', { name: 'Local user' })).toBeNull() - fireEvent.click(screen.getByRole('button', { name: 'Assignee' })) - await waitFor(() => expect(search).toHaveBeenCalledTimes(2)) - expect(screen.queryByRole('button', { name: 'Local user' })).toBeNull() - expect(screen.queryByRole('button', { name: 'Assign to me (Local me)' })).toBeNull() - rerender( - - ) - fireEvent.click(screen.getByRole('button', { name: 'Assign to me (Remote me)' })) - expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ accountId: 'remote-me' })) -}) diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-board-move-dialog.tsx b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board-move-dialog.tsx similarity index 95% rename from src/renderer/src/components/task-page/jira/task-page-jira-board-move-dialog.tsx rename to src/renderer/src/components/task-page/jira/boards/task-page-jira-board-move-dialog.tsx index 682e1babaf4..b849151157a 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-board-move-dialog.tsx +++ b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board-move-dialog.tsx @@ -8,13 +8,13 @@ import { DialogTitle } from '@/components/ui/dialog' import { useTranslate } from '@/i18n/use-translate' -import { JiraTransitionFieldsForm } from '../../jira-transition-fields-form' -import { classifyTransitionRequirements } from '../../../../../shared/jira/jira-transition-fields' +import { JiraTransitionFieldsForm } from '../../../jira-transition-fields-form' +import { classifyTransitionRequirements } from '../../../../../../shared/jira/jira-transition-fields' import type { JiraIssue, JiraIssueUpdate, JiraTransition -} from '../../../../../shared/jira/jira-types' +} from '../../../../../../shared/jira/jira-types' import { openAppHttpLink } from '@/lib/browser/navigation/app-http-link-open' export type JiraBoardMovePrompt = { diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-board-move.ts b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board-move.ts similarity index 89% rename from src/renderer/src/components/task-page/jira/task-page-jira-board-move.ts rename to src/renderer/src/components/task-page/jira/boards/task-page-jira-board-move.ts index 5bd213a77c9..36569d2c881 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-board-move.ts +++ b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board-move.ts @@ -3,9 +3,9 @@ import type { JiraIssueUpdate, JiraMutationResult, JiraTransition -} from '../../../../../shared/jira/jira-types' +} from '../../../../../../shared/jira/jira-types' import type { TaskPageJiraBoardSection } from './task-page-jira-board-sections' -import type { RuntimeClientTarget } from '../../../../../shared/runtime-client-target' +import type { RuntimeClientTarget } from '../../../../../../shared/runtime-client-target' import { findJiraBoardSectionTransitions, loadTaskPageJiraIssueTransitions diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-board-sections.ts b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board-sections.ts similarity index 95% rename from src/renderer/src/components/task-page/jira/task-page-jira-board-sections.ts rename to src/renderer/src/components/task-page/jira/boards/task-page-jira-board-sections.ts index bdc8520dfcf..89d4d026aa0 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-board-sections.ts +++ b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board-sections.ts @@ -1,5 +1,5 @@ -import type { JiraIssue, JiraProjectStatusOrder } from '../../../../../shared/jira/jira-types' -import { groupJiraIssuesByStatus } from './task-page-jira-issue-list' +import type { JiraIssue, JiraProjectStatusOrder } from '../../../../../../shared/jira/jira-types' +import { groupJiraIssuesByStatus } from '../listing/task-page-jira-issue-list' export type TaskPageJiraBoardSection = { key: string diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-board-transitions.ts b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board-transitions.ts similarity index 92% rename from src/renderer/src/components/task-page/jira/task-page-jira-board-transitions.ts rename to src/renderer/src/components/task-page/jira/boards/task-page-jira-board-transitions.ts index 08978ea9e58..645c56e9d86 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-board-transitions.ts +++ b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board-transitions.ts @@ -1,5 +1,5 @@ -import type { JiraIssue, JiraTransition } from '../../../../../shared/jira/jira-types' -import type { RuntimeClientTarget } from '../../../../../shared/runtime-client-target' +import type { JiraIssue, JiraTransition } from '../../../../../../shared/jira/jira-types' +import type { RuntimeClientTarget } from '../../../../../../shared/runtime-client-target' import { jiraListTransitionsForTarget } from '@/runtime/jira/runtime-jira-detail-client' import { getRuntimeTargetHostId } from '@/runtime/rpc/targets/runtime-client-target' import { createMetadataRequestStore, loadMetadata } from '@/hooks/metadata-request-cache' diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-board-view.tsx b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board-view.tsx similarity index 94% rename from src/renderer/src/components/task-page/jira/task-page-jira-board-view.tsx rename to src/renderer/src/components/task-page/jira/boards/task-page-jira-board-view.tsx index fbe6b20685e..6ea2209f254 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-board-view.tsx +++ b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board-view.tsx @@ -3,21 +3,21 @@ import { useAppStore } from '@/store/index' import { getEnvironmentRuntimeContextKey } from '@/runtime/hosts/observations/provider-runtime-context' import { runtimeTargetEnvironmentId } from '@/runtime/rpc/targets/runtime-client-target' import { useTranslate } from '@/i18n/use-translate' -import { classifyTransitionRequirements } from '../../../../../shared/jira/jira-transition-fields' +import { classifyTransitionRequirements } from '../../../../../../shared/jira/jira-transition-fields' import type { JiraIssue, JiraIssueUpdate, JiraProjectStatusOrder, JiraTransition -} from '../../../../../shared/jira/jira-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' -import type { RuntimeClientTarget } from '../../../../../shared/runtime-client-target' +} from '../../../../../../shared/jira/jira-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' +import type { RuntimeClientTarget } from '../../../../../../shared/runtime-client-target' import { TaskPageJiraBoard, jiraBoardIssueRefKey } from './task-page-jira-board' import type { TaskPageJiraBoardSection } from './task-page-jira-board-sections' import { JiraBoardMoveDialog, type JiraBoardMovePrompt } from './task-page-jira-board-move-dialog' import { applyJiraBoardMove, getJiraBoardMoveTransitions } from './task-page-jira-board-move' -import { formatRelativeTime } from '../task-page-source-context' -import { enqueueJiraIssueMutation, jiraIssueRequestScope } from '../../jira-issue-mutation-queue' +import { formatRelativeTime } from '../../task-page-source-context' +import { enqueueJiraIssueMutation, jiraIssueRequestScope } from '../../../jira-issue-mutation-queue' type Props = { issues: JiraIssue[] diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-board.tsx b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board.tsx similarity index 99% rename from src/renderer/src/components/task-page/jira/task-page-jira-board.tsx rename to src/renderer/src/components/task-page/jira/boards/task-page-jira-board.tsx index a2e6183a195..f86846a7024 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-board.tsx +++ b/src/renderer/src/components/task-page/jira/boards/task-page-jira-board.tsx @@ -17,7 +17,7 @@ import { writeJiraBoardIssueDragData } from '@/lib/work-items/jira/jira-board-drag-payload' import type { JiraBoardIssueDragRef } from '@/lib/work-items/jira/jira-board-drag-payload' -import type { JiraIssue, JiraProjectStatusOrder } from '../../../../../shared/jira/jira-types' +import type { JiraIssue, JiraProjectStatusOrder } from '../../../../../../shared/jira/jira-types' import { buildJiraBoardSections, type TaskPageJiraBoardSection diff --git a/src/renderer/src/components/task-page/jira/IssueAssigneeField.tsx b/src/renderer/src/components/task-page/jira/creation/IssueAssigneeField.tsx similarity index 96% rename from src/renderer/src/components/task-page/jira/IssueAssigneeField.tsx rename to src/renderer/src/components/task-page/jira/creation/IssueAssigneeField.tsx index 051628eab53..1d81be293df 100644 --- a/src/renderer/src/components/task-page/jira/IssueAssigneeField.tsx +++ b/src/renderer/src/components/task-page/jira/creation/IssueAssigneeField.tsx @@ -1,8 +1,8 @@ import { useCallback } from 'react' -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { getJiraSelfUser } from '@/components/jira-self-user' import { JiraUserPicker, type JiraUserPickerFixedOption } from '@/components/jira-user-picker' -import { hasJiraAssigneeCreateField } from '@/components/task-page/jira/task-page-jira-create-fields' +import { hasJiraAssigneeCreateField } from '@/components/task-page/jira/creation/task-page-jira-create-fields' import { getJiraProjectSelectionKey } from '@/components/task-page/jira/task-page-jira-project-selection' import { jiraListAssignableUsersForProject } from '@/runtime/jira/runtime-jira-user-fields-client' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/task-page/jira/IssueDialog.tsx b/src/renderer/src/components/task-page/jira/creation/IssueDialog.tsx similarity index 98% rename from src/renderer/src/components/task-page/jira/IssueDialog.tsx rename to src/renderer/src/components/task-page/jira/creation/IssueDialog.tsx index b2d8aa673b2..aa7ad3ab009 100644 --- a/src/renderer/src/components/task-page/jira/IssueDialog.tsx +++ b/src/renderer/src/components/task-page/jira/creation/IssueDialog.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { Dialog, DialogContent, @@ -34,7 +34,7 @@ import { Input } from '@/components/ui/input' import { getJiraCreateAllowedValueLabel, isJiraScalarUserCreateField -} from '@/components/task-page/jira/task-page-jira-create-fields' +} from '@/components/task-page/jira/creation/task-page-jira-create-fields' import { TaskPageJiraIssueAssigneeField } from './IssueAssigneeField' import { TaskPageJiraIssueUserField } from './IssueUserField' export function TaskPageJiraIssueDialog({ diff --git a/src/renderer/src/components/task-page/jira/IssueTitleField.tsx b/src/renderer/src/components/task-page/jira/creation/IssueTitleField.tsx similarity index 97% rename from src/renderer/src/components/task-page/jira/IssueTitleField.tsx rename to src/renderer/src/components/task-page/jira/creation/IssueTitleField.tsx index e5c99a559c8..1f859e72c24 100644 --- a/src/renderer/src/components/task-page/jira/IssueTitleField.tsx +++ b/src/renderer/src/components/task-page/jira/creation/IssueTitleField.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { Input } from '@/components/ui/input' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/task-page/jira/IssueUserField.tsx b/src/renderer/src/components/task-page/jira/creation/IssueUserField.tsx similarity index 92% rename from src/renderer/src/components/task-page/jira/IssueUserField.tsx rename to src/renderer/src/components/task-page/jira/creation/IssueUserField.tsx index 4243aa53873..aaf545134c1 100644 --- a/src/renderer/src/components/task-page/jira/IssueUserField.tsx +++ b/src/renderer/src/components/task-page/jira/creation/IssueUserField.tsx @@ -1,7 +1,7 @@ import { useState } from 'react' import { JiraUserPicker } from '@/components/jira-user-picker' import type { RowLessSource as RuntimeJiraSettings } from '@/runtime/hosts/selection/default-creation-host' -import type { JiraUser } from '../../../../../shared/jira/jira-types' +import type { JiraUser } from '../../../../../../shared/jira/jira-types' export function TaskPageJiraIssueUserField({ providerSettings, diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-create-fields.ts b/src/renderer/src/components/task-page/jira/creation/task-page-jira-create-fields.ts similarity index 98% rename from src/renderer/src/components/task-page/jira/task-page-jira-create-fields.ts rename to src/renderer/src/components/task-page/jira/creation/task-page-jira-create-fields.ts index 75728349eb3..eac52fd8e92 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-create-fields.ts +++ b/src/renderer/src/components/task-page/jira/creation/task-page-jira-create-fields.ts @@ -1,5 +1,5 @@ import { buildJiraCreateTextAdf } from '@/components/jira-create-adf' -import type { JiraCreateField } from '../../../../../shared/jira/jira-types' +import type { JiraCreateField } from '../../../../../../shared/jira/jira-types' // 'assignee' is system here because the dialog renders a dedicated picker for it. const JIRA_CREATE_SYSTEM_FIELD_KEYS = new Set([ diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-creation-metadata.ts b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-metadata.ts similarity index 100% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-creation-metadata.ts rename to src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-metadata.ts diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-creation-projects.ts b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-projects.ts similarity index 98% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-creation-projects.ts rename to src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-projects.ts index 4ef8cb4afd8..6672fd53548 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-creation-projects.ts +++ b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-projects.ts @@ -1,9 +1,9 @@ -import { useJiraCreateAssignee } from '../../use-jira-create-assignee' +import { useJiraCreateAssignee } from '../../../use-jira-create-assignee' import React, { useCallback, useEffect, useMemo } from 'react' import { useUiLanguageRevision } from '@/i18n/use-ui-language-revision' import { filterJiraProjectPickerProjects } from '@/components/jira-project-picker-filter' -import { isVisibleJiraCreateField } from '@/components/task-page/jira/task-page-jira-create-fields' +import { isVisibleJiraCreateField } from '@/components/task-page/jira/creation/task-page-jira-create-fields' import { jiraProjectDisplayLabelComparator, getJiraProjectSelectionKey diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-state.ts similarity index 97% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts rename to src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-state.ts index f7075dc9606..9b01f898f0c 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-creation-state.ts +++ b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-creation-state.ts @@ -1,8 +1,8 @@ -import type { TaskPageGitHubMutationStateModel } from '../github/mutations/use-task-page-github-mutation-state' +import type { TaskPageGitHubMutationStateModel } from '../../github/mutations/use-task-page-github-mutation-state' import { useState, useRef, useEffect } from 'react' -import type { JiraIssueType, JiraCreateField } from '../../../../../shared/jira/jira-types' +import type { JiraIssueType, JiraCreateField } from '../../../../../../shared/jira/jira-types' import { useTaskCreationDraftRetention } from '@/components/use-task-creation-draft-retention' -import { writeNewJiraIssueDraft } from '../task-page-draft-storage' +import { writeNewJiraIssueDraft } from '../../task-page-draft-storage' import { useTaskPageJiraCreationProjects } from './use-task-page-jira-creation-projects' export function useTaskPageJiraCreationStatePrelude(model: TaskPageGitHubMutationStateModel) { const { diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-issue-creation.ts b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-issue-creation.ts similarity index 96% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-issue-creation.ts rename to src/renderer/src/components/task-page/jira/creation/use-task-page-jira-issue-creation.ts index f7ee09e20de..6739ab0c53d 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-issue-creation.ts +++ b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-issue-creation.ts @@ -2,12 +2,12 @@ import { taskProviderErrorMessage, taskProviderErrorText } from '@/lib/work-items/task-provider-error-copy' -import type { TaskPageLinearIssueCreationModel } from '../linear/use-task-page-linear-issue-creation' +import type { TaskPageLinearIssueCreationModel } from '../../linear/creation/use-task-page-linear-issue-creation' import { useCallback } from 'react' import { buildJiraCreateSubmission, hasJiraAssigneeCreateField -} from '@/components/task-page/jira/task-page-jira-create-fields' +} from '@/components/task-page/jira/creation/task-page-jira-create-fields' import { jiraCreateIssue } from '@/runtime/jira/runtime-jira-user-fields-client' import { jiraGetIssue } from '@/runtime/jira/runtime-jira-client' import { toast } from 'sonner' diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-summary-generation.test.tsx b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-summary-generation.test.tsx similarity index 98% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-summary-generation.test.tsx rename to src/renderer/src/components/task-page/jira/creation/use-task-page-jira-summary-generation.test.tsx index 8a78a86819a..f516032f699 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-summary-generation.test.tsx +++ b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-summary-generation.test.tsx @@ -2,11 +2,11 @@ import { act, useState } from 'react' import { renderHook, cleanup } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { JiraIssueSummaryGenerationResult } from '../../../../../shared/jira/jira-issue-summary-generation' +import type { JiraIssueSummaryGenerationResult } from '../../../../../../shared/jira/jira-issue-summary-generation' import { LOCAL_EXECUTION_HOST_ID, toRuntimeExecutionHostId -} from '../../../../../shared/execution-host' +} from '../../../../../../shared/execution-host' import { useTaskPageJiraSummaryGeneration } from './use-task-page-jira-summary-generation' const mocks = vi.hoisted(() => ({ generate: vi.fn(), cancel: vi.fn(), error: vi.fn() })) diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-summary-generation.ts b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-summary-generation.ts similarity index 96% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-summary-generation.ts rename to src/renderer/src/components/task-page/jira/creation/use-task-page-jira-summary-generation.ts index 6193651324f..0a0efcecde8 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-summary-generation.ts +++ b/src/renderer/src/components/task-page/jira/creation/use-task-page-jira-summary-generation.ts @@ -3,16 +3,16 @@ import { taskProviderErrorText } from '@/lib/work-items/task-provider-error-copy' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { createNonSecureContextUuid as createBrowserUuid } from '../../../../../shared/non-secure-context-uuid' +import { createNonSecureContextUuid as createBrowserUuid } from '../../../../../../shared/non-secure-context-uuid' import { isWebClientLocation } from '@/app-shell/platform/web-client-location' import { ipcOrRuntimeTargetForHostId } from '@/runtime/rpc/targets/runtime-client-target' import { generateRuntimeJiraIssueSummary, cancelRuntimeJiraIssueSummary } from '@/runtime/jira/runtime-jira-summary-generation-client' -import type { RuntimeClientTarget } from '../../../../../shared/runtime-client-target' -import type { JiraIssueSummaryGenerationRequest } from '../../../../../shared/jira/jira-issue-summary-generation' -import { getTaskSourceCacheScope } from '../../../../../shared/work-items/task-source-context' +import type { RuntimeClientTarget } from '../../../../../../shared/runtime-client-target' +import type { JiraIssueSummaryGenerationRequest } from '../../../../../../shared/jira/jira-issue-summary-generation' +import { getTaskSourceCacheScope } from '../../../../../../shared/work-items/task-source-context' import { translate } from '@/i18n/i18n' import type { TaskPageJiraIssueCreationModel } from './use-task-page-jira-issue-creation' diff --git a/src/renderer/src/components/task-page/jira/TaskPageJiraFavouriteFilters.tsx b/src/renderer/src/components/task-page/jira/filters/TaskPageJiraFavouriteFilters.tsx similarity index 98% rename from src/renderer/src/components/task-page/jira/TaskPageJiraFavouriteFilters.tsx rename to src/renderer/src/components/task-page/jira/filters/TaskPageJiraFavouriteFilters.tsx index fd6f6f70b7f..6001a5cde4c 100644 --- a/src/renderer/src/components/task-page/jira/TaskPageJiraFavouriteFilters.tsx +++ b/src/renderer/src/components/task-page/jira/filters/TaskPageJiraFavouriteFilters.tsx @@ -10,7 +10,7 @@ import { CommandItem, CommandEmpty } from '@/components/ui/command' -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' export function TaskPageJiraFavouriteFilters({ model diff --git a/src/renderer/src/components/task-page/jira/TaskPageJiraSavedFilters.tsx b/src/renderer/src/components/task-page/jira/filters/TaskPageJiraSavedFilters.tsx similarity index 98% rename from src/renderer/src/components/task-page/jira/TaskPageJiraSavedFilters.tsx rename to src/renderer/src/components/task-page/jira/filters/TaskPageJiraSavedFilters.tsx index 93ec324453f..1516976151a 100644 --- a/src/renderer/src/components/task-page/jira/TaskPageJiraSavedFilters.tsx +++ b/src/renderer/src/components/task-page/jira/filters/TaskPageJiraSavedFilters.tsx @@ -1,4 +1,4 @@ -import { isUtf8ByteLengthWithinLimit } from '../../../../../shared/utf8-byte-limits' +import { isUtf8ByteLengthWithinLimit } from '../../../../../../shared/utf8-byte-limits' import { useState } from 'react' import { Check, ListFilter, Pencil, Plus, Trash2 } from 'lucide-react' import { @@ -6,7 +6,7 @@ import { MAX_JIRA_SAVED_FILTER_NAME_LENGTH, MAX_JIRA_SAVED_FILTERS, type JiraSavedFilter -} from '../../../../../shared/jira/jira-saved-filters' +} from '../../../../../../shared/jira/jira-saved-filters' import { useTranslate } from '@/i18n/use-translate' import { Command, diff --git a/src/renderer/src/components/task-page/jira/TaskPageJiraStatusFilters.tsx b/src/renderer/src/components/task-page/jira/filters/TaskPageJiraStatusFilters.tsx similarity index 94% rename from src/renderer/src/components/task-page/jira/TaskPageJiraStatusFilters.tsx rename to src/renderer/src/components/task-page/jira/filters/TaskPageJiraStatusFilters.tsx index 90901e76c61..32342f7ba56 100644 --- a/src/renderer/src/components/task-page/jira/TaskPageJiraStatusFilters.tsx +++ b/src/renderer/src/components/task-page/jira/filters/TaskPageJiraStatusFilters.tsx @@ -2,13 +2,13 @@ import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group' import { useTranslate } from '@/i18n/use-translate' -import { buildJiraPresetJql } from '../../../../../shared/jira/jira-preset-jql' +import { buildJiraPresetJql } from '../../../../../../shared/jira/jira-preset-jql' import { getJiraStatusCatalogScopeKey, includeSelectedJiraStatuses } from './task-page-jira-status-catalog' -import { restrictJiraQueryToProject } from '../../../../../shared/jira/jira-project-reference' -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import { restrictJiraQueryToProject } from '../../../../../../shared/jira/jira-project-reference' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' export function TaskPageJiraStatusFilters({ model diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-status-catalog.ts b/src/renderer/src/components/task-page/jira/filters/task-page-jira-status-catalog.ts similarity index 87% rename from src/renderer/src/components/task-page/jira/task-page-jira-status-catalog.ts rename to src/renderer/src/components/task-page/jira/filters/task-page-jira-status-catalog.ts index 79b9eeb958f..3d1b6b9e113 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-status-catalog.ts +++ b/src/renderer/src/components/task-page/jira/filters/task-page-jira-status-catalog.ts @@ -1,6 +1,6 @@ -import type { JiraIssue } from '../../../../../shared/jira/jira-types' -import type { JiraProjectReference } from '../../../../../shared/jira/jira-project-reference' -import { getJiraProjectSelectionKey } from './task-page-jira-project-selection' +import type { JiraIssue } from '../../../../../../shared/jira/jira-types' +import type { JiraProjectReference } from '../../../../../../shared/jira/jira-project-reference' +import { getJiraProjectSelectionKey } from '../task-page-jira-project-selection' export function getJiraStatusCatalogScopeKey( sourceScopeKey: string, diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-favourite-filters.ts b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-favourite-filters.ts similarity index 96% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-favourite-filters.ts rename to src/renderer/src/components/task-page/jira/filters/use-task-page-jira-favourite-filters.ts index bd5ebab979c..24b1f3618ea 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-favourite-filters.ts +++ b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-favourite-filters.ts @@ -4,8 +4,8 @@ import { jiraListFavouriteFiltersForTarget, type JiraFavouriteFiltersResult } from '@/runtime/jira/runtime-jira-favourite-filters-client' -import type { JiraFavouriteFilter } from '../../../../../shared/jira/jira-favourite-filters' -import type { TaskPageLinearViewStateModel } from '../linear/use-task-page-linear-view-state' +import type { JiraFavouriteFilter } from '../../../../../../shared/jira/jira-favourite-filters' +import type { TaskPageLinearViewStateModel } from '../../linear/use-task-page-linear-view-state' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-project-scope.test.tsx b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-project-scope.test.tsx similarity index 97% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-project-scope.test.tsx rename to src/renderer/src/components/task-page/jira/filters/use-task-page-jira-project-scope.test.tsx index ad095a2911e..30f03b92a4e 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-project-scope.test.tsx +++ b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-project-scope.test.tsx @@ -3,7 +3,7 @@ import { act, cleanup, renderHook } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { useAppStore } from '@/store/index' -import type { Repo } from '../../../../../shared/repo-types' +import type { Repo } from '../../../../../../shared/repo-types' import { useTaskPageJiraProjectScope } from './use-task-page-jira-project-scope' const { readSavedFilters, mutateSavedFilters, readQuickFilters, writeQuickFilters } = vi.hoisted( diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-project-scope.ts b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-project-scope.ts similarity index 79% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-project-scope.ts rename to src/renderer/src/components/task-page/jira/filters/use-task-page-jira-project-scope.ts index 4f562a9d885..152fb3156a0 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-project-scope.ts +++ b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-project-scope.ts @@ -1,9 +1,12 @@ import { useCallback, useState } from 'react' -import type { Repo } from '../../../../../shared/repo-types' -import type { JiraProjectReference } from '../../../../../shared/jira/jira-project-reference' -import { getRepoExecutionHostId, type ExecutionHostId } from '../../../../../shared/execution-host' -import { resolveJiraSourceHostId } from '../../../../../shared/jira/jira-source-host' -import { getRepoHostIdentity } from '../../../../../shared/repo-host-identity' +import type { Repo } from '../../../../../../shared/repo-types' +import type { JiraProjectReference } from '../../../../../../shared/jira/jira-project-reference' +import { + getRepoExecutionHostId, + type ExecutionHostId +} from '../../../../../../shared/execution-host' +import { resolveJiraSourceHostId } from '../../../../../../shared/jira/jira-source-host' +import { getRepoHostIdentity } from '../../../../../../shared/repo-host-identity' export function useTaskPageJiraProjectScope( repos: readonly Repo[], diff --git a/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-quick-filters.test.tsx b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-quick-filters.test.tsx new file mode 100644 index 00000000000..22a609c5a82 --- /dev/null +++ b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-quick-filters.test.tsx @@ -0,0 +1,59 @@ +// @vitest-environment happy-dom +import { act, cleanup, renderHook, waitFor } from '@testing-library/react' +import { afterEach, beforeEach, expect, it, vi } from 'vitest' +import { useTaskPageJiraQuickFilters } from './use-task-page-jira-quick-filters' +import type { JiraQuickFilterState } from '../../../../../../shared/jira/jira-quick-filters' +import { toRuntimeExecutionHostId } from '../../../../../../shared/execution-host' +const { read, write } = vi.hoisted(() => ({ read: vi.fn(), write: vi.fn() })) +vi.mock('@/runtime/jira/runtime-jira-quick-filter-state', () => ({ + readJiraQuickFilterState: read, + writeJiraQuickFilterState: write +})) +beforeEach(() => { + vi.resetAllMocks() +}) +afterEach(cleanup) +function model( + host: string | null, + accountHost = 'account-owner' +): Parameters[0] { + return { + accountBackedTaskSourceHostId: toRuntimeExecutionHostId(accountHost), + taskSource: 'jira', + taskResumeApplied: true, + jiraTaskSourceContext: host + ? { + kind: 'task-source', + provider: 'jira', + projectId: 'project', + hostId: toRuntimeExecutionHostId(host) + } + : null + } +} +it('ignores a late mutation reply after changing Jira execution hosts', async () => { + read + .mockResolvedValueOnce({ statuses: ['Blocked'] }) + .mockResolvedValueOnce({ statuses: ['Open'] }) + let finish: (value: JiraQuickFilterState) => void = () => {} + write.mockReturnValue( + new Promise((resolve) => { + finish = resolve + }) + ) + const { result, rerender } = renderHook(({ host }) => useTaskPageJiraQuickFilters(model(host)), { + initialProps: { host: 'a' } + }) + await waitFor(() => expect(result.current.jiraQuickFiltersLoading).toBe(false)) + let pending: Promise = Promise.resolve() + act(() => { + pending = result.current.selectJiraStatuses(['In Progress']) + }) + rerender({ host: 'b' }) + await waitFor(() => expect(result.current.selectedJiraStatuses).toEqual(['Open'])) + await act(async () => { + finish({ statuses: ['In Progress'] }) + await pending + }) + expect(result.current.selectedJiraStatuses).toEqual(['Open']) +}) diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-quick-filters.ts b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-quick-filters.ts similarity index 96% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-quick-filters.ts rename to src/renderer/src/components/task-page/jira/filters/use-task-page-jira-quick-filters.ts index 643fc68755e..20975f4b884 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-quick-filters.ts +++ b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-quick-filters.ts @@ -7,8 +7,8 @@ import { import { normalizeJiraQuickFilterState, type JiraQuickFilterState -} from '../../../../../shared/jira/jira-quick-filters' -import type { TaskPageLinearViewStateModel } from '../linear/use-task-page-linear-view-state' +} from '../../../../../../shared/jira/jira-quick-filters' +import type { TaskPageLinearViewStateModel } from '../../linear/use-task-page-linear-view-state' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-saved-filters.test.tsx b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-saved-filters.test.tsx new file mode 100644 index 00000000000..9d536b63eb9 --- /dev/null +++ b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-saved-filters.test.tsx @@ -0,0 +1,58 @@ +// @vitest-environment happy-dom +import { afterEach, beforeEach, expect, it, vi } from 'vitest' +import { act, cleanup, renderHook, waitFor } from '@testing-library/react' + +import { useTaskPageJiraSavedFilters } from './use-task-page-jira-saved-filters' +import { toRuntimeExecutionHostId } from '../../../../../../shared/execution-host' +const { read, mutate } = vi.hoisted(() => ({ read: vi.fn(), mutate: vi.fn() })) +vi.mock('@/runtime/jira/runtime-jira-saved-filter-state', () => ({ + readJiraSavedFilterState: read, + mutateJiraSavedFilters: mutate +})) +afterEach(cleanup) +beforeEach(() => { + vi.resetAllMocks() +}) +const filter = { id: 'mine', name: 'My work', jql: 'project = APP' } +const initial = { filters: [filter], activeFilterId: filter.id } +function model( + host: string | null, + accountHost = 'account-owner' +): Parameters[0] { + return { + taskSource: 'jira', + taskResumeApplied: true, + accountBackedTaskSourceHostId: toRuntimeExecutionHostId(accountHost), + jiraTaskSourceContext: host + ? { + kind: 'task-source', + provider: 'jira', + projectId: 'project', + hostId: toRuntimeExecutionHostId(host) + } + : null + } +} +it('ignores an old host read that completes after switching execution hosts', async () => { + let resolveOld: (value: typeof initial) => void = () => {} + read + .mockImplementationOnce( + () => + new Promise((resolve) => { + resolveOld = resolve + }) + ) + .mockResolvedValueOnce({ filters: [], activeFilterId: null }) + const setQuery = vi.fn() + const { result, rerender } = renderHook( + ({ host }) => useTaskPageJiraSavedFilters(model(host), setQuery), + { initialProps: { host: 'host-a' } } + ) + rerender({ host: 'host-b' }) + await waitFor(() => expect(result.current.jiraSavedFiltersReady).toBe(true)) + await act(async () => { + resolveOld(initial) + }) + expect(result.current.jiraSavedFilters).toEqual([]) + expect(setQuery).not.toHaveBeenCalledWith(filter.jql) +}) diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-saved-filters.ts b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-saved-filters.ts similarity index 96% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-saved-filters.ts rename to src/renderer/src/components/task-page/jira/filters/use-task-page-jira-saved-filters.ts index 33a251c9baa..249b7f66ec7 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-saved-filters.ts +++ b/src/renderer/src/components/task-page/jira/filters/use-task-page-jira-saved-filters.ts @@ -1,4 +1,4 @@ -import { createNonSecureContextUuid } from '../../../../../shared/non-secure-context-uuid' +import { createNonSecureContextUuid } from '../../../../../../shared/non-secure-context-uuid' import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' import { ipcOrRuntimeTargetForHostId } from '@/runtime/rpc/targets/runtime-client-target' import { @@ -9,8 +9,8 @@ import type { JiraSavedFilterMutation, JiraSavedFilter, JiraSavedFilterState -} from '../../../../../shared/jira/jira-saved-filters' -import type { TaskPageLinearViewStateModel } from '../linear/use-task-page-linear-view-state' +} from '../../../../../../shared/jira/jira-saved-filters' +import type { TaskPageLinearViewStateModel } from '../../linear/use-task-page-linear-view-state' import { translate } from '@/i18n/i18n' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-cache-selectors.ts b/src/renderer/src/components/task-page/jira/listing/task-page-jira-cache-selectors.ts similarity index 91% rename from src/renderer/src/components/task-page/jira/task-page-jira-cache-selectors.ts rename to src/renderer/src/components/task-page/jira/listing/task-page-jira-cache-selectors.ts index 3cd7c652cf2..1a82b394c33 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-cache-selectors.ts +++ b/src/renderer/src/components/task-page/jira/listing/task-page-jira-cache-selectors.ts @@ -2,8 +2,8 @@ import type { CacheEntry } from '@/store/github/cache/cache-model' import { getTaskSourceCacheScope, type TaskSourceContext -} from '../../../../../shared/work-items/task-source-context' -import type { JiraIssue } from '../../../../../shared/jira/jira-types' +} from '../../../../../../shared/work-items/task-source-context' +import type { JiraIssue } from '../../../../../../shared/jira/jira-types' type JiraIssueCache = Record> type JiraSearchCache = Record> diff --git a/src/renderer/src/components/task-page/jira/listing/task-page-jira-error-banner.tsx b/src/renderer/src/components/task-page/jira/listing/task-page-jira-error-banner.tsx new file mode 100644 index 00000000000..66c7e3348d0 --- /dev/null +++ b/src/renderer/src/components/task-page/jira/listing/task-page-jira-error-banner.tsx @@ -0,0 +1,52 @@ +import { useTranslate } from '../../../../i18n/use-translate' +import type { TaskPageJiraLoadError } from './task-page-jira-load-state' +import React from 'react' +import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '../../../ui/collapsible' +import { AlertCircle, ChevronDown, ChevronRight } from 'lucide-react' +import { Button } from '../../../ui/button' + +export function TaskPageJiraErrorBanner({ + error, + open, + onOpenChange +}: { + error: TaskPageJiraLoadError + open: boolean + onOpenChange: (open: boolean) => void +}): React.JSX.Element { + const t = useTranslate() + return ( + +
+ +
+
{error.title}
+ {error.details ? ( + <> + + + + +
+ {error.details} +
+
+ + ) : null} +
+
+ + ) +} diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-issue-cells.tsx b/src/renderer/src/components/task-page/jira/listing/task-page-jira-issue-cells.tsx similarity index 97% rename from src/renderer/src/components/task-page/jira/task-page-jira-issue-cells.tsx rename to src/renderer/src/components/task-page/jira/listing/task-page-jira-issue-cells.tsx index 62d2d04066a..9aa443b1006 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-issue-cells.tsx +++ b/src/renderer/src/components/task-page/jira/listing/task-page-jira-issue-cells.tsx @@ -4,12 +4,12 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip import { translate } from '@/i18n/i18n' import { useIntlLocale, useTranslate, type Translate } from '@/i18n/use-translate' import { cn } from '@/lib/class-names' -import type { JiraIssue } from '../../../../../shared/jira/jira-types' +import type { JiraIssue } from '../../../../../../shared/jira/jira-types' import { formatJiraEstimate, formatJiraStoryPoints, type JiraListColumnId -} from '../../jira-list-columns' +} from '../../../jira-list-columns' import { getJiraPriorityTone } from './task-page-jira-status-tone' const MUTED_CELL = 'block truncate text-[12px] text-muted-foreground' diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-issue-list.tsx b/src/renderer/src/components/task-page/jira/listing/task-page-jira-issue-list.tsx similarity index 97% rename from src/renderer/src/components/task-page/jira/task-page-jira-issue-list.tsx rename to src/renderer/src/components/task-page/jira/listing/task-page-jira-issue-list.tsx index c54a848b1dc..ff2f91ea43b 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-issue-list.tsx +++ b/src/renderer/src/components/task-page/jira/listing/task-page-jira-issue-list.tsx @@ -6,12 +6,12 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/component import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { useTranslate } from '@/i18n/use-translate' import { cn } from '@/lib/class-names' -import { JiraIssueRowCopyMenu } from '../../JiraIssueRowCopyMenu' -import { JiraLinkedWorkspaceAction } from '../../JiraLinkedWorkspaceAction' -import type { Worktree } from '../../../../../shared/worktree/types' -import { jiraListColumnVisibility, type JiraListColumn } from '../../jira-list-columns' +import { JiraIssueRowCopyMenu } from '../../../JiraIssueRowCopyMenu' +import { JiraLinkedWorkspaceAction } from '../../../JiraLinkedWorkspaceAction' +import type { Worktree } from '../../../../../../shared/worktree/types' +import { jiraListColumnVisibility, type JiraListColumn } from '../../../jira-list-columns' import { JiraIssueCell, JiraPriorityText, unassignedLabel } from './task-page-jira-issue-cells' -import type { JiraIssue, JiraProjectStatusOrder } from '../../../../../shared/jira/jira-types' +import type { JiraIssue, JiraProjectStatusOrder } from '../../../../../../shared/jira/jira-types' import { openAppHttpLink } from '@/lib/browser/navigation/app-http-link-open' export type TaskPageJiraIssueSection = { diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-linked-issues.ts b/src/renderer/src/components/task-page/jira/listing/task-page-jira-linked-issues.ts similarity index 89% rename from src/renderer/src/components/task-page/jira/task-page-jira-linked-issues.ts rename to src/renderer/src/components/task-page/jira/listing/task-page-jira-linked-issues.ts index f7dd8ccabf2..7a1b78d8815 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-linked-issues.ts +++ b/src/renderer/src/components/task-page/jira/listing/task-page-jira-linked-issues.ts @@ -1,7 +1,7 @@ -import type { JiraIssue } from '../../../../../shared/jira/jira-types' +import type { JiraIssue } from '../../../../../../shared/jira/jira-types' import type { LinkedJiraIssueRef } from '@/lib/work-items/jira/jira-issue-workspace-attachment' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' -import { readLinkedTaskIssuesWithLimit } from '../../read-linked-task-issues' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' +import { readLinkedTaskIssuesWithLimit } from '../../../read-linked-task-issues' import { createTaskPageJiraLoadFailureState } from './task-page-jira-load-state' import { translate } from '@/i18n/i18n' diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-load-state.ts b/src/renderer/src/components/task-page/jira/listing/task-page-jira-load-state.ts similarity index 95% rename from src/renderer/src/components/task-page/jira/task-page-jira-load-state.ts rename to src/renderer/src/components/task-page/jira/listing/task-page-jira-load-state.ts index e5940978fdb..7fe760f476e 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-load-state.ts +++ b/src/renderer/src/components/task-page/jira/listing/task-page-jira-load-state.ts @@ -1,5 +1,5 @@ -import type { JiraIssue } from '../../../../../shared/jira/jira-types' -import { parseJiraStatusError } from '../../../../../shared/jira/jira-status-error' +import type { JiraIssue } from '../../../../../../shared/jira/jira-types' +import { parseJiraStatusError } from '../../../../../../shared/jira/jira-status-error' import { translate } from '@/i18n/i18n' export type TaskPageJiraLoadError = { diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-search.ts b/src/renderer/src/components/task-page/jira/listing/task-page-jira-search.ts similarity index 87% rename from src/renderer/src/components/task-page/jira/task-page-jira-search.ts rename to src/renderer/src/components/task-page/jira/listing/task-page-jira-search.ts index 80e3669481c..34b7d68fbe4 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-search.ts +++ b/src/renderer/src/components/task-page/jira/listing/task-page-jira-search.ts @@ -1,6 +1,9 @@ -import type { JiraIssue } from '../../../../../shared/jira/jira-types' -import { buildJiraTextMatchJql, mayBeJql } from '../../../../../shared/jira/jira-search-input-jql' -import { searchJiraIssuesByKeyOrText } from '../../jira-key-or-text-search' +import type { JiraIssue } from '../../../../../../shared/jira/jira-types' +import { + buildJiraTextMatchJql, + mayBeJql +} from '../../../../../../shared/jira/jira-search-input-jql' +import { searchJiraIssuesByKeyOrText } from '../../../jira-key-or-text-search' import { getJiraBadRequestReason } from './task-page-jira-load-state' export type TaskPageJiraJqlRejection = { diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-sort-controls.tsx b/src/renderer/src/components/task-page/jira/listing/task-page-jira-sort-controls.tsx similarity index 98% rename from src/renderer/src/components/task-page/jira/task-page-jira-sort-controls.tsx rename to src/renderer/src/components/task-page/jira/listing/task-page-jira-sort-controls.tsx index bbd2312c42f..57babdf9f10 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-sort-controls.tsx +++ b/src/renderer/src/components/task-page/jira/listing/task-page-jira-sort-controls.tsx @@ -15,8 +15,8 @@ import { getJiraListColumnLabel, jiraListColumnVisibility, type JiraListColumn -} from '../../jira-list-columns' -import type { JiraIssueSortColumn, JiraIssueSortDirection } from '../../jira-issue-sorter' +} from '../../../jira-list-columns' +import type { JiraIssueSortColumn, JiraIssueSortDirection } from '../../../jira-issue-sorter' type TaskPageJiraSortControlsProps = { columns: readonly JiraListColumn[] diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-status-order.ts b/src/renderer/src/components/task-page/jira/listing/task-page-jira-status-order.ts similarity index 95% rename from src/renderer/src/components/task-page/jira/task-page-jira-status-order.ts rename to src/renderer/src/components/task-page/jira/listing/task-page-jira-status-order.ts index adef776958e..48d3bfabb27 100644 --- a/src/renderer/src/components/task-page/jira/task-page-jira-status-order.ts +++ b/src/renderer/src/components/task-page/jira/listing/task-page-jira-status-order.ts @@ -1,5 +1,5 @@ -import type { JiraIssue, JiraProjectStatusOrder } from '../../../../../shared/jira/jira-types' -import type { RuntimeClientTarget } from '../../../../../shared/runtime-client-target' +import type { JiraIssue, JiraProjectStatusOrder } from '../../../../../../shared/jira/jira-types' +import type { RuntimeClientTarget } from '../../../../../../shared/runtime-client-target' import { runtimeTargetEnvironmentId } from '@/runtime/rpc/targets/runtime-client-target' import { getEnvironmentRuntimeContextKey } from '@/runtime/hosts/observations/provider-runtime-context' import { jiraGetProjectStatusOrder } from '@/runtime/jira/runtime-jira-client' diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-status-tone.ts b/src/renderer/src/components/task-page/jira/listing/task-page-jira-status-tone.ts similarity index 100% rename from src/renderer/src/components/task-page/jira/task-page-jira-status-tone.ts rename to src/renderer/src/components/task-page/jira/listing/task-page-jira-status-tone.ts diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-text-fallback-notice.tsx b/src/renderer/src/components/task-page/jira/listing/task-page-jira-text-fallback-notice.tsx similarity index 100% rename from src/renderer/src/components/task-page/jira/task-page-jira-text-fallback-notice.tsx rename to src/renderer/src/components/task-page/jira/listing/task-page-jira-text-fallback-notice.tsx diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-list-effects.ts b/src/renderer/src/components/task-page/jira/listing/use-task-page-jira-list-effects.ts similarity index 92% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-list-effects.ts rename to src/renderer/src/components/task-page/jira/listing/use-task-page-jira-list-effects.ts index 011c0d589a9..f51132496cc 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-list-effects.ts +++ b/src/renderer/src/components/task-page/jira/listing/use-task-page-jira-list-effects.ts @@ -1,26 +1,26 @@ import { translate } from '@/i18n/i18n' import { isRuntimeProviderSearchQueryWithinLimit } from '@/runtime/issue-search/runtime-provider-search-bounds' -import { withCurrentSprintClause } from '../../../../../shared/jira/jira-jql-predicate' +import { withCurrentSprintClause } from '../../../../../../shared/jira/jira-jql-predicate' import { buildJiraStatusCatalog, getJiraStatusCatalogScopeKey -} from './task-page-jira-status-catalog' -import type { TaskPageLinearCollectionEffectsModel } from '../linear/use-task-page-linear-collection-effects' +} from '../filters/task-page-jira-status-catalog' +import type { TaskPageLinearCollectionEffectsModel } from '../../linear/projects/use-task-page-linear-collection-effects' import { useEffect, useMemo, useRef } from 'react' import { getSingleJiraProjectScope, getTaskPageJiraStatusOrderScopeKey, getTaskPageJiraStatusOrderRuntimeScopeKey, loadTaskPageJiraProjectStatusOrder -} from '@/components/task-page/jira/task-page-jira-status-order' -import { createTaskPageJiraLoadFailureState } from '@/components/task-page/jira/task-page-jira-load-state' -import { searchTaskPageJiraIssues } from '@/components/task-page/jira/task-page-jira-search' -import { JIRA_ITEM_LIMIT } from '../task-page-source-context' -import { useTaskSearchDebouncedApply } from '../use-task-search-debounced-apply' +} from '@/components/task-page/jira/listing/task-page-jira-status-order' +import { createTaskPageJiraLoadFailureState } from '@/components/task-page/jira/listing/task-page-jira-load-state' +import { searchTaskPageJiraIssues } from '@/components/task-page/jira/listing/task-page-jira-search' +import { JIRA_ITEM_LIMIT } from '../../task-page-source-context' +import { useTaskSearchDebouncedApply } from '../../use-task-search-debounced-apply' import { readLinkedJiraIssues } from './task-page-jira-linked-issues' import { ipcOrRuntimeTargetForHostId } from '@/runtime/rpc/targets/runtime-client-target' -import { buildJiraPresetJql } from '../../../../../shared/jira/jira-preset-jql' -import { restrictJiraQueryToProject } from '../../../../../shared/jira/jira-project-reference' +import { buildJiraPresetJql } from '../../../../../../shared/jira/jira-preset-jql' +import { restrictJiraQueryToProject } from '../../../../../../shared/jira/jira-project-reference' export function useTaskPageJiraListEffects(model: TaskPageLinearCollectionEffectsModel) { const { accountBackedTaskSourceHostId, diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-list-projection.ts b/src/renderer/src/components/task-page/jira/listing/use-task-page-jira-list-projection.ts similarity index 93% rename from src/renderer/src/components/task-page/jira/use-task-page-jira-list-projection.ts rename to src/renderer/src/components/task-page/jira/listing/use-task-page-jira-list-projection.ts index 5e5afd56182..82ff87c1ec0 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-list-projection.ts +++ b/src/renderer/src/components/task-page/jira/listing/use-task-page-jira-list-projection.ts @@ -1,11 +1,11 @@ -import type { TaskPageLinearBoardModel } from '../linear/use-task-page-linear-board' +import type { TaskPageLinearBoardModel } from '../../linear/issues/use-task-page-linear-board' import { useMemo, useCallback } from 'react' -import { findTaskPageJiraIssue } from '@/components/task-page/jira/task-page-jira-cache-selectors' +import { findTaskPageJiraIssue } from '@/components/task-page/jira/listing/task-page-jira-cache-selectors' import { getSingleJiraProjectScope, getTaskPageJiraStatusOrderScopeKey, getTaskPageJiraStatusOrderRuntimeScopeKey -} from '@/components/task-page/jira/task-page-jira-status-order' +} from '@/components/task-page/jira/listing/task-page-jira-status-order' import { ipcOrRuntimeTargetForHostId } from '@/runtime/rpc/targets/runtime-client-target' import { buildJiraIssueWorkspaceAttachmentIndex, @@ -13,7 +13,7 @@ import { findJiraIssueWorkspaceAttachmentsInIndex } from '@/lib/work-items/jira/jira-issue-workspace-attachment' import { filterLinkedJiraIssues } from './task-page-jira-linked-issues' -import { sortJiraIssues } from '../../jira-issue-sorter' +import { sortJiraIssues } from '../../../jira-issue-sorter' export function useTaskPageJiraListProjection(model: TaskPageLinearBoardModel) { const { accountBackedTaskSourceHostId, diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-board-transitions.test.ts b/src/renderer/src/components/task-page/jira/task-page-jira-board-transitions.test.ts deleted file mode 100644 index 54a512bc239..00000000000 --- a/src/renderer/src/components/task-page/jira/task-page-jira-board-transitions.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { JiraIssue, JiraStatus, JiraTransition } from '../../../../../shared/jira/jira-types' -import type { RuntimeClientTarget } from '../../../../../shared/runtime-client-target' - -const mocks = vi.hoisted(() => ({ - jiraListTransitionsForTarget: vi.fn() -})) - -vi.mock('@/runtime/jira/runtime-jira-detail-client', () => ({ - jiraListTransitionsForTarget: mocks.jiraListTransitionsForTarget -})) -vi.mock('@/runtime/jira/runtime-jira-detail-client', () => ({ - jiraListTransitionsForTarget: mocks.jiraListTransitionsForTarget -})) - -const { loadTaskPageJiraIssueTransitions } = await import('./task-page-jira-board-transitions') -const target: RuntimeClientTarget = { kind: 'local' } - -function status(id: string, name: string): JiraStatus { - return { id, name, categoryKey: 'indeterminate', categoryName: 'In Progress' } -} - -function transition(id: string, to: JiraStatus): JiraTransition { - return { id, name: `To ${to.name}`, to } -} - -function issue(key: string, statusValue: JiraStatus, siteId?: string): JiraIssue { - return { - id: key, - key, - siteId, - title: key, - url: `https://example.atlassian.net/browse/${key}`, - project: { id: 'p1', key: 'STA', name: 'Stably' }, - issueType: { id: 't1', name: 'Task' }, - status: statusValue, - labels: [], - updatedAt: '2026-08-07T12:00:00.000Z', - createdAt: '2026-08-07T12:00:00.000Z' - } -} - -describe('loadTaskPageJiraIssueTransitions', () => { - beforeEach(() => { - mocks.jiraListTransitionsForTarget.mockReset() - }) - - it('surfaces a failed transitions request and allows an explicit retry', async () => { - mocks.jiraListTransitionsForTarget.mockRejectedValue(new Error('offline')) - await expect( - loadTaskPageJiraIssueTransitions( - target, - 'scope', - issue('STA-500', status('2', 'In Progress')) - ) - ).rejects.toThrow('offline') - mocks.jiraListTransitionsForTarget.mockResolvedValue([]) - await expect( - loadTaskPageJiraIssueTransitions( - target, - 'scope', - issue('STA-500', status('2', 'In Progress')), - true - ) - ).resolves.toEqual([]) - expect(mocks.jiraListTransitionsForTarget).toHaveBeenCalledTimes(2) - }) - - it('keeps concurrent local and remote metadata apart even with the same scope', async () => { - const remote: RuntimeClientTarget = { kind: 'environment', environmentId: 'owner' } - const currentIssue = issue('STA-OWNER', status('2', 'In Progress'), 'site-a') - const localAction = transition('local-done', status('3', 'Done')) - const remoteAction = transition('remote-done', status('3', 'Done')) - mocks.jiraListTransitionsForTarget.mockImplementation((owner: RuntimeClientTarget) => - Promise.resolve(owner.kind === 'local' ? [localAction] : [remoteAction]) - ) - await expect( - Promise.all([ - loadTaskPageJiraIssueTransitions(target, 'same-scope', currentIssue), - loadTaskPageJiraIssueTransitions(remote, 'same-scope', currentIssue) - ]) - ).resolves.toEqual([[localAction], [remoteAction]]) - await expect( - loadTaskPageJiraIssueTransitions(remote, 'same-scope', currentIssue) - ).resolves.toEqual([remoteAction]) - expect(mocks.jiraListTransitionsForTarget).toHaveBeenCalledTimes(2) - expect(mocks.jiraListTransitionsForTarget).toHaveBeenCalledWith( - remote, - currentIssue.key, - currentIssue.siteId - ) - }) -}) diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-favourite-selection.test.tsx b/src/renderer/src/components/task-page/jira/task-page-jira-favourite-selection.test.tsx deleted file mode 100644 index 364a38f2b47..00000000000 --- a/src/renderer/src/components/task-page/jira/task-page-jira-favourite-selection.test.tsx +++ /dev/null @@ -1,89 +0,0 @@ -// @vitest-environment happy-dom -import { act, cleanup, renderHook, waitFor } from '@testing-library/react' -import { afterEach, beforeEach, expect, it } from 'vitest' - -import { - favouriteTransport as transport, - favourite, - named, - emptySaved, - resetFavouriteTransport, - useFavouriteTaskPage, - type FavouriteFixtureProps -} from '../../__mocks__/task-page-jira-favourite-fixture' - -afterEach(cleanup) -beforeEach(() => { - localStorage.clear() - resetFavouriteTransport() -}) - -async function ready(result: { current: ReturnType }) { - act(() => result.current.loadJiraFavouriteFilters()) - await waitFor(() => { - expect(result.current.jiraSavedFiltersLoading).toBe(false) - expect(result.current.jiraFavouriteFiltersLoading).toBe(false) - expect(result.current.jiraLoading).toBe(false) - }) -} - -it('waits for confirmed named deselection before changing query or linked mode', async () => { - transport.readSaved.mockResolvedValue({ ...emptySaved, activeFilterId: named.id }) - const { result } = renderHook(() => useFavouriteTaskPage()) - await ready(result) - act(() => result.current.setJiraHasWorkspace(true)) - const deferred = Promise.withResolvers() - transport.mutateSaved.mockReturnValueOnce(deferred.promise) - let applying = Promise.resolve(false) - act(() => { - applying = result.current.applyJiraFavouriteFilter(result.current.jiraFavouriteFilters[0]) - }) - expect(result.current.jiraFavouriteFilter).toBeNull() - expect(result.current.jiraSearchInput).toBe(named.jql) - expect(result.current.jiraHasWorkspace).toBe(true) - await act(async () => { - deferred.resolve(emptySaved) - expect(await applying).toBe(true) - }) - expect(result.current.jiraHasWorkspace).toBe(false) - expect(result.current.jiraSearchInput).toBe('') - expect(result.current.jiraFavouriteFilter?.id).toBe(favourite.id) - expect(transport.mutateSaved).toHaveBeenCalledExactlyOnceWith( - { kind: 'environment', environmentId: 'owner-a' }, - { kind: 'select', id: null } - ) -}) - -it.each(['site', 'owner', 'disabled'] as const)( - 'fences a delayed handoff across %s A→B→A', - async (kind) => { - transport.readSaved.mockResolvedValue({ ...emptySaved, activeFilterId: named.id }) - const { result, rerender } = renderHook( - (props: FavouriteFixtureProps) => useFavouriteTaskPage(props), - { - initialProps: {} - } - ) - await ready(result) - const deferred = Promise.withResolvers() - transport.mutateSaved.mockReturnValueOnce(deferred.promise) - let applying = Promise.resolve(false) - act(() => { - applying = result.current.applyJiraFavouriteFilter(result.current.jiraFavouriteFilters[0]) - }) - const other: FavouriteFixtureProps = { - hostId: kind === 'owner' ? 'runtime:owner-b' : 'runtime:owner-a', - siteId: kind === 'site' ? 'site-b' : 'site-a', - enabled: kind !== 'disabled' - } - rerender(other) - rerender({ hostId: 'runtime:owner-a', siteId: 'site-a', enabled: true }) - await ready(result) - await act(async () => { - deferred.resolve(emptySaved) - expect(await applying).toBe(false) - }) - expect(result.current.jiraFavouriteFilter).toBeNull() - expect(result.current.jiraSearchInput).not.toContain('12345') - } -) diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-grouping.test.ts b/src/renderer/src/components/task-page/jira/task-page-jira-grouping.test.ts deleted file mode 100644 index 4aa765c9cb8..00000000000 --- a/src/renderer/src/components/task-page/jira/task-page-jira-grouping.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { defaultJiraListColumnIds, visibleJiraListColumns } from '../../jira-list-columns' -// @vitest-environment happy-dom - -import '@testing-library/jest-dom/vitest' - -import React from 'react' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { cleanup, render, screen } from '@testing-library/react' -import userEvent from '@testing-library/user-event' - -import { TooltipProvider } from '@/components/ui/tooltip' - -import { TaskPageJiraIssueList } from './task-page-jira-issue-list' - -import type { JiraIssue } from '../../../../../shared/jira/jira-types' - -const { jiraGetProjectStatusOrderMock } = vi.hoisted(() => ({ - jiraGetProjectStatusOrderMock: vi.fn() -})) - -vi.mock('@/runtime/jira/runtime-jira-client', () => ({ - jiraGetProjectStatusOrder: (...args: unknown[]) => jiraGetProjectStatusOrderMock(...args) -})) - -afterEach(() => { - cleanup() - vi.restoreAllMocks() - jiraGetProjectStatusOrderMock.mockReset() -}) - -function jiraIssue( - key: string, - title: string, - statusId: string, - statusName: string, - options: { priority?: string; projectKey?: string; siteId?: string } = {} -): JiraIssue { - const siteId = options.siteId ?? 'site-1' - const projectKey = options.projectKey ?? 'ALP' - return { - id: `${siteId}:${key}`, - key, - title, - url: `https://example.atlassian.net/browse/${key}`, - siteId, - siteName: 'Example Jira', - project: { id: projectKey, key: projectKey, name: projectKey, siteId }, - issueType: { id: '10001', name: 'Bug' }, - status: { id: statusId, name: statusName, categoryKey: 'new', categoryName: statusName }, - priority: options.priority ? { id: options.priority, name: options.priority } : undefined, - labels: [], - createdAt: '2026-01-01T00:00:00.000Z', - updatedAt: '2026-01-01T00:00:00.000Z' - } -} - -describe('Jira issue status grouping', () => { - it('collapses and expands a status group through its accessible trigger', async () => { - const user = userEvent.setup() - render( - React.createElement( - TooltipProvider, - null, - React.createElement(TaskPageJiraIssueList, { - columns: visibleJiraListColumns(defaultJiraListColumnIds()), - formatUpdatedAt: () => 'today', - getStatusTone: () => 'border-border', - issues: [ - jiraIssue('ALP-1', 'First issue', '1', 'To Do'), - jiraIssue('ALP-2', 'Second issue', '1', 'To Do') - ], - onOpenIssue: vi.fn(), - onStartWorkspace: vi.fn(), - selectedIssue: null, - showSiteContext: false, - statusOrder: null - }) - ) - ) - - const trigger = screen.getByRole('button', { name: 'To Do 2' }) - expect(trigger).toHaveAttribute('data-variant', 'ghost') - expect(trigger).toHaveAttribute('aria-expanded', 'true') - expect(screen.getByText('First issue')).toBeInTheDocument() - - await user.click(trigger) - expect(trigger).toHaveAttribute('aria-expanded', 'false') - expect(screen.queryByText('First issue')).not.toBeInTheDocument() - - await user.click(trigger) - expect(screen.getByText('First issue')).toBeInTheDocument() - }) -}) diff --git a/src/renderer/src/components/task-page/jira/task-page-jira-quick-copy.test.tsx b/src/renderer/src/components/task-page/jira/task-page-jira-quick-copy.test.tsx deleted file mode 100644 index f74d5515a43..00000000000 --- a/src/renderer/src/components/task-page/jira/task-page-jira-quick-copy.test.tsx +++ /dev/null @@ -1,122 +0,0 @@ -// @vitest-environment happy-dom -import '@testing-library/jest-dom/vitest' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { cleanup, render, screen, waitFor } from '@testing-library/react' -import userEvent from '@testing-library/user-event' -import { TooltipProvider } from '@/components/ui/tooltip' -import { TaskPageJiraIssueList } from './task-page-jira-issue-list' -import { defaultJiraListColumnIds, visibleJiraListColumns } from '../../jira-list-columns' -import type { JiraIssue } from '../../../../../shared/jira/jira-types' -const writeClipboardText = vi.fn().mockResolvedValue(undefined) -const issue: JiraIssue = { - id: '1', - key: 'APP-1', - title: 'Fix login', - url: 'https://acme.atlassian.net/browse/APP-1', - project: { id: '1', key: 'APP', name: 'App' }, - issueType: { id: '1', name: 'Bug' }, - status: { id: '1', name: 'Open', categoryKey: 'new', categoryName: 'Open' }, - labels: [], - createdAt: '2026-10-01', - updatedAt: '2026-10-01' -} -const onOpenIssue = vi.fn() -function renderList() { - return render( - - 'Today'} - getStatusTone={() => ''} - onOpenIssue={onOpenIssue} - onStartWorkspace={vi.fn()} - /> - - ) -} -beforeEach(() => { - Object.assign(window, { api: { ui: { writeClipboardText } } }) - vi.clearAllMocks() - localStorage.clear() -}) -afterEach(cleanup) -describe('Jira row quick copy', () => { - it('offers the same copy actions in the row context menu', async () => { - renderList() - const user = userEvent.setup() - await user.pointer({ keys: '[MouseRight]', target: screen.getByText('Fix login') }) - await user.click(screen.getByRole('menuitem', { name: 'Copy URL' })) - expect(writeClipboardText).toHaveBeenCalledWith(issue.url) - expect(onOpenIssue).not.toHaveBeenCalled() - }) - - it('renders a template preview with additional instructions and copies it', async () => { - renderList() - const user = userEvent.setup() - await user.click(screen.getByRole('button', { name: 'Copy context for APP-1' })) - await user.click(screen.getByRole('menuitem', { name: 'Copy prompt from template' })) - await user.type( - screen.getByLabelText('Additional instructions (optional)'), - 'Cover SSH behavior.' - ) - const preview = screen.getByLabelText('Preview') - expect(preview).toBeInstanceOf(HTMLTextAreaElement) - if (!(preview instanceof HTMLTextAreaElement)) { - throw new Error('Expected a textarea preview') - } - expect(preview.value).toContain('Cover SSH behavior.') - await user.click(screen.getByRole('button', { name: 'Copy prompt' })) - expect(writeClipboardText).toHaveBeenCalledWith(expect.stringContaining('APP-1')) - expect(writeClipboardText).toHaveBeenCalledWith(expect.stringContaining('Cover SSH behavior.')) - expect( - screen.queryByRole('dialog', { name: 'Copy prompt from template' }) - ).not.toBeInTheDocument() - await waitFor(() => - expect(screen.getByRole('button', { name: 'Copy context for APP-1' })).toHaveFocus() - ) - }) - - it('reuses a custom template after closing and reopening the picker', async () => { - renderList() - const user = userEvent.setup() - async function openCustomTemplate() { - await user.click(screen.getByRole('button', { name: 'Copy context for APP-1' })) - await user.click(screen.getByRole('menuitem', { name: 'Copy prompt from template' })) - await user.click(screen.getByRole('combobox', { name: 'Template' })) - await user.click(screen.getByRole('option', { name: 'Custom template' })) - } - await openCustomTemplate() - await user.clear(screen.getByLabelText('Template text')) - await user.type(screen.getByLabelText('Template text'), 'Review $jira-issue') - await user.click(screen.getByRole('button', { name: 'Cancel' })) - await openCustomTemplate() - expect(screen.getByLabelText('Template text')).toHaveValue('Review $jira-issue') - await user.click(screen.getByRole('button', { name: 'Copy prompt' })) - expect(writeClipboardText).toHaveBeenCalledWith( - `Review Complete Jira issue APP-1: Fix login\n\n${issue.url}` - ) - }) - - it('keeps the template picker open when the clipboard write fails', async () => { - writeClipboardText.mockRejectedValueOnce(new Error('Clipboard unavailable')) - renderList() - const user = userEvent.setup() - await user.click(screen.getByRole('button', { name: 'Copy context for APP-1' })) - await user.click(screen.getByRole('menuitem', { name: 'Copy prompt from template' })) - await user.click(screen.getByRole('button', { name: 'Copy prompt' })) - expect(screen.getByRole('dialog', { name: 'Copy prompt from template' })).toBeInTheDocument() - }) - - it('copies the key from the row without opening the issue', async () => { - renderList() - const user = userEvent.setup() - await user.click(screen.getByRole('button', { name: 'Copy context for APP-1' })) - await user.click(screen.getByRole('menuitem', { name: 'Copy key' })) - expect(writeClipboardText).toHaveBeenCalledWith('APP-1') - expect(onOpenIssue).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-list-state.ts b/src/renderer/src/components/task-page/jira/use-task-page-jira-list-state.ts index c41c110238e..c251c470fca 100644 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-list-state.ts +++ b/src/renderer/src/components/task-page/jira/use-task-page-jira-list-state.ts @@ -1,7 +1,7 @@ -import { useTaskPageJiraQuickFilters } from './use-task-page-jira-quick-filters' -import type { JiraStatusCatalogItem } from './task-page-jira-status-catalog' -import { useTaskPageJiraSavedFilters } from './use-task-page-jira-saved-filters' -import { useTaskPageJiraFavouriteFilters } from './use-task-page-jira-favourite-filters' +import { useTaskPageJiraQuickFilters } from './filters/use-task-page-jira-quick-filters' +import type { JiraStatusCatalogItem } from './filters/task-page-jira-status-catalog' +import { useTaskPageJiraSavedFilters } from './filters/use-task-page-jira-saved-filters' +import { useTaskPageJiraFavouriteFilters } from './filters/use-task-page-jira-favourite-filters' import type { JiraFavouriteFilter } from '../../../../../shared/jira/jira-favourite-filters' import type { JiraSavedFilter } from '../../../../../shared/jira/jira-saved-filters' import type { TaskPageLinearViewStateModel } from '../linear/use-task-page-linear-view-state' @@ -11,8 +11,8 @@ import type { JiraProjectStatusOrder, JiraPriority } from '../../../../../shared/jira/jira-types' -import type { TaskPageJiraLoadError } from '@/components/task-page/jira/task-page-jira-load-state' -import type { TaskPageJiraJqlRejection } from '@/components/task-page/jira/task-page-jira-search' +import type { TaskPageJiraLoadError } from '@/components/task-page/jira/listing/task-page-jira-load-state' +import type { TaskPageJiraJqlRejection } from '@/components/task-page/jira/listing/task-page-jira-search' import type { JiraPresetId } from '@/components/task-page/task-page-localized-options' import type { JiraIssueSortColumn, @@ -35,7 +35,7 @@ import { } from '@/runtime/rpc/targets/runtime-client-target' import { getEnvironmentRuntimeContextKey } from '@/runtime/hosts/observations/provider-runtime-context' import { getTaskSourceCacheScope } from '../../../../../shared/work-items/task-source-context' -import { useTaskPageJiraProjectScope } from './use-task-page-jira-project-scope' +import { useTaskPageJiraProjectScope } from './filters/use-task-page-jira-project-scope' const EMPTY_JIRA_PRIORITIES: JiraPrioritiesBySite = new Map() export function useTaskPageJiraListState(model: TaskPageLinearViewStateModel) { diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-quick-filters.test.tsx b/src/renderer/src/components/task-page/jira/use-task-page-jira-quick-filters.test.tsx deleted file mode 100644 index cdcdb449843..00000000000 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-quick-filters.test.tsx +++ /dev/null @@ -1,114 +0,0 @@ -// @vitest-environment happy-dom -import { act, cleanup, renderHook, waitFor } from '@testing-library/react' -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { useTaskPageJiraQuickFilters } from './use-task-page-jira-quick-filters' -import type { JiraQuickFilterState } from '../../../../../shared/jira/jira-quick-filters' -import { toRuntimeExecutionHostId } from '../../../../../shared/execution-host' -const { read, write } = vi.hoisted(() => ({ read: vi.fn(), write: vi.fn() })) -vi.mock('@/runtime/jira/runtime-jira-quick-filter-state', () => ({ - readJiraQuickFilterState: read, - writeJiraQuickFilterState: write -})) -beforeEach(() => { - vi.resetAllMocks() -}) -afterEach(cleanup) -function model( - host: string | null, - accountHost = 'account-owner' -): Parameters[0] { - return { - accountBackedTaskSourceHostId: toRuntimeExecutionHostId(accountHost), - taskSource: 'jira', - taskResumeApplied: true, - jiraTaskSourceContext: host - ? { - kind: 'task-source', - provider: 'jira', - projectId: 'project', - hostId: toRuntimeExecutionHostId(host) - } - : null - } -} -it('ignores a late mutation reply after changing Jira execution hosts', async () => { - read - .mockResolvedValueOnce({ statuses: ['Blocked'] }) - .mockResolvedValueOnce({ statuses: ['Open'] }) - let finish: (value: JiraQuickFilterState) => void = () => {} - write.mockReturnValue( - new Promise((resolve) => { - finish = resolve - }) - ) - const { result, rerender } = renderHook(({ host }) => useTaskPageJiraQuickFilters(model(host)), { - initialProps: { host: 'a' } - }) - await waitFor(() => expect(result.current.jiraQuickFiltersLoading).toBe(false)) - let pending: Promise = Promise.resolve() - act(() => { - pending = result.current.selectJiraStatuses(['In Progress']) - }) - rerender({ host: 'b' }) - await waitFor(() => expect(result.current.selectedJiraStatuses).toEqual(['Open'])) - await act(async () => { - finish({ statuses: ['In Progress'] }) - await pending - }) - expect(result.current.selectedJiraStatuses).toEqual(['Open']) -}) -it.each(['success', 'failure'] as const)( - 'keeps a new owner visit pending after an old %s settlement', - async (outcome) => { - read.mockResolvedValue({ statuses: ['Open'] }) - let finishOld: (state: JiraQuickFilterState) => void = () => {} - let failOld: (reason: Error) => void = () => {} - let finishNew: (state: JiraQuickFilterState) => void = () => {} - write - .mockReturnValueOnce( - new Promise((resolve, reject) => { - finishOld = resolve - failOld = reject - }) - ) - .mockReturnValueOnce( - new Promise((resolve) => { - finishNew = resolve - }) - ) - const { result, rerender } = renderHook( - ({ host }) => useTaskPageJiraQuickFilters(model(host)), - { initialProps: { host: 'a' } } - ) - await waitFor(() => expect(result.current.jiraQuickFiltersLoading).toBe(false)) - let oldWrite = Promise.resolve() - act(() => { - oldWrite = result.current.selectJiraStatuses(['Closed']).catch(() => {}) - }) - rerender({ host: 'b' }) - await waitFor(() => expect(result.current.jiraQuickFiltersLoading).toBe(false)) - rerender({ host: 'a' }) - await waitFor(() => expect(result.current.jiraQuickFiltersLoading).toBe(false)) - let newWrite = Promise.resolve() - act(() => { - newWrite = result.current.selectJiraStatuses(['In Progress']) - }) - await act(async () => { - if (outcome === 'success') { - finishOld({ statuses: ['Closed'] }) - } else { - failOld(new Error('Old request failed')) - } - await oldWrite - }) - expect(result.current.jiraQuickFiltersPending).toBe(true) - expect(result.current.selectedJiraStatuses).toEqual(['Open']) - expect(result.current.jiraQuickFiltersError).toBeNull() - await act(async () => { - finishNew({ statuses: ['In Progress'] }) - await newWrite - }) - expect(result.current.selectedJiraStatuses).toEqual(['In Progress']) - expect(result.current.jiraQuickFiltersPending).toBe(false) - } -) diff --git a/src/renderer/src/components/task-page/jira/use-task-page-jira-saved-filters.test.tsx b/src/renderer/src/components/task-page/jira/use-task-page-jira-saved-filters.test.tsx deleted file mode 100644 index 8853cfba223..00000000000 --- a/src/renderer/src/components/task-page/jira/use-task-page-jira-saved-filters.test.tsx +++ /dev/null @@ -1,149 +0,0 @@ -// @vitest-environment happy-dom -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { act, cleanup, renderHook, waitFor } from '@testing-library/react' -import { startTransition, Suspense } from 'react' -import { useTaskPageJiraSavedFilters } from './use-task-page-jira-saved-filters' -import { toRuntimeExecutionHostId } from '../../../../../shared/execution-host' -const { read, mutate } = vi.hoisted(() => ({ read: vi.fn(), mutate: vi.fn() })) -vi.mock('@/runtime/jira/runtime-jira-saved-filter-state', () => ({ - readJiraSavedFilterState: read, - mutateJiraSavedFilters: mutate -})) -afterEach(cleanup) -beforeEach(() => { - vi.resetAllMocks() -}) -const filter = { id: 'mine', name: 'My work', jql: 'project = APP' } -const initial = { filters: [filter], activeFilterId: filter.id } -function model( - host: string | null, - accountHost = 'account-owner' -): Parameters[0] { - return { - taskSource: 'jira', - taskResumeApplied: true, - accountBackedTaskSourceHostId: toRuntimeExecutionHostId(accountHost), - jiraTaskSourceContext: host - ? { - kind: 'task-source', - provider: 'jira', - projectId: 'project', - hostId: toRuntimeExecutionHostId(host) - } - : null - } -} -it('ignores an old host read that completes after switching execution hosts', async () => { - let resolveOld: (value: typeof initial) => void = () => {} - read - .mockImplementationOnce( - () => - new Promise((resolve) => { - resolveOld = resolve - }) - ) - .mockResolvedValueOnce({ filters: [], activeFilterId: null }) - const setQuery = vi.fn() - const { result, rerender } = renderHook( - ({ host }) => useTaskPageJiraSavedFilters(model(host), setQuery), - { initialProps: { host: 'host-a' } } - ) - rerender({ host: 'host-b' }) - await waitFor(() => expect(result.current.jiraSavedFiltersReady).toBe(true)) - await act(async () => { - resolveOld(initial) - }) - expect(result.current.jiraSavedFilters).toEqual([]) - expect(setQuery).not.toHaveBeenCalledWith(filter.jql) -}) -it.each(['success', 'failure'] as const)( - 'keeps a new visit to the owner pending after an old write settles with %s', - async (settlement) => { - read.mockResolvedValue(initial) - let settleOld = () => {} - let settleNew = () => {} - mutate - .mockImplementationOnce( - () => - new Promise((resolve, reject) => { - settleOld = () => - settlement === 'success' - ? resolve({ filters: [], activeFilterId: null }) - : reject(new Error('Old host disconnected')) - }) - ) - .mockImplementationOnce( - () => - new Promise((resolve) => { - settleNew = () => resolve(initial) - }) - ) - const setQuery = vi.fn() - const { result, rerender } = renderHook( - ({ host }) => useTaskPageJiraSavedFilters(model(host), setQuery), - { initialProps: { host: 'host-a' } } - ) - await waitFor(() => expect(result.current.jiraSavedFiltersReady).toBe(true)) - let oldWrite = Promise.resolve(false) - act(() => { - oldWrite = result.current.applyJiraSavedFilter(filter).catch(() => false) - }) - rerender({ host: 'host-b' }) - await waitFor(() => expect(result.current.jiraSavedFiltersLoading).toBe(false)) - rerender({ host: 'host-a' }) - await waitFor(() => expect(read).toHaveBeenCalledTimes(3)) - await waitFor(() => expect(result.current.jiraSavedFiltersLoading).toBe(false)) - let newWrite = Promise.resolve(false) - act(() => { - newWrite = result.current.applyJiraSavedFilter(filter) - }) - expect(result.current.jiraSavedFiltersPending).toBe(true) - setQuery.mockClear() - await act(async () => { - settleOld() - await oldWrite - }) - expect(result.current.jiraSavedFilters).toEqual([filter]) - expect(result.current.jiraSavedFiltersPending).toBe(true) - expect(result.current.jiraSavedFiltersError).toBeNull() - expect(setQuery).not.toHaveBeenCalled() - await act(async () => { - settleNew() - await newWrite - }) - expect(result.current.jiraSavedFiltersPending).toBe(false) - expect(setQuery).toHaveBeenCalledWith(filter.jql) - } -) -it('applies only the committed owner while another owner render is suspended', async () => { - read.mockResolvedValue(initial) - mutate.mockResolvedValue(initial) - const setQuery = vi.fn() - const suspended = new Promise(() => {}) - const { result, rerender } = renderHook( - ({ host, suspend }) => { - const filters = useTaskPageJiraSavedFilters(model(host), setQuery) - if (suspend) { - throw suspended - } - return filters - }, - { - initialProps: { host: 'host-a', suspend: false }, - wrapper: ({ children }) => {children} - } - ) - await waitFor(() => expect(result.current.jiraSavedFiltersReady).toBe(true)) - const committedAction = result.current.applyJiraSavedFilter - act(() => { - startTransition(() => rerender({ host: 'host-b', suspend: true })) - }) - await act(async () => { - expect(await committedAction(filter)).toBe(true) - }) - expect(mutate).toHaveBeenCalledWith( - { kind: 'environment', environmentId: 'host-a' }, - { kind: 'select', id: filter.id } - ) - expect(read).toHaveBeenCalledTimes(1) -}) diff --git a/src/renderer/src/components/task-page/linear/Content.tsx b/src/renderer/src/components/task-page/linear/Content.tsx index a50450d9d5c..f4a8b506b3c 100644 --- a/src/renderer/src/components/task-page/linear/Content.tsx +++ b/src/renderer/src/components/task-page/linear/Content.tsx @@ -4,11 +4,11 @@ import { LoaderCircle } from 'lucide-react' import { LinearIcon } from '@/components/task-page/task-page-localized-options' import { useTranslate } from '@/i18n/use-translate' import { Button } from '@/components/ui/button' -import { TaskPageLinearProjectOverview } from './ProjectOverview' -import { TaskPageLinearProjectList } from './ProjectList' -import { TaskPageLinearCustomViewList } from './CustomViewList' -import { TaskPageLinearCustomViewProjects } from './CustomViewProjects' -import { TaskPageLinearIssueList } from './IssueList' +import { TaskPageLinearProjectOverview } from './projects/ProjectOverview' +import { TaskPageLinearProjectList } from './projects/ProjectList' +import { TaskPageLinearCustomViewList } from './views/CustomViewList' +import { TaskPageLinearCustomViewProjects } from './views/CustomViewProjects' +import { TaskPageLinearIssueList } from './issues/IssueList' export function TaskPageLinearContent({ model }: { diff --git a/src/renderer/src/components/task-page/linear/IssueRows.tsx b/src/renderer/src/components/task-page/linear/IssueRows.tsx deleted file mode 100644 index ad47fc1d0c1..00000000000 --- a/src/renderer/src/components/task-page/linear/IssueRows.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' -import { TaskPageLinearIssueBoard } from './IssueBoard' -export function TaskPageLinearIssueRows({ - model -}: { - model: TaskPageComposerActionsModel -}): React.JSX.Element | null { - return -} diff --git a/src/renderer/src/components/task-page/linear/IssueTable.test.tsx b/src/renderer/src/components/task-page/linear/IssueTable.test.tsx deleted file mode 100644 index 806f7f723fa..00000000000 --- a/src/renderer/src/components/task-page/linear/IssueTable.test.tsx +++ /dev/null @@ -1,83 +0,0 @@ -// @vitest-environment happy-dom - -import { cleanup, render, screen } from '@testing-library/react' -import userEvent from '@testing-library/user-event' -import { afterEach, expect, it, vi } from 'vitest' -import type { LinearIssue } from '../../../../../shared/linear/issue-types' -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' -import { TooltipProvider } from '@/components/ui/tooltip' -import { TaskPageLinearIssueTable } from './IssueTable' -import type { LinearIssueListRow } from './task-page-linear-issue-model' - -vi.mock('./task-page-linear-issue-model', () => ({ LinearStateCell: () => null })) - -afterEach(cleanup) - -function issue(id: string): LinearIssue { - return { - id, - identifier: `COR-${id}`, - title: `Issue ${id}`, - url: `https://linear.app/core/issue/COR-${id}`, - state: { name: 'Todo', type: 'unstarted', color: '#000' }, - team: { id: 'core', name: 'Core', key: 'COR' }, - labels: [], - labelIds: [], - priority: 0, - updatedAt: '2026-10-01T00:00:00Z' - } -} - -const first = issue('1') -const second = issue('2') -const third = issue('3') -const rows: LinearIssueListRow[] = [ - { type: 'section', key: 'status:Todo', label: 'Todo', count: 2 }, - { type: 'issue', issue: first }, - { type: 'issue', issue: second }, - { type: 'section', key: 'status:Done', label: 'Done', count: 1 }, - { type: 'issue', issue: third } -] - -function table(linearIssueListRows = rows) { - const fixture = { - selectedLinearWorkspaceId: 'all', - linearTaskSourceContext: null, - selectedLinearIssueId: null, - openLinearDetailPage: vi.fn(), - linearIssueAttachmentIndex: new Map(), - effectiveLinearDisplayProperties: new Set(), - linearIssueGridStyle: {}, - linearIssueListRows, - handleOpenOrUseLinearItem: vi.fn() - } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: The fixture supplies every model field read by this table. - const model = fixture as unknown as TaskPageComposerActionsModel - return ( - - - - ) -} - -it('tracks the group key through refreshed rows and leaves ungrouped issues visible', async () => { - const user = userEvent.setup() - const view = render(table()) - await user.click(screen.getByRole('button', { name: 'Todo 2' })) - const refreshed: LinearIssueListRow[] = [ - ...rows.slice(3), - { type: 'section', key: 'status:Todo', label: 'Todo', count: 3 }, - ...rows.slice(1, 3), - { type: 'issue', issue: issue('4') } - ] - view.rerender(table(refreshed)) - expect(screen.getByRole('button', { name: 'Todo 3', expanded: false })).toBeTruthy() - expect(screen.queryByText('Issue 4')).toBeNull() - expect(screen.getByText('Issue 3')).toBeTruthy() - - view.rerender(table([{ type: 'issue', issue: first }])) - expect(screen.getByText('Issue 1')).toBeTruthy() - view.unmount() - render(table()) - expect(screen.getByRole('button', { name: 'Todo 2', expanded: true })).toBeTruthy() -}) diff --git a/src/renderer/src/components/task-page/linear/IssueAttributes.tsx b/src/renderer/src/components/task-page/linear/creation/IssueAttributes.tsx similarity index 99% rename from src/renderer/src/components/task-page/linear/IssueAttributes.tsx rename to src/renderer/src/components/task-page/linear/creation/IssueAttributes.tsx index bcceb1610e3..e359edb4f01 100644 --- a/src/renderer/src/components/task-page/linear/IssueAttributes.tsx +++ b/src/renderer/src/components/task-page/linear/creation/IssueAttributes.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover' import { useTranslate } from '@/i18n/use-translate' import { linearLabelCountLabel } from './linear-attribute-count-labels' diff --git a/src/renderer/src/components/task-page/linear/IssueDialog.tsx b/src/renderer/src/components/task-page/linear/creation/IssueDialog.tsx similarity index 98% rename from src/renderer/src/components/task-page/linear/IssueDialog.tsx rename to src/renderer/src/components/task-page/linear/creation/IssueDialog.tsx index 02da2829b72..638d9219427 100644 --- a/src/renderer/src/components/task-page/linear/IssueDialog.tsx +++ b/src/renderer/src/components/task-page/linear/creation/IssueDialog.tsx @@ -1,5 +1,5 @@ import { ImeInput } from '@/lib/text-input/ime/ime-text-field' -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { Dialog, DialogContent } from '@/components/ui/dialog' import { isScreenSubmitShortcut } from '@/lib/keyboard/screen-submit-shortcut' import { useTranslate } from '@/i18n/use-translate' diff --git a/src/renderer/src/components/task-page/linear/ProjectDialog.tsx b/src/renderer/src/components/task-page/linear/creation/ProjectDialog.tsx similarity index 98% rename from src/renderer/src/components/task-page/linear/ProjectDialog.tsx rename to src/renderer/src/components/task-page/linear/creation/ProjectDialog.tsx index bed683d4d8b..027429d9668 100644 --- a/src/renderer/src/components/task-page/linear/ProjectDialog.tsx +++ b/src/renderer/src/components/task-page/linear/creation/ProjectDialog.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { Dialog, DialogContent, diff --git a/src/renderer/src/components/task-page/linear/ProjectFields.tsx b/src/renderer/src/components/task-page/linear/creation/ProjectFields.tsx similarity index 99% rename from src/renderer/src/components/task-page/linear/ProjectFields.tsx rename to src/renderer/src/components/task-page/linear/creation/ProjectFields.tsx index 95098699104..1304a4ad433 100644 --- a/src/renderer/src/components/task-page/linear/ProjectFields.tsx +++ b/src/renderer/src/components/task-page/linear/creation/ProjectFields.tsx @@ -1,5 +1,5 @@ import { ImeInput } from '@/lib/text-input/ime/ime-text-field' -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { useTranslate } from '@/i18n/use-translate' import { linearLabelCountLabel, linearMemberCountLabel } from './linear-attribute-count-labels' import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover' diff --git a/src/renderer/src/components/task-page/linear/linear-attribute-count-labels.ts b/src/renderer/src/components/task-page/linear/creation/linear-attribute-count-labels.ts similarity index 100% rename from src/renderer/src/components/task-page/linear/linear-attribute-count-labels.ts rename to src/renderer/src/components/task-page/linear/creation/linear-attribute-count-labels.ts diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-creation-state.ts b/src/renderer/src/components/task-page/linear/creation/use-task-page-linear-creation-state.ts similarity index 98% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-creation-state.ts rename to src/renderer/src/components/task-page/linear/creation/use-task-page-linear-creation-state.ts index 8780dbe2cf9..24882cf576d 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-creation-state.ts +++ b/src/renderer/src/components/task-page/linear/creation/use-task-page-linear-creation-state.ts @@ -1,4 +1,4 @@ -import type { TaskPageJiraListProjectionModel } from '../jira/use-task-page-jira-list-projection' +import type { TaskPageJiraListProjectionModel } from '../../jira/listing/use-task-page-jira-list-projection' import { useState, useMemo, useEffect, useReducer, type SetStateAction } from 'react' import { useTeamMembers, useTeamLabels, useTeamStates } from '@/hooks/useIssueMetadata' import { @@ -10,9 +10,9 @@ import { completeLinearIssueForm, handoffLinearIssueForm as handoff } from '@/components/use-task-creation-draft-retention' -import { useNewLinearIssueProjects } from '../../use-new-linear-issue-projects' +import { useNewLinearIssueProjects } from '../../../use-new-linear-issue-projects' import { useContextualTour } from '@/components/contextual-tours/use-contextual-tour' -import { writeNewLinearIssueDraft, writeNewLinearProjectDraft } from '../task-page-draft-storage' +import { writeNewLinearIssueDraft, writeNewLinearProjectDraft } from '../../task-page-draft-storage' import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' type FormAction = (value: LinearIssueForm, target: string) => LinearIssueForm export function useLinearIssueCreationForm( diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-issue-creation.ts b/src/renderer/src/components/task-page/linear/creation/use-task-page-linear-issue-creation.ts similarity index 99% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-issue-creation.ts rename to src/renderer/src/components/task-page/linear/creation/use-task-page-linear-issue-creation.ts index b803039c4fd..296540264b8 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-issue-creation.ts +++ b/src/renderer/src/components/task-page/linear/creation/use-task-page-linear-issue-creation.ts @@ -6,7 +6,7 @@ import { useCallback, useLayoutEffect, useMemo, useRef } from 'react' import { toast } from 'sonner' import { translate } from '@/i18n/i18n' import { linearCreateIssue } from '@/runtime/linear/runtime-linear-issue-client' -import { useAppStore } from '@/store' +import { useAppStore } from '@/store/index' import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' import { openAppHttpLink } from '@/lib/browser/navigation/app-http-link-open' export function useTaskPageLinearIssueCreation(model: TaskPageLinearProjectCreationModel) { diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-project-creation.ts b/src/renderer/src/components/task-page/linear/creation/use-task-page-linear-project-creation.ts similarity index 97% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-project-creation.ts rename to src/renderer/src/components/task-page/linear/creation/use-task-page-linear-project-creation.ts index d3fe6b4509b..e988d5f0395 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-project-creation.ts +++ b/src/renderer/src/components/task-page/linear/creation/use-task-page-linear-project-creation.ts @@ -2,7 +2,7 @@ import { taskProviderErrorMessage, taskProviderErrorText } from '@/lib/work-items/task-provider-error-copy' -import type { TaskPageGitHubIssueCreationModel } from '../github/issue-creation/use-task-page-github-issue-creation' +import type { TaskPageGitHubIssueCreationModel } from '../../github/issue-creation/use-task-page-github-issue-creation' import { useCallback } from 'react' import { linearCreateProject } from '@/runtime/linear/runtime-linear-project-client' import { toast } from 'sonner' diff --git a/src/renderer/src/components/task-page/linear/issue-refresh/task-page-linear-refresh-lifecycle.test.tsx b/src/renderer/src/components/task-page/linear/issue-refresh/task-page-linear-refresh-lifecycle.test.tsx new file mode 100644 index 00000000000..a93870f5df4 --- /dev/null +++ b/src/renderer/src/components/task-page/linear/issue-refresh/task-page-linear-refresh-lifecycle.test.tsx @@ -0,0 +1,58 @@ +// @vitest-environment happy-dom +// @vitest-environment happy-dom +import { reads, task, store } from './task-page-linear-refresh-test-fixtures' +import '@/store/slices/linear/linear-issue-read' + +import { writeLinearIssue } from '@/lib/work-items/linear/linear-issue-write' + +import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' +import { getLinearSourceScope } from '@/store/slices/linear/linear-slice-scope' + +import { afterEach, beforeEach, expect, it, vi } from 'vitest' +import { act } from '@testing-library/react' +import { deferred } from '../../../../store/slices/linear/linear-slice-test-harness' + +const cacheTime = Date.parse('2026-01-01T00:00:00Z') +beforeEach(() => { + vi.useFakeTimers({ toFake: ['Date'] }) + vi.setSystemTime(cacheTime) +}) +afterEach(() => vi.useRealTimers()) + +it('captured default runtime cannot seed or patch the focused foreign runtime cache', async () => { + const s = store() + const pending = deferred<{ ok: boolean }>() + const saving = writeLinearIssue( + task(), + { state: task('Done').state }, + defaultScopeSource(null), + () => pending.promise + ) + // SAFETY: only activeRuntimeEnvironmentId is read by this captured-scope fixture. + s.setState({ + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. + settings: { activeRuntimeEnvironmentId: 'remote' } as never, + linearIssueCache: { 'w::same-uuid': { data: task('Remote'), fetchedAt: cacheTime } } + }) + const remote = s.getState().fetchLinearIssue(task().id, 'w').acquire() + expect(remote.currentView().data?.state.name).toBe('Remote') + reads.detail.mockResolvedValue(task('Local observation')) + const lease = s + .getState() + .fetchLinearIssue(task().id, 'w', { + scope: getLinearSourceScope({ kind: 'local' }), + force: true + }) + .acquire() + expect(lease.currentView().data).toBeNull() + expect((await lease.waitForCurrentTerminal()).data?.state.name).toBe('Done') + await act(async () => { + pending.resolve({ ok: false }) + await saving + }) + expect(s.getState().linearIssueCache['w::same-uuid'].data?.state.name).toBe('Remote') + expect(remote.currentView().kind).toBe('data') + expect(remote.currentView().data?.state.name).toBe('Remote') + remote.release() + lease.release() +}) diff --git a/src/renderer/src/components/task-page/linear/issue-refresh/task-page-linear-refresh-test-fixtures.tsx b/src/renderer/src/components/task-page/linear/issue-refresh/task-page-linear-refresh-test-fixtures.tsx new file mode 100644 index 00000000000..10f3d9ce9ea --- /dev/null +++ b/src/renderer/src/components/task-page/linear/issue-refresh/task-page-linear-refresh-test-fixtures.tsx @@ -0,0 +1,210 @@ +import type * as ReactModule from 'react' +import type { LinearEditState } from '../../../linear-item-drawer-types' + +import { getLinearSourceScope } from '@/store/slices/linear/linear-slice-scope' +import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' +import { LinearIssueTextEditor } from '../../../LinearIssueTextEditor' +import { useLinearIssueEditController } from '../../../linear-item-drawer-edit-controller' +import type { LinearIssue } from '../../../../../../shared/linear/issue-types' +import { useStore } from 'zustand' + +import { afterEach, beforeEach, vi, type Mock } from 'vitest' +import { cleanup } from '@testing-library/react' +import { createTestStore, issue } from '../../../../store/slices/linear/linear-slice-test-harness' +import { clearLinearRequestMaps } from '../../../../store/slices/linear/linear-slice-request-state' + +const deferredIssueSetters = vi.hoisted(() => { + const updates: VoidFunction[] = [] + return { enabled: false, updates } +}) +vi.mock('react', async (original) => { + const actual = await original() + return { + ...actual, + useReducer(reducer: (previous: S, action: A) => S, initial: S) { + const [value, dispatch] = actual.useReducer(reducer, initial) + const send = actual.useCallback((action: A) => { + if ( + deferredIssueSetters.enabled && + typeof initial === 'object' && + initial !== null && + 'whole' in initial + ) { + deferredIssueSetters.updates.push(() => dispatch(action)) + } else { + dispatch(action) + } + }, []) + return [value, send] as const + } + } +}) +export { deferredIssueSetters } + +const active = vi.hoisted(() => ({ store: vi.fn<() => Store>() })) + +vi.mock('@/store/index', () => ({ + useAppStore: Object.assign( + (selector: (state: ReturnType) => unknown) => + useStore(active.store(), selector), + { + getState: () => active.store().getState(), + subscribe: (...args: Parameters) => active.store().subscribe(...args) + } + ) +})) + +vi.mock('@/i18n/i18n', async (original) => ({ + ...(await original>()), + translate: (_key: string, fallback: string, values?: Record) => + fallback.replace(/{{(\w+)}}/g, (match, key) => values?.[key] ?? match) +})) + +const reads: { + list: Mock + search: Mock + project: Mock + view: Mock + detail: Mock + update: Mock + create: Mock + projects: Mock + connect: Mock + test: Mock + select: Mock +} = vi.hoisted(() => ({ + list: vi.fn(), + search: vi.fn(), + project: vi.fn(), + view: vi.fn(), + detail: vi.fn(), + update: vi.fn(), + create: vi.fn(), + projects: vi.fn(), + connect: vi.fn(), + test: vi.fn(), + select: vi.fn() +})) + +const accountViewer = vi.hoisted(() => ({ + displayName: 'User', + email: null, + organizationName: 'Acme' +})) + +vi.mock('@/runtime/linear/runtime-linear-client', async (original) => ({ + ...(await original>()), + linearTestConnection: (...args: unknown[]) => reads.test(...args), + linearSelectWorkspace: (...args: unknown[]) => reads.select(...args), + linearDisconnect: async () => {}, + linearConnect: (...args: unknown[]) => reads.connect(...args), + linearStatus: async () => ({ connected: true, viewer: accountViewer, selectedWorkspaceId: 'w' }), + linearListIssues: (...args: unknown[]) => reads.list(...args), + linearSearchIssues: (...args: unknown[]) => reads.search(...args) +})) + +vi.mock('@/runtime/linear/runtime-linear-project-client', async (original) => ({ + ...(await original>()), + linearListProjects: reads.projects, + linearTeamStates: async () => [{ id: 'done', name: 'Done', type: 'completed', color: '#888888' }], + linearListProjectIssues: (...args: unknown[]) => reads.project(...args), + linearListCustomViewIssues: (...args: unknown[]) => reads.view(...args) +})) + +vi.mock('@/runtime/linear/runtime-linear-issue-client', async (original) => ({ + ...(await original>()), + linearGetIssue: reads.detail, + linearIssueComments: async () => [], + linearUpdateIssue: reads.update, + linearCreateIssue: reads.create +})) + +vi.mock('@/hooks/useIssueMetadata', async (original) => ({ + ...(await original>()), + useTeamLabels: () => ({ data: [{ id: 'bug', name: 'Bug' }], loading: false }), + useTeamMembers: () => ({ data: [], loading: false }), + clearLinearMetadataCache: vi.fn(), + useTeamStates: () => ({ + data: [{ id: 'done', name: 'Done', type: 'completed', color: '#888888' }], + loading: false + }) +})) + +vi.mock('@/store/slices/worktrees/listing/use-worktree-catalog', async (original) => { + const empty: [] = [] + return { ...(await original>()), useAllWorktrees: () => empty } +}) + +vi.mock('@/store/workspaces/catalog/workspace-record-fields', async (original) => { + const empty: [] = [] + return { ...(await original>()), selectFolderWorkspaces: () => empty } +}) + +vi.mock('@/components/LinearIssueMarkdownDescriptionEditor', () => ({ + LinearIssueMarkdownDescriptionEditor: () => null +})) + +vi.mock('@/components/linear-item-drawer-sheet', () => ({ + renderLinearItemDrawerSheet: (props: { + displayed: LinearIssue | null + editState: LinearEditState | null + }) => + props.displayed && props.editState ? ( + + ) : ( +
+ ) +})) + +export function DrawerFields({ row, editState }: { row: LinearIssue; editState: LinearEditState }) { + const controller = useLinearIssueEditController({ issue: row, editState }) + return ( + <> + + {row.state.name}:{editState.priority}:{editState.labels.join(',')} + + + + + + ) +} + +export function task(state = 'In Review') { + return { + ...issue('same-uuid'), + identifier: 'DAT-124', + workspaceId: 'w', + state: { name: state, type: state === 'Done' ? 'completed' : 'started', color: '#888888' } + } +} + +export function store() { + const result = createTestStore() + result.setState({ + linearStatus: { connected: true, viewer: accountViewer, selectedWorkspaceId: 'w' }, + linearStatusObserved: true, + linearStatusChecked: true, + linearStatusContextKey: getLinearSourceScope(defaultScopeSource(null)).runtimeContextKey + }) + active.store.mockReturnValue(result) + return result +} + +export type Store = ReturnType + +beforeEach(() => { + deferredIssueSetters.enabled = false + deferredIssueSetters.updates.length = 0 + vi.resetAllMocks() + reads.connect.mockResolvedValue({ ok: true, viewer: accountViewer }) + reads.test.mockResolvedValue({ ok: true, viewer: accountViewer }) + clearLinearRequestMaps() +}) + +afterEach(() => { + cleanup() + clearLinearRequestMaps() +}) + +export { reads } diff --git a/src/renderer/src/components/task-page/linear/IssueBoard.tsx b/src/renderer/src/components/task-page/linear/issues/IssueBoard.tsx similarity index 96% rename from src/renderer/src/components/task-page/linear/IssueBoard.tsx rename to src/renderer/src/components/task-page/linear/issues/IssueBoard.tsx index fd58793e71e..00ec7c55584 100644 --- a/src/renderer/src/components/task-page/linear/IssueBoard.tsx +++ b/src/renderer/src/components/task-page/linear/issues/IssueBoard.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { useTranslate } from '@/i18n/use-translate' import { resolveLinearIssueEmptyKind, @@ -6,12 +6,12 @@ import { } from './task-page-linear-issue-empty-state' import { Button } from '@/components/ui/button' import { LinearIcon } from '@/components/task-page/task-page-localized-options' -import { isIntegrationCredentialDecryptionError } from '../../../../../shared/integration-credential-errors' +import { isIntegrationCredentialDecryptionError } from '../../../../../../shared/integration-credential-errors' import { clampLinearIssueListLimit, LINEAR_ISSUE_LIST_MAX -} from '../../../../../shared/linear/issue-read-limits' -import { LINEAR_ITEM_LIMIT } from '../task-page-source-context' +} from '../../../../../../shared/linear/issue-read-limits' +import { LINEAR_ITEM_LIMIT } from '../../task-page-source-context' import { TaskPageLinearIssueBoardColumns } from './IssueBoardColumns' import { TaskPageLinearIssueTable } from './IssueTable' export function TaskPageLinearIssueBoard({ diff --git a/src/renderer/src/components/task-page/linear/IssueBoardColumns.tsx b/src/renderer/src/components/task-page/linear/issues/IssueBoardColumns.tsx similarity index 98% rename from src/renderer/src/components/task-page/linear/IssueBoardColumns.tsx rename to src/renderer/src/components/task-page/linear/issues/IssueBoardColumns.tsx index bed498c9813..981dfd2759f 100644 --- a/src/renderer/src/components/task-page/linear/IssueBoardColumns.tsx +++ b/src/renderer/src/components/task-page/linear/issues/IssueBoardColumns.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { cn } from '@/lib/class-names' import { findLinearIssueWorkspaceAttachmentInIndex } from '@/lib/work-items/linear/linear-issue-workspace-attachment' import { getWorktreeAttachmentLabel } from '@/lib/workspace/display/worktree-attachment-label' @@ -7,8 +7,8 @@ import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip import { Button } from '@/components/ui/button' import { useTranslate } from '@/i18n/use-translate' import { FolderOpen, ArrowRight, ExternalLink } from 'lucide-react' -import { LinearStateCell } from './task-page-linear-issue-model' -import { formatRelativeTime } from '../task-page-source-context' +import { LinearStateCell } from './task-page-linear-state-cell' +import { formatRelativeTime } from '../../task-page-source-context' import { openAppHttpLink } from '@/lib/browser/navigation/app-http-link-open' export function TaskPageLinearIssueBoardColumns({ model diff --git a/src/renderer/src/components/task-page/linear/IssueList.tsx b/src/renderer/src/components/task-page/linear/issues/IssueList.tsx similarity index 95% rename from src/renderer/src/components/task-page/linear/IssueList.tsx rename to src/renderer/src/components/task-page/linear/issues/IssueList.tsx index 7a06ebc962c..0da6ca97b20 100644 --- a/src/renderer/src/components/task-page/linear/IssueList.tsx +++ b/src/renderer/src/components/task-page/linear/issues/IssueList.tsx @@ -1,9 +1,9 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { TaskPageLinearIssueToolbar } from './IssueToolbar' import { useTranslate } from '@/i18n/use-translate' -import { TaskPageLinearIssueRows } from './IssueRows' +import { TaskPageLinearIssueBoard } from './IssueBoard' import { LinearCollectionNotice } from '@/components/linear-project-view-surfaces' -import { PaginationBar } from '../PaginationBar' +import { PaginationBar } from '../../PaginationBar' export function TaskPageLinearIssueList({ model }: { @@ -62,7 +62,7 @@ export function TaskPageLinearIssueList({
) : null} - + {selectedLinearProject && linearProjectTab === 'issues' ? ( <> compareLinearIssues(a, b, orderBy)) + if (groupBy === 'none') { + return [ + { + key: 'all', + label: t('auto.components.TaskPage.dfc0c79bd8', 'Issues'), + issues: sorted + } + ] + } + const sections = new Map() + for (const issue of sorted) { + const group = getLinearIssueGroup(issue, groupBy, t) + const section = sections.get(group.key) + if (section) { + section.issues.push(issue) + } else { + sections.set(group.key, { + key: group.key, + label: group.label, + issues: [issue] + }) + } + } + return [...sections.values()] +} diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-issue-request.ts b/src/renderer/src/components/task-page/linear/issues/task-page-linear-issue-request.ts similarity index 92% rename from src/renderer/src/components/task-page/linear/task-page-linear-issue-request.ts rename to src/renderer/src/components/task-page/linear/issues/task-page-linear-issue-request.ts index e94284bb17b..beccb32df3d 100644 --- a/src/renderer/src/components/task-page/linear/task-page-linear-issue-request.ts +++ b/src/renderer/src/components/task-page/linear/issues/task-page-linear-issue-request.ts @@ -2,12 +2,12 @@ import { isEmptyLinearIssueAttributeFilter, linearIssueAttributeFilterSignature, type LinearIssueAttributeFilter -} from '../../../../../shared/linear/issue-attribute-filter' +} from '../../../../../../shared/linear/issue-attribute-filter' import { getTaskSourceCacheScope, type TaskSourceContext -} from '../../../../../shared/work-items/task-source-context' -import type { LinearIssueListReadArgs } from '../../../store/slices/linear/linear-slice-contract' +} from '../../../../../../shared/work-items/task-source-context' +import type { LinearIssueListReadArgs } from '../../../../store/slices/linear/linear-slice-contract' export function isLinearIssueSearchActive(immediateQuery: string, appliedQuery: string): boolean { return immediateQuery.trim().length > 0 || appliedQuery.trim().length > 0 diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-issue-model.tsx b/src/renderer/src/components/task-page/linear/issues/task-page-linear-state-cell.tsx similarity index 69% rename from src/renderer/src/components/task-page/linear/task-page-linear-issue-model.tsx rename to src/renderer/src/components/task-page/linear/issues/task-page-linear-state-cell.tsx index b309a04b8b6..a1a05c7e9a7 100644 --- a/src/renderer/src/components/task-page/linear/task-page-linear-issue-model.tsx +++ b/src/renderer/src/components/task-page/linear/issues/task-page-linear-state-cell.tsx @@ -1,84 +1,24 @@ -import { getLinearSourceScope } from '@/store/slices/linear/linear-slice-scope' -import { writeLinearIssue } from '@/lib/work-items/linear/linear-issue-write' -import { taskProviderErrorMessage } from '@/lib/work-items/task-provider-error-copy' -import type { LinearIssue } from '../../../../../shared/linear/issue-types' -import type { LinearCustomViewModel } from '../../../../../shared/linear/project-types' -import type { - LinearCollectionResult, - LinearWorkflowState -} from '../../../../../shared/linear/workspace-types' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' +import { getLinearSourceScope } from '../../../../store/slices/linear/linear-slice-scope' +import { writeLinearIssue } from '../../../../lib/work-items/linear/linear-issue-write' +import { taskProviderErrorMessage } from '../../../../lib/work-items/task-provider-error-copy' +import type { LinearIssue } from '../../../../../../shared/linear/issue-types' +import type { TaskSourceContext } from '../../../../../../shared/work-items/task-source-context' import React, { useState, useRef, useCallback } from 'react' -import { useAppStore } from '@/store/index' -import { useTeamStates } from '@/hooks/useIssueMetadata' -import { linearUpdateIssue } from '@/runtime/linear/runtime-linear-issue-client' +import { useAppStore } from '../../../../store/index' +import { useTeamStates } from '../../../../hooks/useIssueMetadata' +import { linearUpdateIssue } from '../../../../runtime/linear/runtime-linear-issue-client' import { toast } from 'sonner' -import { translate } from '@/i18n/i18n' -import { useTranslate } from '@/i18n/use-translate' -import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover' -import { cn } from '@/lib/class-names' +import { translate } from '../../../../i18n/i18n' +import { useTranslate } from '../../../../i18n/use-translate' +import { Popover, PopoverTrigger, PopoverContent } from '../../../ui/popover' +import { cn } from '../../../../lib/class-names' import { getLinearStatePillStyle, getLinearStateMarkerStyle -} from '@/components/linear-state-pill-style' +} from '../../../linear-state-pill-style' import { LoaderCircle, ChevronDown } from 'lucide-react' -import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' -export type LinearProjectTab = 'overview' | 'issues' -export type LinearGroupSection = { - key: string - label: string - issues: LinearIssue[] -} -export type LinearIssueListRow = - | { - type: 'section' - key: string - label: string - count: number - } - | { - type: 'issue' - issue: LinearIssue - } -export const LINEAR_CUSTOM_VIEW_MODELS = [ - 'issue', - 'project' -] satisfies readonly LinearCustomViewModel[] -export function mergeLinearCollectionResults( - results: LinearCollectionResult[] -): LinearCollectionResult { - const errors = results.flatMap((result) => result.errors ?? []) - return { - items: results.flatMap((result) => result.items), - ...(errors.length > 0 - ? { - errors - } - : {}), - ...(results.some((result) => result.hasMore) - ? { - hasMore: true - } - : {}) - } -} -export function getLinearStatusSectionState( - section: LinearGroupSection -): LinearIssue['state'] | null { - if (!section.key.startsWith('status:')) { - return null - } - return section.issues[0]?.state ?? null -} -export function findLinearWorkflowStateForStatus( - states: LinearWorkflowState[], - targetState: LinearIssue['state'] -): LinearWorkflowState | undefined { - return ( - states.find((state) => state.name === targetState.name && state.type === targetState.type) ?? - states.find((state) => state.name === targetState.name) - ) -} +import { defaultScopeSource } from '../../../../runtime/hosts/selection/default-creation-host' + export function LinearStateCell({ issue, className, diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-team-selection.ts b/src/renderer/src/components/task-page/linear/issues/task-page-linear-team-selection.ts similarity index 91% rename from src/renderer/src/components/task-page/linear/task-page-linear-team-selection.ts rename to src/renderer/src/components/task-page/linear/issues/task-page-linear-team-selection.ts index 27c4b96cc68..93bf864faa4 100644 --- a/src/renderer/src/components/task-page/linear/task-page-linear-team-selection.ts +++ b/src/renderer/src/components/task-page/linear/issues/task-page-linear-team-selection.ts @@ -1,4 +1,4 @@ -import type { LinearTeam } from '../../../../../shared/linear/workspace-types' +import type { LinearTeam } from '../../../../../../shared/linear/workspace-types' // Treat malformed persisted selections as sticky-all; unvalidated strings previously crashed array filtering. export function storedLinearTeamSelection(value: unknown): string[] | null { diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-board.ts b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-board.ts similarity index 88% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-board.ts rename to src/renderer/src/components/task-page/linear/issues/use-task-page-linear-board.ts index a5abaaf64be..9eeae5753ff 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-board.ts +++ b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-board.ts @@ -3,7 +3,7 @@ import { writeLinearIssue } from '@/lib/work-items/linear/linear-issue-write' import { taskProviderErrorMessage } from '@/lib/work-items/task-provider-error-copy' import type { TaskPageLinearListProjectionModel } from './use-task-page-linear-list-projection' import React, { useMemo, useCallback } from 'react' -import type { LinearIssue } from '../../../../../shared/linear/issue-types' +import type { LinearIssue } from '../../../../../../shared/linear/issue-types' import { writeLinearBoardIssueDragData, readLinearBoardIssueDragData @@ -12,15 +12,14 @@ import { linearTeamStates } from '@/runtime/linear/runtime-linear-project-client import { linearUpdateIssue } from '@/runtime/linear/runtime-linear-issue-client' import { toast } from 'sonner' import { translate } from '@/i18n/i18n' -import { useAppStore } from '@/store' -import type { LinearDisplayProperty } from '../../../../../shared/linear/issue-view-resume-state' -import { groupLinearIssues } from './task-page-linear-jira-list-model' -import type { LinearGroupSection } from './task-page-linear-issue-model' -import { - findLinearWorkflowStateForStatus, - getLinearStatusSectionState -} from './task-page-linear-issue-model' +import { useAppStore } from '@/store/index' +import type { LinearDisplayProperty } from '../../../../../../shared/linear/issue-view-resume-state' +import { groupLinearIssues } from './task-page-linear-issue-grouping' +import type { LinearGroupSection } from './task-page-linear-issue-grouping' + import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' +import type { LinearWorkflowState } from '../../../../../../shared/linear/workspace-types' + export function useTaskPageLinearBoard(model: TaskPageLinearListProjectionModel) { const { settings, @@ -209,3 +208,22 @@ export function useTaskPageLinearBoard(model: TaskPageLinearListProjectionModel) return nextModel } export type TaskPageLinearBoardModel = ReturnType + +export function getLinearStatusSectionState( + section: LinearGroupSection +): LinearIssue['state'] | null { + if (!section.key.startsWith('status:')) { + return null + } + return section.issues[0]?.state ?? null +} + +export function findLinearWorkflowStateForStatus( + states: LinearWorkflowState[], + targetState: LinearIssue['state'] +): LinearWorkflowState | undefined { + return ( + states.find((state) => state.name === targetState.name && state.type === targetState.type) ?? + states.find((state) => state.name === targetState.name) + ) +} diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-filter-selection.ts b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-filter-selection.ts similarity index 92% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-filter-selection.ts rename to src/renderer/src/components/task-page/linear/issues/use-task-page-linear-filter-selection.ts index c2a925b3d20..7f470d59440 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-filter-selection.ts +++ b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-filter-selection.ts @@ -4,15 +4,15 @@ import { resolveLinearIssueAttributeFilterPrimaryTeam } from '@/components/linea import { type LinearIssueAttributeFilter, linearIssueAttributeFilterSignature -} from '../../../../../shared/linear/issue-attribute-filter' -import { setLinearWorkspaceIssueFilter } from '../../../../../shared/linear/issue-view-resume-state' -import { LINEAR_ITEM_LIMIT } from '../task-page-source-context' +} from '../../../../../../shared/linear/issue-attribute-filter' +import { setLinearWorkspaceIssueFilter } from '../../../../../../shared/linear/issue-view-resume-state' +import { LINEAR_ITEM_LIMIT } from '../../task-page-source-context' import { type LinearPrimaryTeamObservation, shouldClearTeamDerivedFacets, teamDerivedFacetsForPrimaryTeamChange, isLinearIssueSearchActive -} from '@/components/task-page/linear/task-page-linear-issue-request' +} from '@/components/task-page/linear/issues/task-page-linear-issue-request' export function useTaskPageLinearFilterSelection(model: TaskPageLinearListSelectionPreludeModel) { const { linearMode, diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-in-orca-effects.ts b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-in-orca-effects.ts similarity index 96% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-in-orca-effects.ts rename to src/renderer/src/components/task-page/linear/issues/use-task-page-linear-in-orca-effects.ts index b2d6c6543cd..70cfdc4c0d2 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-in-orca-effects.ts +++ b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-in-orca-effects.ts @@ -4,8 +4,8 @@ import { useEffect } from 'react' import { readLinkedLinearIssuesWithLimit, filterLinearIssuesForInOrcaWorkspace -} from '@/components/task-page/linear/task-page-linear-in-orca-issues' -import type { LinearIssue } from '../../../../../shared/linear/issue-types' +} from '@/components/task-page/linear/issues/task-page-linear-in-orca-issues' +import type { LinearIssue } from '../../../../../../shared/linear/issue-types' import { translate } from '@/i18n/i18n' function isCurrentLinearRequest( diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-list-effects.ts b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-effects.ts similarity index 92% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-list-effects.ts rename to src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-effects.ts index f7365e19ce4..d83f0060065 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-list-effects.ts +++ b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-effects.ts @@ -2,29 +2,29 @@ import { observeLinearIssueRead, type LinearIssueRead } from '@/store/slices/linear/linear-issue-read-lifecycle' -import type { LinearIssue } from '../../../../../shared/linear/issue-types' -import type { LinearCollectionResult } from '../../../../../shared/linear/workspace-types' +import type { LinearIssue } from '../../../../../../shared/linear/issue-types' +import type { LinearCollectionResult } from '../../../../../../shared/linear/workspace-types' import { taskProviderErrorText } from '@/lib/work-items/task-provider-error-copy' -import type { TaskPageGlobalEffectsModel } from '../use-task-page-global-effects' +import type { TaskPageGlobalEffectsModel } from '../../use-task-page-global-effects' import { useEffect } from 'react' -import { isIntegrationCredentialDecryptionError } from '../../../../../shared/integration-credential-errors' -import { saveLinearIssueView } from '../../linear-issue-view-storage' -import { serializeLinearIssueViewResumeState } from '../../../../../shared/linear/issue-view-resume-state' -import { clampLinearIssueListLimit } from '../../../../../shared/linear/issue-read-limits' +import { isIntegrationCredentialDecryptionError } from '../../../../../../shared/integration-credential-errors' +import { saveLinearIssueView } from '../../../linear-issue-view-storage' +import { serializeLinearIssueViewResumeState } from '../../../../../../shared/linear/issue-view-resume-state' +import { clampLinearIssueListLimit } from '../../../../../../shared/linear/issue-read-limits' import { buildLinearIssueListReadArgs, type LinearIssueListFilterRead, shouldForceLinearIssueListRead, buildLinearIssueListRequestSignature -} from '@/components/task-page/linear/task-page-linear-issue-request' -import { linearIssueAttributeFilterSignature } from '../../../../../shared/linear/issue-attribute-filter' +} from '@/components/task-page/linear/issues/task-page-linear-issue-request' +import { linearIssueAttributeFilterSignature } from '../../../../../../shared/linear/issue-attribute-filter' import { linearIssueReadError } from '@/lib/work-items/linear/linear-read-error' import { taskPageLinearIssuesHaveMore, reconcileTaskPageLinearIssuesAfterLandingRefresh } from './task-page-linear-cache-selectors' -import { LINEAR_ITEM_LIMIT } from '../task-page-source-context' -import { useTaskSearchDebouncedApply } from '../use-task-search-debounced-apply' +import { LINEAR_ITEM_LIMIT } from '../../task-page-source-context' +import { useTaskSearchDebouncedApply } from '../../use-task-search-debounced-apply' export function useTaskPageLinearListEffects(model: TaskPageGlobalEffectsModel) { const { setTaskResumeState, diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-list-presentation.ts b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-presentation.ts similarity index 76% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-list-presentation.ts rename to src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-presentation.ts index 3dc1fa5aaef..2661f3f355c 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-list-presentation.ts +++ b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-presentation.ts @@ -1,8 +1,10 @@ import React, { useMemo } from 'react' -import { getLinearIssueGridTemplate, groupLinearIssues } from './task-page-linear-jira-list-model' -import type { LinearIssueListRow } from './task-page-linear-issue-model' +import { groupLinearIssues } from './task-page-linear-issue-grouping' + import type { TaskPageLinearListProjectionPreludeModel } from './use-task-page-linear-list-projection' +import type { LinearIssue } from '../../../../../../shared/linear/issue-types' +import type { LinearDisplayProperty } from '../../../../../../shared/linear/issue-view-resume-state' export function useTaskPageLinearListPresentation(model: TaskPageLinearListProjectionPreludeModel) { const { @@ -98,3 +100,39 @@ export function useTaskPageLinearListPresentation(model: TaskPageLinearListProje export type TaskPageLinearListPresentationModel = ReturnType< typeof useTaskPageLinearListPresentation > + +export type LinearIssueListRow = + | { + type: 'section' + key: string + label: string + count: number + } + | { + type: 'issue' + issue: LinearIssue + } + +export function getLinearIssueGridTemplate( + visibleProperties: ReadonlySet +): string { + const columns = ['96px', 'minmax(240px,1.55fr)'] + if (visibleProperties.has('labels')) { + columns.push('minmax(168px,0.9fr)') + } + if (visibleProperties.has('team')) { + columns.push('minmax(172px,0.9fr)') + } + if (visibleProperties.has('state')) { + columns.push('138px') + } + if (visibleProperties.has('assignee')) { + columns.push('64px') + } + if (visibleProperties.has('updated')) { + columns.push('104px') + } + // Why: Worktrees is icon-only (open vs start); keep it narrow so issue title keeps the room. + columns.push('64px') + return columns.join(' ') +} diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-list-projection.ts b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-projection.ts similarity index 97% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-list-projection.ts rename to src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-projection.ts index 26bafb6a469..20f35e45b3f 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-list-projection.ts +++ b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-projection.ts @@ -1,12 +1,12 @@ import type { TaskPageLinearListSelectionModel } from './use-task-page-linear-list-selection' import { useMemo, useCallback, useEffect } from 'react' -import { filterLinearIssuesBySearchQuery } from '@/components/task-page/linear/task-page-linear-in-orca-issues' +import { filterLinearIssuesBySearchQuery } from '@/components/task-page/linear/issues/task-page-linear-in-orca-issues' import { clampLinearIssueListLimit, LINEAR_ISSUE_LIST_MAX -} from '../../../../../shared/linear/issue-read-limits' -import { compareLinearIssues } from './task-page-linear-jira-list-model' -import { LINEAR_ITEM_LIMIT } from '../task-page-source-context' +} from '../../../../../../shared/linear/issue-read-limits' +import { compareLinearIssues } from './task-page-linear-issue-grouping' +import { LINEAR_ITEM_LIMIT } from '../../task-page-source-context' import { useTaskPageLinearListPresentation } from './use-task-page-linear-list-presentation' export function useTaskPageLinearListProjectionPrelude(model: TaskPageLinearListSelectionModel) { const { diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-list-selection.ts b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-selection.ts similarity index 96% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-list-selection.ts rename to src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-selection.ts index 13a01d43e64..6eeb5765abc 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-list-selection.ts +++ b/src/renderer/src/components/task-page/linear/issues/use-task-page-linear-list-selection.ts @@ -1,17 +1,17 @@ import { linearIssueReadError } from '@/lib/work-items/linear/linear-read-error' -import type { TaskPageProviderMetadataModel } from '../use-task-page-provider-metadata' +import type { TaskPageProviderMetadataModel } from '../../use-task-page-provider-metadata' import { useState, useMemo, useEffect } from 'react' -import { LINEAR_ISSUE_LIST_MAX } from '../../../../../shared/linear/issue-read-limits' +import { LINEAR_ISSUE_LIST_MAX } from '../../../../../../shared/linear/issue-read-limits' import { useTranslate } from '@/i18n/use-translate' -import type { LinearTeam } from '../../../../../shared/linear/workspace-types' +import type { LinearTeam } from '../../../../../../shared/linear/workspace-types' import { buildLinearTeamUrl, getLinearOrganizationUrlKeyFromIssueUrl -} from '../../../../../shared/linear/links' +} from '../../../../../../shared/linear/links' import { reconcileLinearTeamSelection, storedLinearTeamSelection -} from '@/components/task-page/linear/task-page-linear-team-selection' +} from '@/components/task-page/linear/issues/task-page-linear-team-selection' import { useTaskPageLinearFilterSelection } from './use-task-page-linear-filter-selection' export type TaskPageLinearListSelectionPreludeModel = ReturnType< typeof useTaskPageLinearListSelectionPrelude diff --git a/src/renderer/src/components/task-page/linear/ProjectList.tsx b/src/renderer/src/components/task-page/linear/projects/ProjectList.tsx similarity index 96% rename from src/renderer/src/components/task-page/linear/ProjectList.tsx rename to src/renderer/src/components/task-page/linear/projects/ProjectList.tsx index 4855a01b4b9..95f2bddbbab 100644 --- a/src/renderer/src/components/task-page/linear/ProjectList.tsx +++ b/src/renderer/src/components/task-page/linear/projects/ProjectList.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { useTranslate } from '@/i18n/use-translate' import { LinearProjectTable, diff --git a/src/renderer/src/components/task-page/linear/ProjectOverview.tsx b/src/renderer/src/components/task-page/linear/projects/ProjectOverview.tsx similarity index 96% rename from src/renderer/src/components/task-page/linear/ProjectOverview.tsx rename to src/renderer/src/components/task-page/linear/projects/ProjectOverview.tsx index 3d453b0b727..d23ea8d120b 100644 --- a/src/renderer/src/components/task-page/linear/ProjectOverview.tsx +++ b/src/renderer/src/components/task-page/linear/projects/ProjectOverview.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { LinearProjectOverview } from '@/components/linear-project-view-surfaces' import { openAppHttpLink } from '@/lib/browser/navigation/app-http-link-open' export function TaskPageLinearProjectOverview({ diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-collection-effects.ts b/src/renderer/src/components/task-page/linear/projects/use-task-page-linear-collection-effects.ts similarity index 93% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-collection-effects.ts rename to src/renderer/src/components/task-page/linear/projects/use-task-page-linear-collection-effects.ts index dcc217fa5f8..395c29f2f3e 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-collection-effects.ts +++ b/src/renderer/src/components/task-page/linear/projects/use-task-page-linear-collection-effects.ts @@ -2,12 +2,12 @@ import { observeLinearIssueRead } from '@/store/slices/linear/linear-issue-read- import { linearIssueReadError } from '@/lib/work-items/linear/linear-read-error' import { taskProviderErrorText } from '@/lib/work-items/task-provider-error-copy' import { translate } from '@/i18n/i18n' -import type { TaskPageLinearInOrcaEffectsModel } from './use-task-page-linear-in-orca-effects' +import type { TaskPageLinearInOrcaEffectsModel } from '../issues/use-task-page-linear-in-orca-effects' import { useEffect } from 'react' -import { LINEAR_ITEM_LIMIT } from '../task-page-source-context' -import { useTaskSearchDebouncedApply } from '../use-task-search-debounced-apply' -import { clampLinearIssueListLimit } from '../../../../../shared/linear/issue-read-limits' -import { useTaskPageLinearCustomViewEffects } from './use-task-page-linear-custom-view-effects' +import { LINEAR_ITEM_LIMIT } from '../../task-page-source-context' +import { useTaskSearchDebouncedApply } from '../../use-task-search-debounced-apply' +import { clampLinearIssueListLimit } from '../../../../../../shared/linear/issue-read-limits' +import { useTaskPageLinearCustomViewEffects } from '../views/use-task-page-linear-custom-view-effects' export function useTaskPageLinearCollectionEffectsPrelude(model: TaskPageLinearInOrcaEffectsModel) { const { diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-committed-publication.test.tsx b/src/renderer/src/components/task-page/linear/task-page-linear-committed-publication.test.tsx deleted file mode 100644 index 6ead6b39cee..00000000000 --- a/src/renderer/src/components/task-page/linear/task-page-linear-committed-publication.test.tsx +++ /dev/null @@ -1,361 +0,0 @@ -// @vitest-environment happy-dom -import { - reads, - task, - store, - source, - useRoutingFixture -} from './task-page-linear-refresh-test-fixtures' -import { reads as issueReadRegistry } from '@/store/slices/linear/linear-issue-read-lifecycle' -import '@/store/slices/linear/linear-issue-read' -import { pendingLinearIssueWrites } from '@/store/slices/linear/linear-issue-write-publication' -import { getLinearSourceScope } from '@/store/slices/linear/linear-slice-scope' -import { writeLinearIssue } from '@/lib/work-items/linear/linear-issue-write' -import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' -import { useLinearIssueWorkspaceDetail } from '../../linear-issue-workspace-detail-state' -import type { LinearIssue } from '../../../../../shared/linear/issue-types' -import { expect, it } from 'vitest' -import { act, render, screen } from '@testing-library/react' -import { deferred } from '../../../store/slices/linear/linear-slice-test-harness' - -import { Suspense, startTransition, useState, useLayoutEffect } from 'react' -import { useLinearIssuePublication } from '@/hooks/useLinearIssuePublication' -import type { useTaskPageDetailRouting } from '../use-task-page-detail-routing' - -it.each([true, false])( - 'publication speculative B (suspend=%s) preserves committed authority', - async (suspend) => { - store() - const A = source('local'), - B = { ...A, accountLabel: 'B' } - const blocked = deferred() - const pending = deferred<{ ok: boolean }>() - const seen: string[] = [], - committed: string[] = [] - let navigate!: (value: boolean) => void - function Consumer({ b }: { b: boolean }) { - const row = useLinearIssuePublication(task(), b ? B : A).issue! - seen.push(b ? 'B' : 'A') - useLayoutEffect(() => { - committed.push(b ? 'B' : 'A') - }) - if (b && suspend) { - throw blocked.promise - } - return ( - - {b ? 'B' : 'A'}:{row.priority} - - ) - } - function Harness() { - const [b, setB] = useState(false) - navigate = setB - return ( - Blocked}> - - - ) - } - const mounted = render() - await act(async () => { - startTransition(() => navigate(true)) - }) - expect(seen).toContain('B') - expect(committed.includes('B')).toBe(!suspend) - expect(screen.getByTestId('publication-scope').textContent).toBe(suspend ? 'A:0' : 'B:0') - let saving!: Promise<{ ok: boolean }> - act(() => { - saving = writeLinearIssue(task(), { priority: 4 }, A, () => pending.promise) - }) - if (suspend) { - await act(async () => { - navigate(false) - }) - } - const visible = screen.getByTestId('publication-scope').textContent - try { - expect(visible).toBe(suspend ? 'A:4' : 'B:0') - } finally { - mounted.unmount() - pending.resolve({ ok: false }) - await saving - expect(pendingLinearIssueWrites.size).toBe(0) - expect(issueReadRegistry.size).toBe(0) - } - } -) - -it.each([true, false])( - 'real routing updater discard (suspend=%s) cannot retire a pending field publication', - async (suspend) => { - const s = store() - s.setState({ jiraIssueCache: {}, jiraSearchCache: {} }) - const context = defaultScopeSource(null) - const pendingPriority = deferred<{ ok: boolean }>(), - pendingTitle = deferred<{ ok: boolean }>(), - pendingEarlierTitle = deferred<{ ok: boolean }>() - const hydrated = deferred(), - blocked = deferred() - reads.detail.mockReturnValue(hydrated.promise) - let route!: ReturnType - let pulse!: () => void - const renders: string[] = [], - commits: string[] = [] - let block = true - function Consumer() { - const [tick, setTick] = useState(0) - const current = useRoutingFixture(null) - useLayoutEffect(() => { - route = current - pulse = () => setTick((n) => n + 1) - commits.push( - `${current.selectedLinearIssue?.state.name}:${current.selectedLinearIssue?.priority}` - ) - }) - renders.push( - `${current.selectedLinearIssue?.state.name}:${current.selectedLinearIssue?.priority}` - ) - if (suspend && block && current.selectedLinearIssue?.state.name === 'Hydrated') { - throw blocked.promise - } - return ( - - {current.selectedLinearIssue?.title}:{current.selectedLinearIssue?.priority}:{tick} - - ) - } - const mounted = render( - Blocked}> - - - ) - act(() => route.setSelectedLinearIssue(task())) - let lease!: ReturnType['fetchLinearIssue']> extends { - acquire: () => infer L - } - ? L - : never - let prioritySaving!: Promise<{ ok: boolean }>, - titleSaving!: Promise<{ ok: boolean }>, - earlierTitleSaving!: Promise<{ ok: boolean }> - await act(async () => { - startTransition(() => { - earlierTitleSaving = writeLinearIssue( - task(), - { title: 'Earlier title' }, - context, - () => pendingEarlierTitle.promise - ) - prioritySaving = writeLinearIssue( - task(), - { priority: 4 }, - context, - () => pendingPriority.promise - ) - lease = s - .getState() - .fetchLinearIssue(task().id, 'w', { force: true, seed: task() }) - .acquire() - }) - hydrated.resolve(task('Hydrated')) - await hydrated.promise - await Promise.resolve() - await Promise.resolve() - const view = lease.currentView() - expect(view.data?.state.name).toBe('Hydrated') - expect(view.data?.priority).toBe(4) - startTransition(() => - route.setSelectedLinearIssue(view.data, { - expectedSelection: route.linearSelectionRequest, - isCurrent: () => lease.commit(view, false, () => true) - }) - ) - }) - expect(renders).toContain('Hydrated:4') - expect(commits.includes('Hydrated:4')).toBe(!suspend) - // A real later field write retires the delivered read but must not erase pending priority. - act(() => { - titleSaving = writeLinearIssue( - task(), - { title: 'Current title' }, - context, - () => pendingTitle.promise - ) - pulse() - }) - block = false - await act(async () => { - pulse() - }) - const visible = screen.getByTestId('hydration-scope').textContent - const { projectLinearIssue } = - await import('@/store/slices/linear/linear-issue-write-publication') - expect(projectLinearIssue(getLinearSourceScope(context), task()).priority).toBe(4) - try { - expect(visible).toContain('Current title:4:') - } finally { - mounted.unmount() - lease.release() - pendingTitle.resolve({ ok: false }) - pendingPriority.resolve({ ok: false }) - pendingEarlierTitle.resolve({ ok: false }) - await Promise.all([prioritySaving, titleSaving, earlierTitleSaving]) - expect(pendingLinearIssueWrites.size).toBe(0) - expect(issueReadRegistry.size).toBe(0) - } - } -) - -it('committed hydration survives actual skipped-lane replay, force loading and terminal failure', async () => { - const s = store() - const first = deferred(), - h = deferred() - let rejectReplacement!: (error: Error) => void - const replacement = new Promise((_resolve, reject) => { - rejectReplacement = reject - }) - reads.detail - .mockReturnValueOnce(first.promise) - .mockReturnValueOnce(h.promise) - .mockReturnValueOnce(replacement) - const pending = deferred<{ ok: boolean }>(), - blocked = deferred() - let hold = true - let enroll!: () => void - let saving!: Promise<{ ok: boolean }> - const committed: string[] = [], - rendered: string[] = [] - function Detail() { - const [low, setLow] = useState(false) - const detail = useLinearIssueWorkspaceDetail({ - issue: task(), - providerSettings: defaultScopeSource(null), - requestKey: 'H' - }) - const value = `${detail.displayed.priority}:${detail.editState.priority}:${detail.issueLoading}:${low}` - rendered.push(value) - useLayoutEffect(() => { - committed.push(value) - enroll = () => setLow(true) - }) - if (low && hold) { - throw blocked.promise - } - return {value} - } - const mounted = render( - Blocked}> - - - ) - await act(async () => { - startTransition(() => { - enroll() - saving = writeLinearIssue( - task(), - { priority: 4 }, - defaultScopeSource(null), - () => pending.promise - ) - }) - }) - expect(rendered.some((value) => value.startsWith('4:4:') && value.endsWith(':true'))).toBe(true) - expect(committed.some((value) => value.startsWith('4:4:'))).toBe(false) - await act(async () => { - pending.resolve({ ok: false }) - await saving - }) - expect(pendingLinearIssueWrites.size).toBe(0) - expect(reads.detail).toHaveBeenCalledTimes(2) - await act(async () => { - h.resolve({ ...task('H'), priority: 2 }) - await h.promise - }) - expect(screen.getByTestId('replay').textContent).toBe('2:2:false:false') - act(() => { - s.getState().fetchLinearIssue(task().id, 'w', { force: true }) - }) - expect(reads.detail).toHaveBeenCalledTimes(3) - expect(screen.getByTestId('replay').textContent).toBe('2:2:true:false') - hold = false - await act(async () => { - blocked.resolve() - await blocked.promise - }) - expect(screen.getByTestId('replay').textContent).toBe('2:2:true:true') - await act(async () => { - rejectReplacement(new Error('offline')) - await replacement.catch(() => {}) - }) - expect(screen.getByTestId('replay').textContent).toBe('2:2:false:true') - expect(reads.detail).toHaveBeenCalledTimes(3) - reads.detail.mockResolvedValueOnce({ ...task('G'), priority: 3 }) - await act(async () => { - s.getState().fetchLinearIssue(task().id, 'w', { force: true }) - }) - expect(screen.getByTestId('replay').textContent).toBe('3:3:false:true') - mounted.unmount() - first.resolve(task('retired')) - await first.promise - expect(pendingLinearIssueWrites.size).toBe(0) - expect(issueReadRegistry.size).toBe(0) - act(() => s.getState().patchLinearIssue(task().id, { priority: 4 })) - expect(reads.detail).toHaveBeenCalledTimes(4) -}) - -it('committed field t reconstructs while newer u is in a genuinely suspended lane', async () => { - store() - const t = deferred<{ ok: boolean }>(), - u = deferred<{ ok: boolean }>(), - blocked = deferred() - let savingT!: Promise<{ ok: boolean }>, savingU!: Promise<{ ok: boolean }> - let enqueue!: () => void, pulse!: () => void - let hold = true - const commits: string[] = [] - function Consumer() { - const [low, setLow] = useState(false), - [tick, setTick] = useState(0) - const publication = useLinearIssuePublication(task(), defaultScopeSource(null)) - const value = `${publication.issue?.priority}:${low}:${tick}` - useLayoutEffect(() => { - enqueue = () => setLow(true) - pulse = () => setTick((n) => n + 1) - commits.push(value) - }) - if (low && hold) { - throw blocked.promise - } - return {value} - } - const mounted = render( - Blocked}> - - - ) - act(() => { - savingT = writeLinearIssue(task(), { priority: 4 }, defaultScopeSource(null), () => t.promise) - }) - expect(screen.getByTestId('field-replay').textContent).toBe('4:false:0') - await act(async () => { - startTransition(() => { - enqueue() - savingU = writeLinearIssue(task(), { priority: 2 }, defaultScopeSource(null), () => u.promise) - }) - }) - expect(commits.some((value) => value.startsWith('2:'))).toBe(false) - act(() => pulse()) - expect(screen.getByTestId('field-replay').textContent).toBe('4:false:1') - hold = false - await act(async () => { - blocked.resolve() - await blocked.promise - }) - expect(screen.getByTestId('field-replay').textContent).toBe('2:true:1') - mounted.unmount() - t.resolve({ ok: false }) - u.resolve({ ok: false }) - await Promise.all([savingT, savingU]) - expect(pendingLinearIssueWrites.size).toBe(0) - expect(issueReadRegistry.size).toBe(0) -}) diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-detail-query.test.tsx b/src/renderer/src/components/task-page/linear/task-page-linear-detail-query.test.tsx deleted file mode 100644 index 4a8a64338f6..00000000000 --- a/src/renderer/src/components/task-page/linear/task-page-linear-detail-query.test.tsx +++ /dev/null @@ -1,118 +0,0 @@ -// @vitest-environment happy-dom -import { reads, task } from './task-page-linear-refresh-test-fixtures' -import { afterEach, beforeEach, expect, it, vi } from 'vitest' - -import { getIssue } from '../../../../../main/linear/issues/linear-issue-lookups' -import { createLinearClient, loadLinearSdk } from '../../../../../main/linear/requests/linear-sdk' -import { ISSUE_QUERY } from '../../../../../main/linear/issues/context/issue-context-graphql' - -const clients = vi.hoisted(() => ({ get: vi.fn() })) -vi.mock('../../../../../main/linear/accounts/linear-account-client', () => ({ - getClients: clients.get, - isAuthError: (error: unknown) => error instanceof loadLinearSdk().AuthenticationLinearError -})) -vi.mock('../../../../../main/linear/accounts/linear-token-store', () => ({ clearToken: vi.fn() })) - -const child = { - id: 'child', - identifier: 'DAT-125', - title: 'Child task', - url: 'https://linear.app/acme/issue/DAT-125' -} -const project = { - id: 'project', - name: 'Release', - url: 'https://linear.app/acme/project/release', - color: '#888888' -} -const rawIssue = { - ...task('Fresh'), - branchName: 'dat-124', - description: 'Full description', - priority: 2, - estimate: 3, - dueDate: '2026-11-10', - updatedAt: '2026-11-02T10:00:00-08:00', - assignee: { id: 'user', displayName: 'Ada', avatarUrl: 'https://example.com/avatar' }, - project, - labels: { nodes: [{ id: 'bug', name: 'Bug' }] }, - children: { nodes: [child] } -} -// The shared query returns the same first label page the drawer previously requested. -const { labelIds: _labelIds, ...responseIssue } = rawIssue -const expected = { - ...task('Fresh'), - branchName: rawIssue.branchName, - description: rawIssue.description, - priority: 2, - estimate: 3, - dueDate: rawIssue.dueDate, - updatedAt: '2026-11-02T18:00:00.000Z', - workspaceName: 'Acme', - assignee: rawIssue.assignee, - project, - labels: ['Bug'], - labelIds: ['bug'], - subIssues: [child] -} -let requests: { query: string; variables: Record }[] - -beforeEach(() => { - requests = [] - vi.spyOn(globalThis, 'fetch').mockImplementation(async (_input, init) => { - requests.push(JSON.parse(String(init?.body))) - return Response.json({ data: { issue: responseIssue } }) - }) - clients.get.mockImplementation((_workspaceId: string, signal: AbortSignal) => [ - { - workspace: { id: 'w', organizationName: 'Acme' }, - client: createLinearClient({ apiKey: 'test-only', signal }) - } - ]) - reads.detail.mockImplementation((_source, id: string, workspaceId: string) => - getIssue(id, workspaceId) - ) -}) -afterEach(() => vi.restoreAllMocks()) - -function expectQuery(index: number, id: string) { - expect(requests[index]).toEqual({ - query: ISSUE_QUERY, - variables: { id, includeDrawerFields: true } - }) - expect(requests[index].query).toContain('children(first: 25)') - expect(clients.get).toHaveBeenLastCalledWith('w', expect.any(AbortSignal)) -} - -it('keeps readable core detail when an optional relation returns partial GraphQL errors', async () => { - vi.mocked(globalThis.fetch).mockImplementation(async (_input, init) => { - requests.push(JSON.parse(String(init?.body))) - return Response.json({ - data: { - issue: { ...responseIssue, assignee: { ...responseIssue.assignee, avatarUrl: null } } - }, - errors: [{ message: 'Avatar resolver failed', path: ['issue', 'assignee', 'avatarUrl'] }] - }) - }) - await expect(getIssue(task().id, 'w')).resolves.toEqual({ ...expected, assignee: undefined }) - expect(requests).toHaveLength(1) - expectQuery(0, task().id) -}) - -it.each([ - { path: ['issue', 'title'], extensions: undefined, auth: false }, - { path: ['issue', 'assignee'], extensions: { type: 'authentication error' }, auth: true } -])( - 'does not accept partial data for a core or authentication failure: $path', - async ({ path, extensions, auth }) => { - vi.spyOn(console, 'warn').mockImplementation(() => {}) - vi.mocked(globalThis.fetch).mockResolvedValue( - Response.json({ - data: { issue: responseIssue }, - errors: [{ message: 'Read failed', path, extensions }] - }) - ) - const read = getIssue(task().id, 'w') - await (auth ? expect(read).rejects.toThrow('Linear') : expect(read).resolves.toBeNull()) - } -) diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-jira-list-model.tsx b/src/renderer/src/components/task-page/linear/task-page-linear-jira-list-model.tsx deleted file mode 100644 index 9101632e7c9..00000000000 --- a/src/renderer/src/components/task-page/linear/task-page-linear-jira-list-model.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import type { LinearIssue } from '../../../../../shared/linear/issue-types' -import type { GitHubWorkItem } from '../../../../../shared/github/work-items/work-item-types' -import { getLinearPriorityLabel } from '@/components/task-page/task-page-localized-options' -import type { - LinearOrderBy, - LinearGroupBy, - LinearDisplayProperty -} from '../../../../../shared/linear/issue-view-resume-state' -import { translate } from '@/i18n/i18n' -import { useTranslate, type Translate } from '@/i18n/use-translate' -import type { TaskPageJiraLoadError } from '@/components/task-page/jira/task-page-jira-load-state' -import React from 'react' -import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@/components/ui/collapsible' -import { AlertCircle, ChevronDown, ChevronRight } from 'lucide-react' -import { Button } from '@/components/ui/button' -import type { TaskPageGitHubMutationIntent } from '@/components/task-page/github/mutations/task-page-github-work-item-mutation-patches' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' -import type { LinearGroupSection } from './task-page-linear-issue-model' -import { compareNumericLocaleText } from '../../../../../shared/locale-text-collators' -export function getLinearPriorityRank(priority: number): number { - return priority === 0 ? 5 : priority -} -export function compareLinearIssues( - a: LinearIssue, - b: LinearIssue, - orderBy: LinearOrderBy -): number { - if (orderBy === 'updated') { - return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime() - } - if (orderBy === 'identifier') { - return compareNumericLocaleText(a.identifier, b.identifier) - } - const priorityDelta = getLinearPriorityRank(a.priority) - getLinearPriorityRank(b.priority) - if (priorityDelta !== 0) { - return priorityDelta - } - return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime() -} -export function getLinearIssueGroup( - issue: LinearIssue, - groupBy: LinearGroupBy, - t: Translate = translate -): { - key: string - label: string -} { - if (groupBy === 'status') { - return { - key: `status:${issue.state.name}`, - label: issue.state.name - } - } - if (groupBy === 'assignee') { - return { - key: `assignee:${issue.assignee?.id ?? 'unassigned'}`, - label: - issue.assignee?.displayName ?? t('auto.components.TaskPage.groupUnassigned', 'Unassigned') - } - } - if (groupBy === 'priority') { - return { - key: `priority:${issue.priority}`, - label: getLinearPriorityLabel(issue.priority) - } - } - if (groupBy === 'team') { - return { - key: `team:${issue.team.id}`, - label: issue.team.name - } - } - return { - key: 'all', - label: t('auto.components.TaskPage.dfc0c79bd8', 'Issues') - } -} -export function groupLinearIssues( - issues: LinearIssue[], - groupBy: LinearGroupBy, - orderBy: LinearOrderBy, - t: Translate = translate -): LinearGroupSection[] { - const sorted = [...issues].sort((a, b) => compareLinearIssues(a, b, orderBy)) - if (groupBy === 'none') { - return [ - { - key: 'all', - label: t('auto.components.TaskPage.dfc0c79bd8', 'Issues'), - issues: sorted - } - ] - } - const sections = new Map() - for (const issue of sorted) { - const group = getLinearIssueGroup(issue, groupBy, t) - const section = sections.get(group.key) - if (section) { - section.issues.push(issue) - } else { - sections.set(group.key, { - key: group.key, - label: group.label, - issues: [issue] - }) - } - } - return [...sections.values()] -} -export function TaskPageJiraErrorBanner({ - error, - open, - onOpenChange -}: { - error: TaskPageJiraLoadError - open: boolean - onOpenChange: (open: boolean) => void -}): React.JSX.Element { - const t = useTranslate() - return ( - -
- -
-
{error.title}
- {error.details ? ( - <> - - - - -
- {error.details} -
-
- - ) : null} -
-
-
- ) -} -export function getLinearIssueGridTemplate( - visibleProperties: ReadonlySet -): string { - const columns = ['96px', 'minmax(240px,1.55fr)'] - if (visibleProperties.has('labels')) { - columns.push('minmax(168px,0.9fr)') - } - if (visibleProperties.has('team')) { - columns.push('minmax(172px,0.9fr)') - } - if (visibleProperties.has('state')) { - columns.push('138px') - } - if (visibleProperties.has('assignee')) { - columns.push('64px') - } - if (visibleProperties.has('updated')) { - columns.push('104px') - } - // Why: Worktrees is icon-only (open vs start); keep it narrow so issue title keeps the room. - columns.push('64px') - return columns.join(' ') -} -export type TaskPageGitHubWorkItemMutationRunner = { - run: (input: { - item: GitHubWorkItem - intent: TaskPageGitHubMutationIntent - sourceContext?: TaskSourceContext | null - mutate: () => Promise<{ - ok?: boolean - error?: - | string - | { - message?: string - } - } | void> - successToast?: string - errorToast: string - }) => Promise<'confirmed' | 'rolled_back' | 'stale'> - isIntentPending: (input: { - item: GitHubWorkItem - intent: TaskPageGitHubMutationIntent - sourceContext?: TaskSourceContext | null - }) => boolean -} diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-refresh-entry.test.tsx b/src/renderer/src/components/task-page/linear/task-page-linear-refresh-entry.test.tsx deleted file mode 100644 index b4edb49b54f..00000000000 --- a/src/renderer/src/components/task-page/linear/task-page-linear-refresh-entry.test.tsx +++ /dev/null @@ -1,190 +0,0 @@ -// @vitest-environment happy-dom -import { reads, task, store, source } from './task-page-linear-refresh-test-fixtures' - -import { getLinearSourceScope } from '@/store/slices/linear/linear-slice-scope' -import { writeLinearIssue } from '@/lib/work-items/linear/linear-issue-write' -import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' - -import { useLinearIssueWorkspaceDetail } from '../../linear-issue-workspace-detail-state' -import type { LinearIssue } from '../../../../../shared/linear/issue-types' -import { expect, it } from 'vitest' -import { act, renderHook, waitFor } from '@testing-library/react' -import { deferred } from '../../../store/slices/linear/linear-slice-test-harness' - -it('workspace hydration follows a full-source change without a remount or a successor edge', async () => { - store() - const A = source('local'), - B = { ...A, accountLabel: 'B' } - const old = deferred(), - current = deferred() - reads.detail - .mockReturnValueOnce(old.promise) - .mockReturnValueOnce(current.promise) - .mockResolvedValue({ ...task('External'), priority: 2 }) - const detail = renderHook( - ({ context }) => - useLinearIssueWorkspaceDetail({ - issue: task(), - providerSettings: context, - requestKey: 'unchanged-host-workspace-U' - }), - { initialProps: { context: A } } - ) - detail.rerender({ context: B }) - expect(reads.detail).toHaveBeenCalledTimes(2) - const pending = deferred<{ ok: boolean }>() - let saving!: Promise<{ ok: boolean }> - act(() => { - saving = writeLinearIssue(task(), { priority: 4 }, B, () => pending.promise) - }) - await act(async () => { - old.resolve({ ...task('Old A'), priority: 0 }) - await old.promise - }) - expect(detail.result.current.displayed.priority).toBe(4) - expect(detail.result.current.displayed.state.name).not.toBe('Old A') - await act(async () => { - current.resolve({ ...task('Current B'), priority: 1 }) - await current.promise - }) - expect(detail.result.current.displayed.priority).toBe(4) - await act(async () => { - pending.resolve({ ok: false }) - await saving - }) - await waitFor(() => expect(detail.result.current.displayed.state.name).toBe('External')) - expect(detail.result.current.displayed.priority).toBe(2) - expect(detail.result.current.issueLoading).toBe(false) -}) - -it('actual runtime-status session loss and recovery reacquire hydration and finish loading', async () => { - const s = store() - const { createRuntimeStatusSlice } = - await import('@/store/slices/runtime-status/create-runtime-status-slice') - // SAFETY: this fixture installs the production runtime slice over the same Zustand store. - const runtime = createRuntimeStatusSlice( - s.setState, - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - s.getState as unknown as Parameters[1] - ) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - s.setState({ ...runtime, settings: { activeRuntimeEnvironmentId: 'remote' } as never }) - runtime.setRuntimeEnvironmentStatus( - 'remote', - { status: null, checkedAt: 1 }, - { suppressDisconnectToast: true } - ) - const old = deferred(), - fresh = deferred() - reads.detail.mockReturnValueOnce(old.promise).mockReturnValueOnce(fresh.promise) - const detail = renderHook(() => - useLinearIssueWorkspaceDetail({ - issue: task(), - providerSettings: source('runtime:remote'), - requestKey: 'same' - }) - ) - act(() => - detail.result.current.handleCommentAdded({ - id: 'local', - body: 'Draft comment', - createdAt: 'today' - }) - ) - await act(async () => { - // SAFETY: the real transition inspects runtime identity/readiness only; no snapshot or transport is used. - runtime.setRuntimeEnvironmentStatus( - 'remote', - { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - status: { - runtimeId: 'new', - graphStatus: 'ready', - rendererGraphEpoch: 0, - authoritativeWindowId: null, - liveTabCount: 0, - liveLeafCount: 0, - runtimeProtocolVersion: 3, - minCompatibleRuntimeClientVersion: 3 - } as never, - checkedAt: 2 - }, - { suppressDisconnectToast: true } - ) - }) - expect(reads.detail).toHaveBeenCalledTimes(2) - await act(async () => { - old.resolve(task('Old session')) - fresh.resolve(task('New session')) - await Promise.all([old.promise, fresh.promise]) - }) - expect(detail.result.current.displayed.state.name).toBe('New session') - expect(detail.result.current.issueLoading).toBe(false) - expect(detail.result.current.comments.map((comment) => comment.id)).toContain('local') - const pending = deferred<{ ok: boolean }>() - let saving!: Promise<{ ok: boolean }> - act(() => { - saving = writeLinearIssue( - task(), - { priority: 4 }, - source('runtime:remote'), - () => pending.promise - ) - }) - expect(reads.detail).toHaveBeenCalledTimes(2) - detail.unmount() - await act(async () => { - pending.resolve({ ok: false }) - await saving - }) -}) - -it.each(['probe', 'disconnect'] as const)( - 'foreign mounted hydration completes after local %s without another render edge', - async (edge) => { - const s = store() - const foreign = source('runtime:foreign') - const transport = deferred() - reads.detail.mockReturnValue(transport.promise) - const local = renderHook(() => - useLinearIssueWorkspaceDetail({ - issue: task(), - providerSettings: defaultScopeSource(null), - requestKey: 'local-U' - }) - ) - const foreignKey = `${getLinearSourceScope(foreign).cachePrefix}::w::other` - const foreignCache = { data: task('Foreign cache'), fetchedAt: 1 } - s.setState({ linearIssueCache: { [foreignKey]: foreignCache } }) - const detail = renderHook(() => - useLinearIssueWorkspaceDetail({ - issue: task(), - providerSettings: foreign, - requestKey: 'foreign-U' - }) - ) - expect(detail.result.current.issueLoading).toBe(true) - await act(async () => { - await (edge === 'probe' - ? s.getState().testLinearConnection('w') - : s.getState().disconnectLinear()) - }) - await act(async () => { - transport.resolve(task('Foreign provider')) - await transport.promise - }) - expect(detail.result.current.displayed.state.name).toBe('Foreign provider') - expect(detail.result.current.issueLoading).toBe(false) - expect(reads.detail).toHaveBeenCalledTimes(2) - expect(s.getState().linearIssueCache[foreignKey]).toBe(foreignCache) - if (edge === 'disconnect') { - expect(local.result.current.issueLoading).toBe(false) - expect(local.result.current.displayed.state.name).toBe('In Review') - } else { - expect(local.result.current.displayed.state.name).toBe('Foreign provider') - expect(local.result.current.issueLoading).toBe(false) - } - local.unmount() - detail.unmount() - } -) diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-refresh-lifecycle.test.tsx b/src/renderer/src/components/task-page/linear/task-page-linear-refresh-lifecycle.test.tsx deleted file mode 100644 index 56badab631e..00000000000 --- a/src/renderer/src/components/task-page/linear/task-page-linear-refresh-lifecycle.test.tsx +++ /dev/null @@ -1,197 +0,0 @@ -// @vitest-environment happy-dom -import { - reads, - task, - store, - read, - loadingHook, - source -} from './task-page-linear-refresh-test-fixtures' -import '@/store/slices/linear/linear-issue-read' -import { - pendingLinearIssueWrites, - registerLinearIssueConsumer -} from '@/store/slices/linear/linear-issue-write-publication' -import { writeLinearIssue } from '@/lib/work-items/linear/linear-issue-write' - -import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' -import { getLinearSourceScope } from '@/store/slices/linear/linear-slice-scope' - -import type { LinearIssue } from '../../../../../shared/linear/issue-types' -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { act, waitFor } from '@testing-library/react' -import { deferred } from '../../../store/slices/linear/linear-slice-test-harness' - -const cacheTime = Date.parse('2026-01-01T00:00:00Z') -beforeEach(() => { - vi.useFakeTimers({ toFake: ['Date'] }) - vi.setSystemTime(cacheTime) -}) -afterEach(() => vi.useRealTimers()) - -it('keeps only own-query warm omissions, accepts new membership, and releases protection at settlement', async () => { - const s = store() - const untouched = { ...task(), id: 'untouched', title: 'untouched' } - reads.list.mockResolvedValueOnce({ items: [task(), untouched] }) - await read(s, 'list') - const next = { ...task('Server'), id: 'new', title: 'new' } - reads.list.mockResolvedValueOnce({ items: [next] }) - const pending = deferred<{ ok: boolean }>() - const hook = loadingHook(s) - let saving!: Promise<{ ok: boolean }> - act(() => { - saving = writeLinearIssue( - task(), - { state: task('Done').state }, - defaultScopeSource(null), - () => pending.promise - ) - }) - // A force dispatched during the pending interval may add rows, but cannot remove this query's protected occurrence. - reads.list.mockResolvedValueOnce({ items: [next] }) - hook.rerender({ nonce: 1 }) - await waitFor(() => - expect(hook.result.current.rows.map((row) => row.id)).toEqual(['same-uuid', 'new']) - ) - expect(hook.result.current.rows[0].state.name).toBe('Done') - reads.list.mockResolvedValueOnce({ items: [] }) - await act(async () => { - pending.resolve({ ok: true }) - await saving - }) - await waitFor(() => expect(hook.result.current.rows).toEqual([])) -}) - -it('dispatches despite a broken local subscriber and clears thrown writers independently of mount', async () => { - const s = store() - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - const registration = registerLinearIssueConsumer( - getLinearSourceScope(defaultScopeSource(null)), - task().id, - () => { - throw new Error('subscriber bug') - } - ) - const mutate = vi.fn(async () => { - throw new Error('transport failure') - }) - await act(async () => { - await expect( - writeLinearIssue(task(), { priority: 4 }, defaultScopeSource(null), mutate) - ).rejects.toThrow('transport failure') - }) - expect(mutate).toHaveBeenCalledTimes(1) - registration.release() - reads.detail.mockResolvedValue({ ...task('External'), priority: 2 }) - expect((await s.getState().fetchLinearIssue(task().id, 'w', { force: true }))?.priority).toBe(2) - warn.mockRestore() -}) - -it('captured default runtime cannot seed or patch the focused foreign runtime cache', async () => { - const s = store() - const pending = deferred<{ ok: boolean }>() - const saving = writeLinearIssue( - task(), - { state: task('Done').state }, - defaultScopeSource(null), - () => pending.promise - ) - // SAFETY: only activeRuntimeEnvironmentId is read by this captured-scope fixture. - s.setState({ - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - settings: { activeRuntimeEnvironmentId: 'remote' } as never, - linearIssueCache: { 'w::same-uuid': { data: task('Remote'), fetchedAt: cacheTime } } - }) - const remote = s.getState().fetchLinearIssue(task().id, 'w').acquire() - expect(remote.currentView().data?.state.name).toBe('Remote') - reads.detail.mockResolvedValue(task('Local observation')) - const lease = s - .getState() - .fetchLinearIssue(task().id, 'w', { - scope: getLinearSourceScope({ kind: 'local' }), - force: true - }) - .acquire() - expect(lease.currentView().data).toBeNull() - expect((await lease.waitForCurrentTerminal()).data?.state.name).toBe('Done') - await act(async () => { - pending.resolve({ ok: false }) - await saving - }) - expect(s.getState().linearIssueCache['w::same-uuid'].data?.state.name).toBe('Remote') - expect(remote.currentView().kind).toBe('data') - expect(remote.currentView().data?.state.name).toBe('Remote') - remote.release() - lease.release() -}) - -it('full disconnect detaches default and every explicit source, preserving another runtime and replacement owner', async () => { - const s = store() - const contexts = [ - defaultScopeSource(null), - source('local'), - { ...source('local'), accountLabel: 'second' }, - source('runtime:other') - ] - const pending = contexts.map(() => deferred<{ ok: boolean }>()) - const saves = contexts.map((context, i) => - writeLinearIssue(task(), { priority: 4 }, context, () => pending[i].promise) - ) - await s.getState().disconnectLinear() - const keys = contexts.map((context) => getLinearSourceScope(context).contextKey) - expect(keys.map((key) => pendingLinearIssueWrites.has(key))).toEqual([false, false, false, true]) - expect((await s.getState().connectLinear('fixture-not-a-credential')).ok).toBe(true) - reads.detail.mockResolvedValue({ ...task(), priority: 2 }) - const lease = s - .getState() - .fetchLinearIssue(task().id, 'w', { scope: getLinearSourceScope(contexts[1]), force: true }) - .acquire() - expect((await lease.waitForCurrentTerminal()).data?.priority).toBe(2) - const next = deferred<{ ok: boolean }>() - const replacement = writeLinearIssue( - { ...task(), priority: 2 }, - { priority: 3 }, - contexts[1], - () => next.promise - ) - await act(async () => { - for (let i = 0; i < 3; i++) { - pending[i].resolve({ ok: i === 2 }) - } - await Promise.all(saves.slice(0, 3)) - }) - expect(pendingLinearIssueWrites.has(keys[1])).toBe(true) - expect(pendingLinearIssueWrites.has(keys[3])).toBe(true) - expect(lease.currentView().data?.priority).toBe(3) - await act(async () => { - next.resolve({ ok: false }) - pending[3].resolve({ ok: true }) - await Promise.all([replacement, saves[3]]) - }) - expect((await lease.waitForCurrentTerminal()).data?.priority).toBe(2) - lease.release() - expect(pendingLinearIssueWrites.size).toBe(0) -}) - -it('a shared read gives explicit-runtime consumers authority after default focus moves', async () => { - const s = store(), - pending = deferred() - reads.detail.mockReturnValue(pending.promise) - const focused = s.getState().fetchLinearIssue(task().id, 'w').acquire() - const pinned = s - .getState() - .fetchLinearIssue(task().id, 'w', { scope: getLinearSourceScope({ kind: 'local' }) }) - .acquire() - expect(reads.detail).toHaveBeenCalledTimes(1) - // SAFETY: the fixture only reads the focused runtime selector from settings. - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - s.setState({ settings: { activeRuntimeEnvironmentId: 'elsewhere' } as never }) - await act(async () => { - pending.resolve(task('Local')) - await pending.promise - }) - expect(focused.currentView().kind).toBe('abandoned') - expect(pinned.currentView().data?.state.name).toBe('Local') - focused.release() - pinned.release() -}) diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-refresh-test-fixtures.tsx b/src/renderer/src/components/task-page/linear/task-page-linear-refresh-test-fixtures.tsx deleted file mode 100644 index b69852a36fc..00000000000 --- a/src/renderer/src/components/task-page/linear/task-page-linear-refresh-test-fixtures.tsx +++ /dev/null @@ -1,446 +0,0 @@ -import { useTaskPageDetailRouting } from '../use-task-page-detail-routing' -import type * as ReactModule from 'react' -import type { LinearEditState } from '../../linear-item-drawer-types' -import { useCallback } from 'react' -import { useLinearIssueResult } from '@/hooks/linear-issue-committed-state' -import { getLinearSourceScope } from '@/store/slices/linear/linear-slice-scope' -import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' -import { LinearIssueTextEditor } from '../../LinearIssueTextEditor' -import { useLinearIssueEditController } from '../../linear-item-drawer-edit-controller' -import type { LinearIssue } from '../../../../../shared/linear/issue-types' -import { useStore } from 'zustand' -import type { TaskSourceContext } from '../../../../../shared/work-items/task-source-context' -import { useTaskPageLinearCollectionEffectsPrelude } from './use-task-page-linear-collection-effects' -import { useTaskPageLinearCustomViewEffects } from './use-task-page-linear-custom-view-effects' -import { afterEach, beforeEach, vi, type Mock } from 'vitest' -import { cleanup, renderHook } from '@testing-library/react' -import { createTestStore, issue } from '../../../store/slices/linear/linear-slice-test-harness' -import { clearLinearRequestMaps } from '../../../store/slices/linear/linear-slice-request-state' -import { useTaskPageLinearListSelectionPrelude } from './use-task-page-linear-list-selection' -import { useTaskPageLinearListEffects } from './use-task-page-linear-list-effects' - -const deferredIssueSetters = vi.hoisted(() => { - const updates: VoidFunction[] = [] - return { enabled: false, updates } -}) -vi.mock('react', async (original) => { - const actual = await original() - return { - ...actual, - useReducer(reducer: (previous: S, action: A) => S, initial: S) { - const [value, dispatch] = actual.useReducer(reducer, initial) - const send = actual.useCallback((action: A) => { - if ( - deferredIssueSetters.enabled && - typeof initial === 'object' && - initial !== null && - 'whole' in initial - ) { - deferredIssueSetters.updates.push(() => dispatch(action)) - } else { - dispatch(action) - } - }, []) - return [value, send] as const - } - } -}) -export { deferredIssueSetters } - -const active = vi.hoisted(() => ({ store: vi.fn<() => Store>() })) - -vi.mock('@/store', () => ({ - useAppStore: Object.assign( - (selector: (state: ReturnType) => unknown) => - useStore(active.store(), selector), - { - getState: () => active.store().getState(), - subscribe: (...args: Parameters) => active.store().subscribe(...args) - } - ) -})) - -vi.mock('@/i18n/i18n', async (original) => ({ - ...(await original>()), - translate: (_key: string, fallback: string, values?: Record) => - fallback.replace(/{{(\w+)}}/g, (match, key) => values?.[key] ?? match) -})) - -const reads: { - list: Mock - search: Mock - project: Mock - view: Mock - detail: Mock - update: Mock - create: Mock - projects: Mock - connect: Mock - test: Mock - select: Mock -} = vi.hoisted(() => ({ - list: vi.fn(), - search: vi.fn(), - project: vi.fn(), - view: vi.fn(), - detail: vi.fn(), - update: vi.fn(), - create: vi.fn(), - projects: vi.fn(), - connect: vi.fn(), - test: vi.fn(), - select: vi.fn() -})) - -const accountViewer = vi.hoisted(() => ({ - displayName: 'User', - email: null, - organizationName: 'Acme' -})) - -vi.mock('@/runtime/linear/runtime-linear-client', async (original) => ({ - ...(await original>()), - linearTestConnection: (...args: unknown[]) => reads.test(...args), - linearSelectWorkspace: (...args: unknown[]) => reads.select(...args), - linearDisconnect: async () => {}, - linearConnect: (...args: unknown[]) => reads.connect(...args), - linearStatus: async () => ({ connected: true, viewer: accountViewer, selectedWorkspaceId: 'w' }), - linearListIssues: (...args: unknown[]) => reads.list(...args), - linearSearchIssues: (...args: unknown[]) => reads.search(...args) -})) - -vi.mock('@/runtime/linear/runtime-linear-project-client', async (original) => ({ - ...(await original>()), - linearListProjects: reads.projects, - linearTeamStates: async () => [{ id: 'done', name: 'Done', type: 'completed', color: '#888888' }], - linearListProjectIssues: (...args: unknown[]) => reads.project(...args), - linearListCustomViewIssues: (...args: unknown[]) => reads.view(...args) -})) - -vi.mock('@/runtime/linear/runtime-linear-issue-client', async (original) => ({ - ...(await original>()), - linearGetIssue: reads.detail, - linearIssueComments: async () => [], - linearUpdateIssue: reads.update, - linearCreateIssue: reads.create -})) - -vi.mock('@/hooks/useIssueMetadata', async (original) => ({ - ...(await original>()), - useTeamLabels: () => ({ data: [{ id: 'bug', name: 'Bug' }], loading: false }), - useTeamMembers: () => ({ data: [], loading: false }), - clearLinearMetadataCache: vi.fn(), - useTeamStates: () => ({ - data: [{ id: 'done', name: 'Done', type: 'completed', color: '#888888' }], - loading: false - }) -})) - -vi.mock('@/store/slices/worktrees/listing/use-worktree-catalog', async (original) => { - const empty: [] = [] - return { ...(await original>()), useAllWorktrees: () => empty } -}) - -vi.mock('@/store/workspaces/catalog/workspace-record-fields', async (original) => { - const empty: [] = [] - return { ...(await original>()), selectFolderWorkspaces: () => empty } -}) - -vi.mock('@/components/LinearIssueMarkdownDescriptionEditor', () => ({ - LinearIssueMarkdownDescriptionEditor: () => null -})) - -vi.mock('@/components/linear-item-drawer-sheet', () => ({ - renderLinearItemDrawerSheet: (props: { - displayed: LinearIssue | null - editState: LinearEditState | null - }) => - props.displayed && props.editState ? ( - - ) : ( -
- ) -})) - -export function DrawerFields({ row, editState }: { row: LinearIssue; editState: LinearEditState }) { - const controller = useLinearIssueEditController({ issue: row, editState }) - return ( - <> - - {row.state.name}:{editState.priority}:{editState.labels.join(',')} - - - - - - ) -} - -export function task(state = 'In Review') { - return { - ...issue('same-uuid'), - identifier: 'DAT-124', - workspaceId: 'w', - state: { name: state, type: state === 'Done' ? 'completed' : 'started', color: '#888888' } - } -} - -export function store() { - const result = createTestStore() - result.setState({ - linearStatus: { connected: true, viewer: accountViewer, selectedWorkspaceId: 'w' }, - linearStatusObserved: true, - linearStatusChecked: true, - linearStatusContextKey: getLinearSourceScope(defaultScopeSource(null)).runtimeContextKey - }) - active.store.mockReturnValue(result) - return result -} - -export type Store = ReturnType - -export type View = 'list' | 'search' | 'project' | 'custom-view' - -export const views: View[] = ['list', 'search', 'project', 'custom-view'] - -export const listArgs = { kind: 'list', filter: 'all', limit: 36 } as const - -export function read(s: Store, view: View, force = true) { - const state = s.getState() - if (view === 'search') { - return state.searchLinearIssues('DAT-124', 36, { force }) - } - if (view === 'project') { - return state.listLinearProjectIssues('p', 'w', 36, { force }) - } - if (view === 'custom-view') { - return state.listLinearCustomViewIssues('v', 'w', 36, { force }) - } - return state.listLinearIssues(listArgs, { force }) -} - -export function wire(view: View): Mock { - return view === 'custom-view' ? reads.view : reads[view] -} - -export function response(view: View, state: string) { - return view === 'search' ? [task(state)] : { items: [task(state)] } -} - -export function fixture(s: Store, rows: LinearIssue[], view: View = 'list') { - return { - settings: null, - linearStatus: s.getState().linearStatus, - selectedLinearWorkspaceId: 'w', - linearTaskSourceContext: null, - linearMode: 'issues', - linearAttributeFilter: { stateIds: [], priorities: [], assignee: null, labelIds: [] }, - linearAttributeFilterWorkspaceId: 'w', - linearIssues: rows, - linearIssueLimit: 36, - linearIssuePage: 0, - linearIssueLoadingTargetPage: null, - linearIssuesHasMore: false, - linearLoading: false, - linearError: null, - appliedLinearSearch: view === 'search' ? 'DAT-124' : '', - selectedLinearProject: - view === 'project' ? { id: 'p', workspaceId: 'w', name: 'Project' } : null, - linearProjectTab: view === 'project' ? 'issues' : 'overview', - linearProjectIssuesResult: { items: rows }, - linearProjectIssueLimit: 36, - linearProjectIssuePage: 0, - linearProjectIssueLoadingTargetPage: null, - linearProjectIssuesLoading: false, - linearProjectIssuesError: null, - selectedLinearCustomView: - view === 'custom-view' ? { id: 'v', workspaceId: 'w', model: 'issue', name: 'View' } : null, - linearCustomViewIssuesResult: { items: rows }, - linearCustomViewIssueLimit: 36, - linearCustomViewIssuePage: 0, - linearCustomViewIssueLoadingTargetPage: null, - linearCustomViewContentsLoading: false, - linearCustomViewContentsError: null, - availableTeams: [] - } -} - -export function display( - s: Store, - rows: LinearIssue[], - view: View = 'list', - context: TaskSourceContext | null = null -) { - const base = { ...fixture(s, rows, view), ...s.getState() } - const noop = vi.fn() - return renderHook(() => { - const scope = getLinearSourceScope(context ?? defaultScopeSource(null)) - const projectResult = useLinearIssueResult(`${scope.contextKey}project`, { items: rows }, scope) - const viewResult = useLinearIssueResult(`${scope.contextKey}view`, { items: rows }, scope) - const result = view === 'custom-view' ? viewResult : projectResult - const current = view === 'project' || view === 'custom-view' ? result.data.items : rows - const model = { - ...base, - linearTaskSourceContext: context, - linearIssues: current, - linearProjectIssueResult: projectResult, - linearCustomViewIssueResult: viewResult, - linearProjectIssuesResult: projectResult.data, - linearCustomViewIssuesResult: viewResult.data, - taskResumeApplied: true, - linearProjectSearchInput: '', - setAppliedLinearProjectSearch: noop, - filteredLinearIssues: rows, - clearSelectedLinearIssue: noop, - linearConnected: true, - taskSource: 'linear', - linearRefreshNonce: 0, - fetchLinearProject: async () => ({ id: 'p' }), - setSelectedLinearProjectDetail: noop, - setLinearProjectDetailLoading: noop, - setLinearProjectDetailError: noop, - setLinearProjectIssuesLoading: projectResult.setLoading, - setLinearProjectIssuesError: projectResult.setError, - setLinearCustomViewContentsLoading: viewResult.setLoading, - setLinearCustomViewContentsError: viewResult.setError, - setLinearCustomViewProjectsResult: noop, - setLinearProjectIssuesResult: projectResult.setData, - setLinearCustomViewIssuesResult: viewResult.setData - } - // SAFETY: fixture supplies the selected collection loader's fields; unrelated persistence is disabled. - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - useTaskPageLinearCollectionEffectsPrelude({ - ...model, - selectedLinearProject: view === 'project' ? base.selectedLinearProject : null - } as never) - // SAFETY: fixture supplies the custom-view loader fields and no view inventory is requested. - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - useTaskPageLinearCustomViewEffects({ ...model, linearMode: 'issues' } as never) - // List/search cache notifications are exercised with the real loader in loadingHook. - // SAFETY: fixture supplies the selection prelude fields. - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - const selection = useTaskPageLinearListSelectionPrelude(model as never) - return { ...selection, loading: result.loading, error: result.error } - }) -} - -beforeEach(() => { - deferredIssueSetters.enabled = false - deferredIssueSetters.updates.length = 0 - vi.resetAllMocks() - reads.connect.mockResolvedValue({ ok: true, viewer: accountViewer }) - reads.test.mockResolvedValue({ ok: true, viewer: accountViewer }) - clearLinearRequestMaps() -}) - -afterEach(() => { - cleanup() - clearLinearRequestMaps() -}) - -export function loadingHook(s: Store, query = '', extra = {}, queued?: VoidFunction[]) { - const refs = { - linearSearchPersistReadyRef: { current: false }, - linearViewPersistReadyRef: { current: false }, - linearAttributeFilterReadRef: { current: null }, - lastLinearRequestRef: { current: null }, - landingLinearRefreshKeysRef: { current: new Set() } - } - const noop = vi.fn() - let renderCount = 0 - const base = { - ...fixture(s, [task()]), - ...refs, - ...s.getState(), - taskSource: 'linear', - linearConnected: true, - taskResumeApplied: true, - linearSearchInput: query, - appliedLinearSearch: query, - setTaskResumeState: noop, - setAppliedLinearSearch: noop, - setLinearIssueLimit: noop, - setLinearIssuePage: noop, - setLinearIssueLoadingTargetPage: noop, - setLinearIssuesHasMore: noop, - setLinearLoading: noop, - linearIssueFiltersByWorkspaceId: {}, - ...extra - } - return renderHook( - ({ nonce }) => { - renderCount++ - const scope = getLinearSourceScope(defaultScopeSource(null)) - const result = useLinearIssueResult(scope.contextKey + query, [task()], scope) - const { data: rows, error, loading, read: consumeLinearRead } = result - const deliver = useCallback( - (lease, view, value) => { - if (queued && 'data' in value) { - queued.push(() => consumeLinearRead(lease, view, value)) - // Preserve the original delayed-row seam: outcome consumption is immediate. - const { data: _rows, ...outcome } = value - consumeLinearRead(lease, view, outcome) - } else { - consumeLinearRead(lease, view, value) - } - }, - [consumeLinearRead] - ) - // SAFETY: fixture supplies the loader's dependencies; persistence setters are inert in this hook test. - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - useTaskPageLinearListEffects({ - ...base, - linearRefreshNonce: nonce, - linearIssueResult: { ...result, read: deliver }, - setLinearIssues: result.setData, - setLinearError: result.setError, - setLinearLoading: result.setLoading, - setLinearIssuesHasMore: result.setHasMore - } as never) - // SAFETY: the same fixture supplies the rendered selection fields. - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - const selection = useTaskPageLinearListSelectionPrelude({ - ...base, - linearIssues: rows, - linearError: error - } as never) - return { - rows, - error, - loading, - displayed: selection.displayedLinearIssues, - - renders: renderCount - } - }, - { initialProps: { nonce: 0 } } - ) -} - -export function source(hostId: TaskSourceContext['hostId']): TaskSourceContext { - return { - kind: 'task-source', - provider: 'linear', - hostId, - projectId: 'project', - repoId: null, - providerIdentity: null, - accountLabel: null - } -} - -export { reads } - -export function useRoutingFixture( - context: ReturnType | null, - initial: LinearIssue | null = null -) { - const noop = vi.fn() - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - return useTaskPageDetailRouting({ - pageData: { openLinearIssue: initial ?? undefined }, - linearTaskSourceContext: context, - setDialogWorkItem: noop, - openTaskPage: noop - } as never) -} diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-refresh-writers.test.tsx b/src/renderer/src/components/task-page/linear/task-page-linear-refresh-writers.test.tsx deleted file mode 100644 index c9a395096b4..00000000000 --- a/src/renderer/src/components/task-page/linear/task-page-linear-refresh-writers.test.tsx +++ /dev/null @@ -1,140 +0,0 @@ -// @vitest-environment happy-dom -import { reads, task, store } from './task-page-linear-refresh-test-fixtures' -import { LinearIssueProjectSelector } from '../../linear-issue-project-selector' - -import '@/store/slices/linear/linear-issue-read' -import { pendingLinearIssueWrites } from '@/store/slices/linear/linear-issue-write-publication' -import { writeLinearIssue } from '@/lib/work-items/linear/linear-issue-write' - -import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' - -import LinearItemDrawer from '../../LinearItemDrawer' - -import type { LinearIssue } from '../../../../../shared/linear/issue-types' - -import { useLinearIssueWorkspaceDetail } from '../../linear-issue-workspace-detail-state' -import { expect, it, vi } from 'vitest' -import { act, render, fireEvent, screen } from '@testing-library/react' -import { deferred } from '../../../store/slices/linear/linear-slice-test-harness' - -it('real drawer hydration, field controller and text writer compose rollback without permanent masks', async () => { - const s = store() - s.setState({ - recordFeatureInteraction: vi.fn(), - linearStatus: { connected: true, viewer: null, selectedWorkspaceId: 'B' } - }) - const old = deferred(), - fresh = deferred() - reads.detail.mockReturnValueOnce(old.promise).mockReturnValueOnce(fresh.promise) - const priority = deferred<{ ok: boolean }>(), - labels = deferred<{ ok: boolean }>(), - title = deferred<{ ok: boolean }>() - reads.update.mockImplementation((_source, _id, patch) => - 'priority' in patch ? priority.promise : 'labelIds' in patch ? labels.promise : title.promise - ) - render() - expect(reads.detail).toHaveBeenCalledTimes(1) - fireEvent.click(screen.getByRole('button', { name: 'Priority' })) - fireEvent.click(screen.getByRole('button', { name: 'Label' })) - fireEvent.change(screen.getByRole('textbox', { name: 'Issue title' }), { - target: { value: 'Local title' } - }) - fireEvent.blur(screen.getByRole('textbox', { name: 'Issue title' })) - expect(reads.update).toHaveBeenCalledWith( - expect.anything(), - task().id, - { labelIds: ['bug'] }, - 'w' - ) - expect(screen.getByTestId('drawer').textContent).toBe('In Review:4:Bug') - await act(async () => { - old.resolve(task('Stale')) - await old.promise - labels.resolve({ ok: true }) - await labels.promise - }) - expect(screen.getByTestId('drawer').textContent).toBe('In Review:4:Bug') - await act(async () => { - await writeLinearIssue( - task(), - { title: 'Sibling title' }, - defaultScopeSource(null), - async () => ({ ok: true }) - ) - }) - await act(async () => { - priority.resolve({ ok: false }) - title.resolve({ ok: false }) - await Promise.all([priority.promise, title.promise]) - }) - expect(screen.getByTestId('drawer').textContent).toBe('In Review:0:Bug') - expect(screen.getByRole('textbox', { name: 'Issue title' }).value).toBe( - 'Sibling title' - ) - expect(reads.detail).toHaveBeenCalledTimes(2) - await act(async () => { - fresh.resolve({ - ...task('External'), - title: 'Server title', - priority: 2, - labels: [], - labelIds: [] - }) - await fresh.promise - }) - expect(screen.getByTestId('drawer').textContent).toBe('External:2:') - expect(screen.getByRole('textbox', { name: 'Issue title' }).value).toBe( - 'Server title' - ) -}) - -it('confirmed project selection retires old hydration and publishes through the mounted detail consumer', async () => { - store() - const old = deferred(), - external = deferred() - const pending = deferred<{ ok: boolean }>() - reads.detail.mockReturnValueOnce(old.promise).mockReturnValueOnce(external.promise) - reads.projects.mockResolvedValue({ items: [{ id: 'chosen', name: 'Chosen' }] }) - reads.update.mockReturnValueOnce(pending.promise) - function Detail() { - const detail = useLinearIssueWorkspaceDetail({ - issue: task(), - providerSettings: defaultScopeSource(null), - requestKey: 'project' - }) - return ( - <> - {detail.displayed.project?.name ?? 'None'} - - - ) - } - render() - fireEvent.click(screen.getByRole('button', { name: 'Add to project' })) - fireEvent.change(screen.getByPlaceholderText('Search projects'), { target: { value: 'Chosen' } }) - fireEvent.click(await screen.findByRole('button', { name: 'Chosen' })) - expect(screen.getByTestId('project').textContent).toBe('None') - expect(reads.update).toHaveBeenCalledWith( - defaultScopeSource(null), - task().id, - { projectId: 'chosen' }, - 'w' - ) - await act(async () => { - pending.resolve({ ok: true }) - await pending.promise - }) - expect(screen.getByTestId('project').textContent).toBe('Chosen') - expect(pendingLinearIssueWrites.size).toBe(0) - expect(reads.detail).toHaveBeenCalledTimes(2) - await act(async () => { - old.resolve(task()) - await old.promise - }) - expect(screen.getByTestId('project').textContent).toBe('Chosen') - await act(async () => { - external.resolve({ ...task(), project: { id: 'external', name: 'External' } }) - await external.promise - }) - expect(screen.getByTestId('project').textContent).toBe('External') -}) diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-selection-decisions.test.tsx b/src/renderer/src/components/task-page/linear/task-page-linear-selection-decisions.test.tsx deleted file mode 100644 index 504988edbae..00000000000 --- a/src/renderer/src/components/task-page/linear/task-page-linear-selection-decisions.test.tsx +++ /dev/null @@ -1,98 +0,0 @@ -// @vitest-environment happy-dom -import { - reads, - store, - source, - task, - useRoutingFixture -} from './task-page-linear-refresh-test-fixtures' -import { reads as issueReadRegistry } from '@/store/slices/linear/linear-issue-read-lifecycle' -import '@/store/slices/linear/linear-issue-read' -import { pendingLinearIssueWrites } from '@/store/slices/linear/linear-issue-write-publication' -import { deferred } from '@/store/slices/linear/linear-slice-test-harness' -import type { useTaskPageDetailRouting } from '../use-task-page-detail-routing' -import { expect, it } from 'vitest' -import { act, render, renderHook, screen } from '@testing-library/react' -import { Suspense, startTransition, useLayoutEffect } from 'react' - -it('independent urgent clear supersedes an uncommitted nonempty selection', async () => { - const s = store(), - context = source('local'), - blocked = deferred() - s.setState({ jiraIssueCache: {}, jiraSearchCache: {} }) - let route!: ReturnType - let hold = true - const seen: string[] = [], - commits: string[] = [] - function Consumer() { - const current = useRoutingFixture(context) - const id = current.selectedLinearIssueId ?? 'none' - seen.push(id) - useLayoutEffect(() => { - route = current - commits.push(id) - }) - if (id === 'B' && hold) { - throw blocked.promise - } - return {id} - } - const mounted = render( - Blocked}> - - - ) - try { - await act(async () => { - startTransition(() => route.setSelectedLinearIssue({ ...task('B'), id: 'B' })) - }) - expect(seen).toContain('B') - expect(commits).not.toContain('B') - expect(screen.getByTestId('independent-clear').textContent).toBe('none') - const initialRequest = route.linearSelectionRequest - act(() => route.clearSelectedLinearIssue()) - const clearRequest = route.linearSelectionRequest - expect(clearRequest).not.toBe(initialRequest) - expect(screen.getByTestId('independent-clear').textContent).toBe('none') - hold = false - await act(async () => { - blocked.resolve() - await blocked.promise - }) - expect(screen.getByTestId('independent-clear').textContent).toBe('none') - expect(route.linearSelectionRequest).toBe(clearRequest) - } finally { - mounted.unmount() - hold = false - blocked.resolve() - await blocked.promise - expect(commits).not.toContain('B') - expect(pendingLinearIssueWrites.size).toBe(0) - expect(issueReadRegistry.size).toBe(0) - expect(reads.detail).toHaveBeenCalledTimes(0) - } -}) - -it('same-batch explicit Clear wins while an obsolete observed request cannot clear a newer decision', () => { - store().setState({ jiraIssueCache: {}, jiraSearchCache: {} }) - const hook = renderHook(() => useRoutingFixture(source('local'))) - const request = hook.result.current.linearSelectionRequest - act(() => { - hook.result.current.setSelectedLinearIssue(task()) - hook.result.current.clearSelectedLinearIssue() - }) - expect(hook.result.current.selectedLinearIssue).toBeNull() - const clearRequest = hook.result.current.linearSelectionRequest - expect(clearRequest).not.toBe(request) - hook.rerender() - expect(hook.result.current.linearSelectionRequest).toBe(clearRequest) - act(() => { - hook.result.current.setSelectedLinearIssue(task()) - hook.result.current.setSelectedLinearIssue(null, { expectedSelection: clearRequest }) - }) - expect(hook.result.current.selectedLinearIssue?.id).toBe(task().id) - hook.unmount() - expect(pendingLinearIssueWrites.size).toBe(0) - expect(issueReadRegistry.size).toBe(0) - expect(reads.detail).not.toHaveBeenCalled() -}) diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-view-state.ts b/src/renderer/src/components/task-page/linear/use-task-page-linear-view-state.ts index 20835165110..eaf95a66938 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-view-state.ts +++ b/src/renderer/src/components/task-page/linear/use-task-page-linear-view-state.ts @@ -9,7 +9,7 @@ import { buildLinearIssueWorkspaceAttachmentIndex } from '@/lib/work-items/linea import { collectLinkedLinearIssueRefsFromWorktrees, linkedLinearIssueRefsSignature -} from '@/components/task-page/linear/task-page-linear-in-orca-issues' +} from '@/components/task-page/linear/issues/task-page-linear-in-orca-issues' import type { LinearMode } from '@/components/task-page/task-page-localized-options' import { type LinearViewMode, @@ -33,8 +33,8 @@ import type { LinearIssueAttributeFilter } from '../../../../../shared/linear/is import type { LinearIssueListFilterRead, LinearPrimaryTeamObservation -} from '@/components/task-page/linear/task-page-linear-issue-request' -import type { LinearProjectTab } from './task-page-linear-issue-model' +} from '@/components/task-page/linear/issues/task-page-linear-issue-request' + import { useTaskPageLinearContextState } from './use-task-page-linear-context-state' export type TaskPageLinearViewStatePreludeModel = ReturnType< typeof useTaskPageLinearViewStatePrelude @@ -255,3 +255,5 @@ export function useTaskPageLinearViewState(model: TaskPageDetailRoutingModel) { return useTaskPageLinearContextState(preludeModel) } export type TaskPageLinearViewStateModel = ReturnType + +export type LinearProjectTab = 'overview' | 'issues' diff --git a/src/renderer/src/components/task-page/linear/CustomViewList.tsx b/src/renderer/src/components/task-page/linear/views/CustomViewList.tsx similarity index 96% rename from src/renderer/src/components/task-page/linear/CustomViewList.tsx rename to src/renderer/src/components/task-page/linear/views/CustomViewList.tsx index 9654c1fcd8a..69f7d035a04 100644 --- a/src/renderer/src/components/task-page/linear/CustomViewList.tsx +++ b/src/renderer/src/components/task-page/linear/views/CustomViewList.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { useTranslate } from '@/i18n/use-translate' import { LinearCustomViewTable, diff --git a/src/renderer/src/components/task-page/linear/CustomViewProjects.tsx b/src/renderer/src/components/task-page/linear/views/CustomViewProjects.tsx similarity index 97% rename from src/renderer/src/components/task-page/linear/CustomViewProjects.tsx rename to src/renderer/src/components/task-page/linear/views/CustomViewProjects.tsx index c9a0f7ccc8f..12fa0487dcb 100644 --- a/src/renderer/src/components/task-page/linear/CustomViewProjects.tsx +++ b/src/renderer/src/components/task-page/linear/views/CustomViewProjects.tsx @@ -1,4 +1,4 @@ -import type { TaskPageComposerActionsModel } from '../use-task-page-composer-actions' +import type { TaskPageComposerActionsModel } from '../../use-task-page-composer-actions' import { Button } from '@/components/ui/button' import { useTranslate } from '@/i18n/use-translate' import { ChevronLeft, ExternalLink } from 'lucide-react' diff --git a/src/renderer/src/components/task-page/linear/use-task-page-linear-custom-view-effects.ts b/src/renderer/src/components/task-page/linear/views/use-task-page-linear-custom-view-effects.ts similarity index 86% rename from src/renderer/src/components/task-page/linear/use-task-page-linear-custom-view-effects.ts rename to src/renderer/src/components/task-page/linear/views/use-task-page-linear-custom-view-effects.ts index 097e5fa787d..7141fbc214b 100644 --- a/src/renderer/src/components/task-page/linear/use-task-page-linear-custom-view-effects.ts +++ b/src/renderer/src/components/task-page/linear/views/use-task-page-linear-custom-view-effects.ts @@ -1,16 +1,16 @@ import { observeLinearIssueRead } from '@/store/slices/linear/linear-issue-read-lifecycle' import { linearIssueReadError } from '@/lib/work-items/linear/linear-read-error' import { taskProviderErrorText } from '@/lib/work-items/task-provider-error-copy' -import type { TaskPageLinearInOrcaEffectsModel } from './use-task-page-linear-in-orca-effects' +import type { TaskPageLinearInOrcaEffectsModel } from '../issues/use-task-page-linear-in-orca-effects' import { useEffect } from 'react' -import { - LINEAR_CUSTOM_VIEW_MODELS, - mergeLinearCollectionResults -} from './task-page-linear-issue-model' -import { LINEAR_ITEM_LIMIT } from '../task-page-source-context' -import type { LinearCollectionResult } from '../../../../../shared/linear/workspace-types' -import type { LinearCustomViewSummary } from '../../../../../shared/linear/project-types' -import { clampLinearIssueListLimit } from '../../../../../shared/linear/issue-read-limits' + +import { LINEAR_ITEM_LIMIT } from '../../task-page-source-context' +import type { LinearCollectionResult } from '../../../../../../shared/linear/workspace-types' +import type { + LinearCustomViewSummary, + LinearCustomViewModel +} from '../../../../../../shared/linear/project-types' +import { clampLinearIssueListLimit } from '../../../../../../shared/linear/issue-read-limits' export function useTaskPageLinearCustomViewEffects(model: TaskPageLinearInOrcaEffectsModel) { const { getCachedLinearCustomViews, @@ -209,3 +209,27 @@ export function useTaskPageLinearCustomViewEffects(model: TaskPageLinearInOrcaEf export type TaskPageLinearCustomViewEffectsModel = ReturnType< typeof useTaskPageLinearCustomViewEffects > + +export const LINEAR_CUSTOM_VIEW_MODELS = [ + 'issue', + 'project' +] satisfies readonly LinearCustomViewModel[] + +export function mergeLinearCollectionResults( + results: LinearCollectionResult[] +): LinearCollectionResult { + const errors = results.flatMap((result) => result.errors ?? []) + return { + items: results.flatMap((result) => result.items), + ...(errors.length > 0 + ? { + errors + } + : {}), + ...(results.some((result) => result.hasMore) + ? { + hasMore: true + } + : {}) + } +} diff --git a/src/renderer/src/components/task-page/use-task-page-composer-actions.ts b/src/renderer/src/components/task-page/use-task-page-composer-actions.ts index 9379dc5c750..0f5f2c7795d 100644 --- a/src/renderer/src/components/task-page/use-task-page-composer-actions.ts +++ b/src/renderer/src/components/task-page/use-task-page-composer-actions.ts @@ -1,4 +1,4 @@ -import type { TaskPageJiraListEffectsModel } from './jira/use-task-page-jira-list-effects' +import type { TaskPageJiraListEffectsModel } from './jira/listing/use-task-page-jira-list-effects' import { useCallback } from 'react' import type { LinearIssue } from '../../../../shared/linear/issue-types' import type { LinearWorkspaceSelection } from '../../../../shared/linear/workspace-types' diff --git a/src/renderer/src/components/task-page/use-task-page-detail-routing.ts b/src/renderer/src/components/task-page/use-task-page-detail-routing.ts index 3b0fc51893d..991de5e4241 100644 --- a/src/renderer/src/components/task-page/use-task-page-detail-routing.ts +++ b/src/renderer/src/components/task-page/use-task-page-detail-routing.ts @@ -11,7 +11,7 @@ import type { LinearIssue } from '../../../../shared/linear/issue-types' import type { JiraIssue } from '../../../../shared/jira/jira-types' import { useAppStore } from '@/store' import { useShallow } from 'zustand/react/shallow' -import { findTaskPageJiraIssue } from '@/components/task-page/jira/task-page-jira-cache-selectors' +import { findTaskPageJiraIssue } from '@/components/task-page/jira/listing/task-page-jira-cache-selectors' export function useTaskPageDetailRouting(model: TaskPageGitHubIssueDraftModel) { const { pageData, diff --git a/src/renderer/src/components/task-page/use-task-page-global-effects.ts b/src/renderer/src/components/task-page/use-task-page-global-effects.ts index 528bc6108dc..1d12b1b3c37 100644 --- a/src/renderer/src/components/task-page/use-task-page-global-effects.ts +++ b/src/renderer/src/components/task-page/use-task-page-global-effects.ts @@ -1,4 +1,4 @@ -import type { TaskPageJiraSummaryGenerationModel } from './jira/use-task-page-jira-summary-generation' +import type { TaskPageJiraSummaryGenerationModel } from './jira/creation/use-task-page-jira-summary-generation' import { useEffect } from 'react' import { addAppKeyListener, removeAppKeyListener } from '@/lib/keyboard/app-keyboard-router' export function useTaskPageGlobalEffects(model: TaskPageJiraSummaryGenerationModel) { diff --git a/src/renderer/src/components/task-page/use-task-page-source-display.ts b/src/renderer/src/components/task-page/use-task-page-source-display.ts index 880a4da8946..77b076b586a 100644 --- a/src/renderer/src/components/task-page/use-task-page-source-display.ts +++ b/src/renderer/src/components/task-page/use-task-page-source-display.ts @@ -1,4 +1,4 @@ -import type { TaskPageLinearCreationStateModel } from './linear/use-task-page-linear-creation-state' +import type { TaskPageLinearCreationStateModel } from './linear/creation/use-task-page-linear-creation-state' import { useMemo } from 'react' import type { GitHubTaskKind } from '../../../../shared/work-items/task-github-search' import { buildGitHubRepoUrl } from '../../../../shared/github/repositories/links' diff --git a/src/renderer/src/components/use-task-creation-draft-retention.test.ts b/src/renderer/src/components/use-task-creation-draft-retention.test.ts index 6d6ecfc4fda..b3af1723f8e 100644 --- a/src/renderer/src/components/use-task-creation-draft-retention.test.ts +++ b/src/renderer/src/components/use-task-creation-draft-retention.test.ts @@ -1,407 +1,16 @@ // @vitest-environment happy-dom -import { act, renderHook, render, screen } from '@testing-library/react' -import { describe, expect, it, vi } from 'vitest' +// @vitest-environment happy-dom +import { act, renderHook } from '@testing-library/react' +import { expect, it } from 'vitest' import { useTaskCreationDraftRetention } from './use-task-creation-draft-retention' -import { store, reads } from './task-page/linear/task-page-linear-refresh-test-fixtures' -import { - useLinearIssueCreationForm, - useTaskPageLinearCreationState -} from './task-page/linear/use-task-page-linear-creation-state' -import { useTaskPageJiraCreationStatePrelude } from './task-page/jira/use-task-page-jira-creation-state' +import { store } from './task-page/linear/issue-refresh/task-page-linear-refresh-test-fixtures' + import { writeNewLinearProjectDraft, writeNewJiraIssueDraft } from './task-page/task-page-draft-storage' -import { deferred } from '../store/slices/linear/linear-slice-test-harness' -import { - createElement, - Suspense, - startTransition, - useLayoutEffect, - useCallback, - useEffect, - useState -} from 'react' - -type Draft = { title: string; body: string } - -const emptyDraft = (): Draft => ({ title: '', body: '' }) - -describe('useTaskCreationDraftRetention', () => { - it('does not fan out writes while typing and saves the latest text on dismissal', () => { - const writeDraft = vi.fn() - const view = renderHook( - ({ open, draft }) => useTaskCreationDraftRetention({ open, draft, writeDraft }), - { initialProps: { open: true, draft: emptyDraft() } } - ) - - view.rerender({ open: true, draft: { title: 'Bug', body: '' } }) - view.rerender({ open: true, draft: { title: 'Bug', body: 'Latest details' } }) - - expect(writeDraft).not.toHaveBeenCalled() - - view.rerender({ open: false, draft: emptyDraft() }) - - expect(writeDraft).toHaveBeenCalledOnce() - expect(writeDraft).toHaveBeenCalledWith({ title: 'Bug', body: 'Latest details' }) - }) - - it('saves the latest text when an open composer unmounts', () => { - const writeDraft = vi.fn() - const view = renderHook( - ({ draft }) => useTaskCreationDraftRetention({ open: true, draft, writeDraft }), - { initialProps: { draft: { title: 'First', body: '' } } } - ) - view.rerender({ draft: { title: 'Latest', body: 'Details' } }) - - view.unmount() - - expect(writeDraft).toHaveBeenCalledOnce() - expect(writeDraft).toHaveBeenCalledWith({ title: 'Latest', body: 'Details' }) - }) -}) - -it.each([false, true])( - 'real skipped title and replayed body/completion retain the whole successor (close before success=$0)', - async (closeBeforeSuccess) => { - const s = store(), - blocked = deferred() - const save = vi.spyOn(s.getState(), 'setNewLinearIssueDraft') - const clear = vi.spyOn(s.getState(), 'clearNewLinearIssueDraft') - let form!: ReturnType, low!: () => void - let hold = true - const renders: string[] = [], - commits: string[] = [] - function Composer() { - const [pending, setPending] = useState(false) - const current = useLinearIssueCreationForm('R1', { - open: true, - title: 'Original', - body: 'Body' - }) - const value = `${current.newLinearIssueTitle}/${current.newLinearIssueBody}/${current.newLinearIssueOpen}` - renders.push(value) - useLayoutEffect(() => { - form = current - low = () => setPending(true) - commits.push(value) - }) - if (pending && hold) { - throw blocked.promise - } - return createElement('output', { 'data-testid': 'draft-queue' }, value) - } - const view = render(createElement(Suspense, { fallback: 'Held' }, createElement(Composer))) - const titleSetter = form.setNewLinearIssueTitle - await act(async () => { - startTransition(() => { - form.setNewLinearIssueTitle('Queued title') - low() - }) - }) - expect(renders).toContain('Queued title/Body/true') - expect(commits).not.toContain('Queued title/Body/true') - act(() => form.setNewLinearIssueBody('Current body')) - const submitted = form.captureNewLinearIssueDraft() - expect(save).not.toHaveBeenCalled() - expect(clear).not.toHaveBeenCalled() - if (closeBeforeSuccess) { - act(() => form.setNewLinearIssueOpen(false)) - expect(s.getState().newLinearIssueDraft).toBe(submitted.draft) - expect(s.getState().newLinearIssueDraft).toEqual({ title: 'Original', body: 'Current body' }) - } - act(() => form.completeNewLinearIssueDraft(submitted)) - expect(s.getState().newLinearIssueDraft).toBeNull() - expect(form.newLinearIssueOpen).toBe(false) - hold = false - await act(async () => { - blocked.resolve() - await blocked.promise - }) - expect(screen.getByTestId('draft-queue').textContent).toBe( - `Queued title/Current body/${!closeBeforeSuccess}` - ) - expect(form.setNewLinearIssueTitle).toBe(titleSetter) - if (closeBeforeSuccess) { - const saved = s.getState().newLinearIssueDraft - expect.soft(saved).toEqual({ title: 'Queued title', body: 'Current body' }) - act(() => { - form.setNewLinearIssueTitle(saved?.title ?? '') - form.setNewLinearIssueBody(saved?.body ?? '') - form.setNewLinearIssueOpen(true) - }) - expect.soft(form.newLinearIssueTitle).toBe('Queued title') - expect.soft(form.newLinearIssueBody).toBe('Current body') - } else { - expect(s.getState().newLinearIssueDraft).toBeNull() - } - act(() => { - form.setNewLinearIssueTitle((v) => `${v} final`) - form.setNewLinearIssueBody((v) => `${v} final`) - form.setNewLinearIssueOpen(false) - }) - expect(s.getState().newLinearIssueDraft).toEqual({ - title: 'Queued title final', - body: 'Current body final' - }) - act(() => { - const saved = s.getState().newLinearIssueDraft! - form.setNewLinearIssueTitle(saved.title) - form.setNewLinearIssueBody(saved.body) - form.setNewLinearIssueOpen(true) - }) - expect(form).toMatchObject({ - newLinearIssueTitle: 'Queued title final', - newLinearIssueBody: 'Current body final', - newLinearIssueOpen: true - }) - view.unmount() - } -) - -it.each([ - { title: 'Old', body: 'Body' }, - { title: '', body: '' }, - { title: ' \t', body: '\n ' } -])( - 'queued old-runtime text composes before the actual prelude handoff, never after it (initial=$title/$body)', - async (initial) => { - const s = store(), - blocked = deferred() - let form!: ReturnType, - change!: (runtime: string) => void, - low!: () => void - let hold = true - const commits: string[] = [], - seen: string[] = [] - function Composer() { - const [runtime, setRuntime] = useState('R1'), - [pending, setPending] = useState(false) - const enroll = useCallback(() => setPending(true), []) - const current = useLinearIssueCreationForm(runtime, { open: true, ...initial }) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the real prelude consumes only Linear reset and inert Jira state on this runtime transition. - useTaskPageJiraCreationStatePrelude({ - ...current, - providerRuntimeContextKey: runtime, - setNewLinearIssueProjects: vi.fn(), - setNewLinearIssueProjectsLoading: vi.fn() - } as never) - const value = `${runtime}/${current.newLinearIssueTitle}/${current.newLinearIssueBody}/${current.newLinearIssueOpen}` - seen.push(value) - useLayoutEffect(() => { - form = current - change = setRuntime - low = enroll - commits.push(value) - }, [current, value, enroll]) - if (pending && hold) { - throw blocked.promise - } - return null - } - const view = render(createElement(Suspense, { fallback: null }, createElement(Composer))) - const old = form - await act(async () => { - startTransition(() => { - old.setNewLinearIssueTitle('Queued old') - old.setNewLinearIssueBody('Queued body') - low() - }) - }) - expect(seen).toContain('R1/Queued old/Queued body/true') - expect(commits).not.toContain('R1/Queued old/Queued body/true') - act(() => change('R2')) - expect(commits).toContain('R2///false') - expect(commits).not.toContain('R1/Queued old/Queued body/true') - expect(s.getState().newLinearIssueDraft).toEqual(initial.title.trim() ? initial : null) - hold = false - await act(async () => { - blocked.resolve() - await blocked.promise - }) - expect(form.newLinearIssueOpen).toBe(false) - const saved = s.getState().newLinearIssueDraft - expect.soft(saved).toEqual({ title: 'Queued old', body: 'Queued body' }) - act(() => { - form.setNewLinearIssueTitle(saved?.title ?? '') - form.setNewLinearIssueBody(saved?.body ?? '') - form.setNewLinearIssueOpen(true) - }) - act(() => { - old.setNewLinearIssueTitle('Late old') - old.resetNewLinearIssue() - }) - expect.soft(form.newLinearIssueTitle).toBe('Queued old') - expect.soft(form.newLinearIssueBody).toBe('Queued body') - expect(form.newLinearIssueOpen).toBe(true) - view.unmount() - } -) - -it('current-target layout actions precede delayed runtime reset and obsolete queued completion/finally', async () => { - const s = store(), - blocked = deferred(), - oldAttempt = {}, - replacement = {} - let form!: ReturnType, - change!: (runtime: string) => void, - low!: () => void, - pulse!: () => void - let hold = true, - acquired = 0, - released = 0 - const commits: string[] = [], - seen: string[] = [] - s.setState({ persistedUIReady: false }) - reads.projects.mockResolvedValue({ items: [] }) - const teams = [{ id: 'team-1', workspaceId: 'w' }] - function Composer() { - const [runtime, setRuntime] = useState('R1'), - [pending, setPending] = useState(false), - [tick, setTick] = useState(0) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: actual creation state consumes these account/team/runtime inputs; tour readiness is disabled. - const current = useTaskPageLinearCreationState({ - providerRuntimeContextKey: runtime, - settings: null, - activeModal: 'none', - linearConnected: true, - selectedLinearWorkspaceId: 'w', - linearTaskSourceContext: null, - availableTeams: teams - } as never) - const { - setNewLinearIssueTitle, - setNewLinearIssueBody, - setNewLinearIssueOpen, - setNewLinearIssueStateId, - beginNewLinearIssue - } = current - const enroll = useCallback(() => setPending(true), []) - const advance = useCallback(() => setTick((n) => n + 1), []) - useLayoutEffect(() => { - if (runtime === 'R1') { - setNewLinearIssueTitle('Old') - setNewLinearIssueBody('Body') - setNewLinearIssueOpen(true) - } else { - setNewLinearIssueStateId('new-default') - setNewLinearIssueTitle('Replacement') - setNewLinearIssueBody('Replacement body') - setNewLinearIssueOpen(true) - beginNewLinearIssue(replacement) - } - }, [ - runtime, - setNewLinearIssueTitle, - setNewLinearIssueBody, - setNewLinearIssueOpen, - setNewLinearIssueStateId, - beginNewLinearIssue - ]) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: actual runtime reset owner receives its Linear inputs; Jira is closed and unrelated metadata callbacks are inert. - useTaskPageJiraCreationStatePrelude({ - ...current, - providerRuntimeContextKey: runtime - } as never) - useEffect(() => { - acquired++ - return () => { - released++ - } - }, [current.setNewLinearIssueTitle]) - const value = `${runtime}/${current.newLinearIssueTitle}/${current.newLinearIssueSubmitting}/${tick}` - seen.push(value) - useLayoutEffect(() => { - form = current - change = setRuntime - low = enroll - pulse = advance - commits.push(value) - }, [current, value, enroll, advance]) - if (pending && hold) { - throw blocked.promise - } - return null - } - const view = render(createElement(Suspense, { fallback: null }, createElement(Composer))) - act(() => form.beginNewLinearIssue(oldAttempt)) - const old = form, - submitted = old.captureNewLinearIssueDraft() - seen.length = 0 - commits.length = 0 - await act(async () => { - startTransition(() => { - old.completeNewLinearIssueDraft(submitted) - old.settleNewLinearIssue(oldAttempt) - low() - }) - }) - expect(seen).toContain('R1//false/0') - expect(commits).not.toContain('R1//false/0') - act(() => change('R2')) - expect(form).toMatchObject({ - newLinearIssueOpen: true, - newLinearIssueTitle: 'Replacement', - newLinearIssueBody: 'Replacement body', - newLinearIssueStateId: 'new-default', - newLinearIssueSubmitting: true - }) - hold = false - await act(async () => { - blocked.resolve() - await blocked.promise - }) - act(() => { - old.settleNewLinearIssue(oldAttempt) - old.setNewLinearIssueTitle('Late') - old.resetNewLinearIssue() - pulse() - }) - expect(form).toMatchObject({ - newLinearIssueOpen: true, - newLinearIssueTitle: 'Replacement', - newLinearIssueSubmitting: true - }) - expect(acquired).toBe(2) - expect(released).toBe(1) - act(() => form.settleNewLinearIssue(replacement)) - act(() => form.setNewLinearIssueOpen(false)) - expect(s.getState().newLinearIssueDraft).toEqual({ - title: 'Replacement', - body: 'Replacement body' - }) - view.unmount() - expect(released).toBe(2) -}) - -it('unchanged exact issue success stays disarmed through harmless closed renders and unmount', () => { - const s = store() - const save = vi.spyOn(s.getState(), 'setNewLinearIssueDraft') - const clear = vi.spyOn(s.getState(), 'clearNewLinearIssueDraft') - const view = renderHook(() => - useLinearIssueCreationForm('R1', { open: true, title: 'Created', body: 'Details' }) - ) - const submitted = view.result.current.captureNewLinearIssueDraft() - act(() => { - view.result.current.setNewLinearIssueTitle((value) => value) - view.result.current.setNewLinearIssuePriority(3) - }) - expect(view.result.current.captureNewLinearIssueDraft()).toBe(submitted) - expect(save).not.toHaveBeenCalled() - expect(clear).not.toHaveBeenCalled() - act(() => view.result.current.completeNewLinearIssueDraft(submitted)) - expect(view.result.current.newLinearIssueOpen).toBe(false) - expect(s.getState().newLinearIssueDraft).toBeNull() - act(() => view.result.current.setNewLinearIssuePriority(4)) - view.rerender() - expect(view.result.current.captureNewLinearIssueDraft()).toBe(submitted) - view.unmount() - expect(save).not.toHaveBeenCalled() - expect(clear).toHaveBeenCalledOnce() - expect(s.getState().newLinearIssueDraft).toBeNull() -}) it('project and Jira immediate discard keep their actual plain session slots cleared through close', () => { const s = store() diff --git a/tests/e2e/task-page-linear-refresh-authority.test.tsx b/tests/e2e/task-page-linear-refresh-authority.test.tsx deleted file mode 100644 index 45545c6a58e..00000000000 --- a/tests/e2e/task-page-linear-refresh-authority.test.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { useLinearIssueResult } from '@/hooks/linear-issue-committed-state' -// @vitest-environment happy-dom -import { - reads, - task, - store, - fixture, - source -} from '../../src/renderer/src/components/task-page/linear/task-page-linear-refresh-test-fixtures' - -import { defaultScopeSource } from '@/runtime/hosts/selection/default-creation-host' -import { getLinearSourceScope } from '@/store/slices/linear/linear-slice-scope' - -import type { LinearIssue } from '../../src/shared/linear/issue-types' - -import { useTaskPageLinearInOrcaEffects } from '../../src/renderer/src/components/task-page/linear/use-task-page-linear-in-orca-effects' -import { useTaskPageDetailRouting } from '../../src/renderer/src/components/task-page/use-task-page-detail-routing' -import { useLinearIssueWorkspaceDetail } from '../../src/renderer/src/components/linear-issue-workspace-detail-state' -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { act, renderHook, waitFor } from '@testing-library/react' - -import { getTaskSourceCacheScope } from '../../src/shared/work-items/task-source-context' - -const cacheTime = Date.parse('2026-01-01T00:00:00Z') -beforeEach(() => { - vi.useFakeTimers({ toFake: ['Date'] }) - vi.setSystemTime(cacheTime) -}) -afterEach(() => vi.useRealTimers()) - -it('retains ordered linked source provenance when two hosts return the same UUID', async () => { - const s = store() - const first = source('local'), - second = source('runtime:other') - const refs = [ - { identifier: 'DAT-124', workspaceId: 'w', sourceContext: first }, - { identifier: 'DAT-124', workspaceId: 'w', sourceContext: second } - ] - // Real detail actions create the requested source/identifier keys. - reads.detail.mockResolvedValueOnce(task()).mockResolvedValueOnce(task('Done')) - const base = { - ...fixture(s, []), - ...s.getState(), - taskResumeApplied: true, - taskSource: 'linear', - linearMode: 'in-orca', - linearConnected: true, - linearRefreshNonce: 0, - lastLinearRequestRef: { current: null }, - inOrcaLinkedLinearRefsRef: { current: refs }, - inOrcaLinkedLinearRefsSignature: 'two-hosts', - setLinearLoading: vi.fn(), - setLinearError: vi.fn(), - setLinearIssuesHasMore: vi.fn() - } - const hook = renderHook(() => { - const result = useLinearIssueResult( - 'two-hosts', - [], - getLinearSourceScope(defaultScopeSource(null)) - ) - // SAFETY: fixture supplies the linked loader fields. - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - useTaskPageLinearInOrcaEffects({ - ...base, - linearIssueResult: result, - setLinearIssues: result.setData - } as never) - return result.data - }) - await waitFor(() => expect(hook.result.current).toHaveLength(2)) - act(() => - s - .getState() - .patchLinearIssue('same-uuid', { state: task('Blocked').state }, { sourceContext: first }) - ) - expect(hook.result.current.map((item) => item.state.name)).toEqual(['Blocked', 'Done']) - act(() => - s.getState().patchLinearIssue('same-uuid', { state: task().state }, { sourceContext: first }) - ) - expect(hook.result.current.map((item) => item.state.name)).toEqual(['In Review', 'Done']) -}) - -it('opens the fresh row on its origin source despite stale aliases and failed detail hydration', async () => { - const s = store(), - origin = source('runtime:origin'), - foreign = source('local') - s.setState({ - jiraIssueCache: {}, - jiraSearchCache: {}, - linearIssueCache: { - [`${getTaskSourceCacheScope(origin)}::w::DAT-124`]: { data: task(), fetchedAt: 1 }, - [`${getTaskSourceCacheScope(foreign)}::w::same-uuid`]: { data: task(), fetchedAt: cacheTime } - } - }) - reads.detail.mockRejectedValue(new Error('raw detail failure')) - const hook = renderHook(() => { - // SAFETY: routing fixture only opens Linear; Jira and callbacks are inert. - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this fixture supplies the real consumer's exercised fields and actions; unrelated full-page/provider services are intentionally absent. - const route = useTaskPageDetailRouting({ - pageData: { openLinearIssue: task('Done'), openLinearSourceContext: origin }, - linearTaskSourceContext: foreign, - setDialogWorkItem: vi.fn(), - openTaskPage: vi.fn() - } as never) - if (!route.linearDetailSourceContext) { - throw new Error('Expected the routed detail source') - } - const detail = useLinearIssueWorkspaceDetail({ - issue: route.selectedLinearIssue ?? task(), - providerSettings: route.linearDetailSourceContext, - requestKey: 'origin:uuid' - }) - return { route, detail } - }) - await waitFor(() => expect(hook.result.current.detail.issueLoading).toBe(false)) - expect(hook.result.current.detail.displayed.state.name).toBe('Done') - expect(reads.detail).toHaveBeenCalledWith(origin, 'same-uuid', 'w') -}) diff --git a/tests/e2e/task-page-linear-refresh-authority.unit.test.ts b/tests/e2e/task-page-linear-refresh-authority.unit.test.ts deleted file mode 100644 index 6ba3133ffcc..00000000000 --- a/tests/e2e/task-page-linear-refresh-authority.unit.test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// @vitest-environment happy-dom -import './task-page-linear-refresh-authority.test' From c6b9ac9549bddc8feaea906934c641e20334cd3d Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 04:43:35 -0800 Subject: [PATCH 087/178] test: remove duplicate native policy checks (#42011) * test: remove duplicate native policy checks * test: trim repeated clipboard reads --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../computer-use-linux/runtime_render_test.py | 162 +++++++++--------- .../RowSelectionClickTests.swift | 33 ---- .../TextInsertionReadbackTests.swift | 7 - native/wayland-clipboard/x11_test.py | 3 +- 4 files changed, 78 insertions(+), 127 deletions(-) diff --git a/native/computer-use-linux/runtime_render_test.py b/native/computer-use-linux/runtime_render_test.py index d43d1e8b859..48bc2c1f1e0 100644 --- a/native/computer-use-linux/runtime_render_test.py +++ b/native/computer-use-linux/runtime_render_test.py @@ -437,13 +437,10 @@ class RuntimeRenderTest(unittest.TestCase): result = runtime.run_operation(request) self.assertTrue(result["ok"]) self.assertEqual(pointer_calls, [(505, 206), (525, 226)] if operation["tool"] == "drag" else [(505, 206)]) - self.assertGreaterEqual(fixture.clock, 0.1) else: with self.assertRaisesRegex(RuntimeError, "window_not_focused"): runtime.run_operation(request) self.assertEqual(pointer_calls, []) - self.assertGreaterEqual(fixture.clock, 0.5) - self.assertTrue(restored) def test_restoration_requires_selected_window_focus_before_keyboard_input(self): restore_window = runtime.restore_window @@ -528,24 +525,25 @@ class RuntimeRenderTest(unittest.TestCase): self.assertEqual(active[0], 99) def test_text_entry_inserts_into_the_focused_field_of_a_background_window(self): - for tool in ("type_text", "paste_text"): - for value in ("there", "🚀 café"): - for selection in ((6, 11), None): - field = FakeAccessible("text", "Message", value="Hello world") - field.caret, field.selection = 11, selection - field.get_state_set = lambda: FakeStateSet(FakeStateType.FOCUSED, FakeStateType.EDITABLE) - field.get_editable_text_iface = lambda: field - with self.subTest(tool=tool, value=value, selection=selection), SnapshotFixture(FakeAccessible("frame", "Editor", children=[field])) as fixture: - calls = [] - fixture.restore = lambda: calls.append("restore") - with patch.object(runtime, "type_text", lambda _: calls.append("keys")), \ - patch.object(runtime, "paste_text", lambda _: calls.append("clipboard")): - result = runtime.run_operation({"tool": tool, "app": "Fixture", "text": value, "noScreenshot": True}) - self.assertEqual(calls, []) - prefix = "Hello " if selection else "Hello world" - self.assertEqual((field.value, field.caret), (prefix + value, len(prefix) + len(value))) - self.assertEqual(result["action"]["path"], "accessibility") - self.assertEqual(result["action"]["verification"]["state"], "verified") + cases = ( + ("type_text", "🚀 café", (6, 11), "Hello 🚀 café", 12), + ("paste_text", "there", None, "Hello worldthere", 16), + ) + for tool, value, selection, expected, expected_caret in cases: + field = FakeAccessible("text", "Message", value="Hello world") + field.caret, field.selection = 11, selection + field.get_state_set = lambda: FakeStateSet(FakeStateType.FOCUSED, FakeStateType.EDITABLE) + field.get_editable_text_iface = lambda: field + with self.subTest(tool=tool, value=value, selection=selection), SnapshotFixture(FakeAccessible("frame", "Editor", children=[field])) as fixture: + calls = [] + fixture.restore = lambda: calls.append("restore") + with patch.object(runtime, "type_text", lambda _: calls.append("keys")), \ + patch.object(runtime, "paste_text", lambda _: calls.append("clipboard")): + result = runtime.run_operation({"tool": tool, "app": "Fixture", "text": value, "noScreenshot": True}) + self.assertEqual(calls, []) + self.assertEqual((field.value, field.caret), (expected, expected_caret)) + self.assertEqual(result["action"]["path"], "accessibility") + self.assertEqual(result["action"]["verification"]["state"], "verified") def test_element_text_entry_targets_the_named_field_and_never_types_at_focus(self): for editable in (True, False): @@ -574,45 +572,46 @@ class RuntimeRenderTest(unittest.TestCase): keys.assert_not_called() def test_text_entry_without_an_editable_focused_field_still_requires_window_focus(self): - cases = [ - ("not-editable", 1, (6, 11)), - ("count-error", TimeoutError("selection timed out"), (6, 11)), - ("multiple", 2, (6, 11)), - ("range-error", 1, TimeoutError("range timed out")), - ("range-outside", 1, (6, 12)), - ] - for tool in ("type_text", "paste_text"): - for active in (False, True): - for name, count, selection in cases: - field = FakeAccessible("text", "Message", value="Hello world") - field.caret, field.selection = 11, (6, 11) - states = [FakeStateType.FOCUSED] + ([] if name == "not-editable" else [FakeStateType.EDITABLE]) - field.get_state_set = lambda: FakeStateSet(*states) - field.get_editable_text_iface = lambda: field - selected = types.SimpleNamespace(start_offset=selection[0], end_offset=selection[1]) if isinstance(selection, tuple) else selection - with self.subTest(tool=tool, active=active, case=name), SnapshotFixture( - FakeAccessible("frame", "Editor", children=[field]) - ) as fixture, patch.object(runtime.Atspi.Text, "get_n_selections", return_value=count, side_effect=count if isinstance(count, Exception) else None), \ - patch.object(runtime.Atspi.Text, "get_selection", return_value=selected, side_effect=selected if isinstance(selected, Exception) else None), \ - patch.object(runtime.Atspi.EditableText, "insert_text", wraps=FakeEditableText.insert_text) as insert, \ - patch.object(runtime, "type_text") as keys, patch.object(runtime, "paste_text") as paste: - if active: - fixture.root.get_state_set = lambda: FakeStateSet(FakeStateType.ACTIVE) - result = runtime.run_operation({"tool": tool, "app": "Fixture", "text": "x", "noScreenshot": True}) - if tool == "type_text": - keys.assert_called_once_with("x", fixture.root) - else: - paste.assert_called_once_with("x") - self.assertEqual(result["action"]["verification"]["state"], "unverified") - else: - with self.assertRaisesRegex(RuntimeError, "window_not_focused"): - runtime.run_operation({"tool": tool, "app": "Fixture", "text": "x", "noScreenshot": True}) - if not active or tool != "type_text": - keys.assert_not_called() - if not active or tool != "paste_text": - paste.assert_not_called() - insert.assert_not_called() - self.assertEqual((field.value, field.selection), ("Hello world", (6, 11))) + cases = ( + ("type_text", False, "not-editable", 1, (6, 11)), + ("type_text", True, "not-editable", 1, (6, 11)), + ("paste_text", False, "not-editable", 1, (6, 11)), + ("paste_text", True, "not-editable", 1, (6, 11)), + ("type_text", False, "count-error", TimeoutError("selection timed out"), (6, 11)), + ("type_text", False, "multiple", 2, (6, 11)), + ("type_text", False, "range-error", 1, TimeoutError("range timed out")), + ("type_text", False, "range-outside", 1, (6, 12)), + ) + for tool, active, name, count, selection in cases: + field = FakeAccessible("text", "Message", value="Hello world") + field.caret, field.selection = 11, (6, 11) + states = [FakeStateType.FOCUSED] + ([] if name == "not-editable" else [FakeStateType.EDITABLE]) + field.get_state_set = lambda: FakeStateSet(*states) + field.get_editable_text_iface = lambda: field + selected = types.SimpleNamespace(start_offset=selection[0], end_offset=selection[1]) if isinstance(selection, tuple) else selection + with self.subTest(tool=tool, active=active, case=name), SnapshotFixture( + FakeAccessible("frame", "Editor", children=[field]) + ) as fixture, patch.object(runtime.Atspi.Text, "get_n_selections", return_value=count, side_effect=count if isinstance(count, Exception) else None), \ + patch.object(runtime.Atspi.Text, "get_selection", return_value=selected, side_effect=selected if isinstance(selected, Exception) else None), \ + patch.object(runtime.Atspi.EditableText, "insert_text", wraps=FakeEditableText.insert_text) as insert, \ + patch.object(runtime, "type_text") as keys, patch.object(runtime, "paste_text") as paste: + if active: + fixture.root.get_state_set = lambda: FakeStateSet(FakeStateType.ACTIVE) + result = runtime.run_operation({"tool": tool, "app": "Fixture", "text": "x", "noScreenshot": True}) + if tool == "type_text": + keys.assert_called_once_with("x", fixture.root) + else: + paste.assert_called_once_with("x") + self.assertEqual(result["action"]["verification"]["state"], "unverified") + else: + with self.assertRaisesRegex(RuntimeError, "window_not_focused"): + runtime.run_operation({"tool": tool, "app": "Fixture", "text": "x", "noScreenshot": True}) + if not active or tool != "type_text": + keys.assert_not_called() + if not active or tool != "paste_text": + paste.assert_not_called() + insert.assert_not_called() + self.assertEqual((field.value, field.selection), ("Hello world", (6, 11))) def test_partial_text_replacement_fails_without_typing_again(self): tool = "type_text" @@ -643,12 +642,9 @@ class RuntimeRenderTest(unittest.TestCase): def test_unconfirmed_text_insertion_does_not_fall_back_to_another_write(self): write = FakeEditableText.insert_text failures = { - "false-after-write": "foobarbar", "timeout-after-write": "foobarbar", "no-op": "foobar", - "truncated": "foobbar", "wrong-text": "foobazbar", - "count-unavailable": "foobarbar", } tool = "type_text" for failure, expected in failures.items(): @@ -659,16 +655,13 @@ class RuntimeRenderTest(unittest.TestCase): def insert(node, position, text, length): if failure == "no-op": return True - write(node, position, "baz" if failure == "wrong-text" else text, 1 if failure == "truncated" else length) + write(node, position, "baz" if failure == "wrong-text" else text, length) if failure == "timeout-after-write": raise TimeoutError("reply lost after insertion") - return failure != "false-after-write" - def count(node): - return None if failure == "count-unavailable" and node.value != "foobar" else len(node.value) + return True with self.subTest(tool=tool, failure=failure), SnapshotFixture( FakeAccessible("frame", "Editor", children=[field]) ) as fixture, patch.object(runtime.Atspi.EditableText, "insert_text", side_effect=insert), \ - patch.object(runtime.Atspi.Text, "get_character_count", side_effect=count), \ patch.object(runtime, "type_text") as keys, patch.object(runtime, "paste_text") as paste: fixture.root.get_state_set = lambda: FakeStateSet(FakeStateType.ACTIVE) with self.assertRaisesRegex(RuntimeError, "text_insertion_unconfirmed"): @@ -680,21 +673,20 @@ class RuntimeRenderTest(unittest.TestCase): def test_unconfirmed_caret_movement_does_not_report_text_entry_success(self): tool = "type_text" - for failure in (False, True, TimeoutError("provider timed out")): - field = FakeAccessible("text", "Message", value="Hello world") - field.caret, field.selection = 6, (6, 11) - field.get_state_set = lambda: FakeStateSet(FakeStateType.FOCUSED, FakeStateType.EDITABLE) - field.get_editable_text_iface = lambda: field - with self.subTest(tool=tool, failure=failure), SnapshotFixture( - FakeAccessible("frame", "Editor", children=[field]) - ) as fixture, patch.object(runtime.Atspi.Text, "set_caret_offset", side_effect=failure if isinstance(failure, Exception) else None, return_value=failure), \ - patch.object(runtime, "type_text") as keys, patch.object(runtime, "paste_text") as paste: - fixture.root.get_state_set = lambda: FakeStateSet(FakeStateType.ACTIVE) - with self.assertRaisesRegex(RuntimeError, "text_caret_unconfirmed"): - runtime.run_operation({"tool": tool, "app": "Fixture", "text": "there", "noScreenshot": True}) - self.assertEqual((field.value, field.caret), ("Hello there", 6)) - keys.assert_not_called() - paste.assert_not_called() + field = FakeAccessible("text", "Message", value="Hello world") + field.caret, field.selection = 6, (6, 11) + field.get_state_set = lambda: FakeStateSet(FakeStateType.FOCUSED, FakeStateType.EDITABLE) + field.get_editable_text_iface = lambda: field + with SnapshotFixture( + FakeAccessible("frame", "Editor", children=[field]) + ) as fixture, patch.object(runtime.Atspi.Text, "set_caret_offset", return_value=True), \ + patch.object(runtime, "type_text") as keys, patch.object(runtime, "paste_text") as paste: + fixture.root.get_state_set = lambda: FakeStateSet(FakeStateType.ACTIVE) + with self.assertRaisesRegex(RuntimeError, "text_caret_unconfirmed"): + runtime.run_operation({"tool": tool, "app": "Fixture", "text": "there", "noScreenshot": True}) + self.assertEqual((field.value, field.caret), ("Hello there", 6)) + keys.assert_not_called() + paste.assert_not_called() def test_tree_marks_controls_without_enabled_or_sensitive_state_disabled(self): disabled = FakeAccessible("push button", "Continue") @@ -719,8 +711,8 @@ class RuntimeRenderTest(unittest.TestCase): fixture.delays = {tab: 20 for tab in tabs[:-1]} snapshot = runtime.make_snapshot("chrome", False) by_name = {item["name"]: item for item in snapshot["elements"]} - for node in (tabs[-1], buttons[8]): - self.assertEqual(by_name[node.name]["frame"]["x"], fixture.bounds[node].x - 100) + for node, expected_x in ((tabs[-1], 110), (buttons[8], 200)): + self.assertEqual(by_name[node.name]["frame"]["x"], expected_x) self.assertNotIn("Tab 0", by_name) indexes = [item["index"] for item in snapshot["elements"]] self.assertEqual(len(indexes), len(set(indexes))) diff --git a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/RowSelectionClickTests.swift b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/RowSelectionClickTests.swift index e2f9669cfa7..1d4d6d06337 100644 --- a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/RowSelectionClickTests.swift +++ b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/RowSelectionClickTests.swift @@ -53,18 +53,6 @@ final class RowSelectionClickTests: XCTestCase { ]) } - func testOnlyARowThatReadsUnselectedQualifies() { - let tree = outline() - tree.nodes[5] = .init(role: "AXButton", selected: false, parent: 1) - tree.nodes[6] = .init(role: "AXRow", parent: 1) - - XCTAssertTrue(RowSelectionClick.isUnselectedRow(3, ax: tree)) - XCTAssertTrue(RowSelectionClick.isUnselectedRow(4, ax: tree)) - XCTAssertFalse(RowSelectionClick.isUnselectedRow(2, ax: tree)) - XCTAssertFalse(RowSelectionClick.isUnselectedRow(5, ax: tree)) - XCTAssertFalse(RowSelectionClick.isUnselectedRow(6, ax: tree)) - } - func testAClickThatSelectedTheRowNeedsNoWrite() throws { let tree = outline(rowSettable: ["AXSelected"]) tree.nodes[3]?.selected = true @@ -73,27 +61,6 @@ final class RowSelectionClickTests: XCTestCase { XCTAssertEqual(tree.writes, []) } - func testAClickThatSelectedNothingReplacesTheContainerSelection() throws { - let tree = outline(rowSettable: ["AXSelected"]) - - XCTAssertEqual(try RowSelectionClick.confirm(3, ax: tree), .selectedByWrite(attribute: "AXSelectedRows")) - XCTAssertEqual(tree.writes, ["AXSelectedRows:1:3"]) - } - - func testARowWhoseContainerTakesNoWriteSelectsItself() throws { - let tree = outline(containerSettable: [], rowSettable: ["AXSelected"]) - - XCTAssertEqual(try RowSelectionClick.confirm(3, ax: tree), .selectedByWrite(attribute: "AXSelected")) - XCTAssertEqual(tree.writes, ["AXSelected:3"]) - } - - func testACellIsSelectedThroughItsRowInTheOwningContainer() throws { - let tree = outline() - - XCTAssertEqual(try RowSelectionClick.confirm(4, ax: tree), .selectedByWrite(attribute: "AXSelectedRows")) - XCTAssertEqual(tree.writes, ["AXSelectedRows:1:3"]) - } - func testACellWrappedInAGroupIsSelectedThroughItsRow() throws { let tree = outline() tree.nodes[5] = .init(role: "AXGroup", parent: 3) diff --git a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/TextInsertionReadbackTests.swift b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/TextInsertionReadbackTests.swift index 88ce1fd7932..5451d4da06f 100644 --- a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/TextInsertionReadbackTests.swift +++ b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/TextInsertionReadbackTests.swift @@ -8,7 +8,6 @@ final class TextInsertionReadbackTests: XCTestCase { TextInsertionReadback.expectedValue(current: "a😀bcd", selection: 3..<5, inserting: "XY"), "a😀XYd" ) - XCTAssertEqual(TextInsertionReadback.expectedValue(current: "", selection: nil, inserting: "hi"), "hi") } func testUnpredictableInsertionsReturnNil() { @@ -16,7 +15,6 @@ final class TextInsertionReadbackTests: XCTestCase { XCTAssertNil(TextInsertionReadback.expectedValue(current: "abc", selection: 2..<4, inserting: "d")) XCTAssertNil(TextInsertionReadback.expectedValue(current: "a😀", selection: 2..<2, inserting: "d")) XCTAssertNil(TextInsertionReadback.expectedValue(current: "", selection: 0..<0, inserting: "send\n")) - XCTAssertNil(TextInsertionReadback.expectedValue(current: "", selection: 0..<0, inserting: "a\tb")) XCTAssertNil(TextInsertionReadback.expectedValue(current: "hello", selection: 0..<5, inserting: "hello")) } @@ -68,11 +66,6 @@ final class TextInsertionReadbackTests: XCTestCase { XCTAssertEqual(settle(expected: "hello", reads: ["", "h"], secondsPerRead: 0.025), .undetermined) } - func testFieldStillFillingAtTheCapIsUndetermined() { - let reads = (0..<100).map { String(repeating: "a", count: $0) } - XCTAssertEqual(settle(expected: String(repeating: "a", count: 200), reads: reads, secondsPerRead: 0.1), .undetermined) - } - private func settle(expected: String, reads: [String], secondsPerRead: Double) -> TextInsertionReadback.Outcome { var clock = 0.0 var remaining = reads[...] diff --git a/native/wayland-clipboard/x11_test.py b/native/wayland-clipboard/x11_test.py index c7425e0e6a3..f23843ee320 100644 --- a/native/wayland-clipboard/x11_test.py +++ b/native/wayland-clipboard/x11_test.py @@ -48,7 +48,6 @@ class X11PublisherTest(unittest.TestCase): "XDG_RUNTIME_DIR": server.directory.name, "WAYLAND_DISPLAY": "wayland-test"}, timeout=5) self.assertEqual(result.returncode, 0, result.stderr) - self.assertEqual(result.stdout, b"") self.assertTrue(server.disconnected.wait(1)) def read(self): @@ -62,7 +61,7 @@ class X11PublisherTest(unittest.TestCase): with self.subTest(length=len(text)): self.copy(text) self.assertEqual(self.read(), text) - self.assertEqual(self.read(), text) + self.assertEqual(self.read(), b"x" * (16 * 1024 * 1024)) From b5619045e2eaf399a783f445412a208e1ba5c35a Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 05:03:06 -0800 Subject: [PATCH 088/178] test: remove low-value relay suites and probes (#42017) * test: remove relay diagnostic and marker suites * test: remove timing-based migration probes * test: remove reserve plumbing and migration work counters * test: remove admission estimator and rehome work probes --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../src/assignment/assignment-lease.test.ts | 10 +- .../assignment-placed-marker.test.ts | 48 --- .../assignment-rejection-log-window.test.ts | 20 -- .../assignment-rejection-logging.test.ts | 39 +-- .../assignment-reserve-mode-postgres.test.ts | 44 --- .../assignment-reserve-mode.test.ts | 14 +- .../assignment-reserve-spill.test.ts | 152 ---------- .../assignment-roll-replaced-cell.test.ts | 27 +- ...assignment-row-lock-order-postgres.test.ts | 134 --------- .../dead-cell-evacuation-precheck.test.ts | 47 +-- .../drain-migration-row-lookup.test.ts | 54 ---- .../drain-migration-row-scaling.test.ts | 122 -------- ...lease-cell-row-contention-postgres.test.ts | 282 ------------------ .../migration-recovery-postgres.test.ts | 67 +---- .../postgres-drain-send-locking.test.ts | 4 +- ...tered-migration-inventory-postgres.test.ts | 139 --------- .../registered-migration-inventory.test.ts | 120 -------- .../drain-return-admission.test.ts | 68 +---- .../public-assignment-admission.test.ts | 45 +-- .../public-assignment-circuit-breaker.test.ts | 12 +- ...ublic-assignment-drain-return-lane.test.ts | 126 +------- .../public-assignment-overload.test.ts | 71 +---- .../public-assignment-reconnect-lane.test.ts | 20 +- .../idle-regional-rehome-store.test.ts | 126 +------- .../idle-regional-rehome-window.test.ts | 47 --- .../region-correction-restart.test.ts | 4 +- 26 files changed, 58 insertions(+), 1784 deletions(-) delete mode 100644 cloud/apps/relay/src/assignment/assignment-placed-marker.test.ts delete mode 100644 cloud/apps/relay/src/assignment/assignment-reserve-spill.test.ts delete mode 100644 cloud/apps/relay/src/assignment/assignment-row-lock-order-postgres.test.ts delete mode 100644 cloud/apps/relay/src/assignment/migrations/drain-migration-row-lookup.test.ts delete mode 100644 cloud/apps/relay/src/assignment/migrations/drain-migration-row-scaling.test.ts delete mode 100644 cloud/apps/relay/src/assignment/migrations/drain-release-cell-row-contention-postgres.test.ts delete mode 100644 cloud/apps/relay/src/assignment/migrations/registered-migration-inventory-postgres.test.ts delete mode 100644 cloud/apps/relay/src/assignment/migrations/registered-migration-inventory.test.ts diff --git a/cloud/apps/relay/src/assignment/assignment-lease.test.ts b/cloud/apps/relay/src/assignment/assignment-lease.test.ts index d5ac8680c58..96e2807eac8 100644 --- a/cloud/apps/relay/src/assignment/assignment-lease.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-lease.test.ts @@ -73,7 +73,6 @@ describe('assignment lease classification', () => { expect(await classify(await lease({ kid: assignmentLeaseKeyId(OTHER_KEY) }))).toBe( 'bad-signature' ) - expect(await classify('not.a.jwt')).toBe('bad-signature') expect(await classify('x'.repeat(5_000))).toBe('bad-signature') expect(await classify(await lease({ sub: 'user-2' }))).toBe('wrong-host') expect(await classify(await lease({ cellId: 'production-gce-c8' }))).toBe('wrong-cell') @@ -157,24 +156,21 @@ describe('host hello with the lease shadow on', () => { false ) await vi.waitFor(() => expect(recorded).toHaveLength(1)) - return { socket, recorded, verifyCellAssignment } + return { socket } } it('still refuses a host the database refuses, whatever its lease says', async () => { - const { socket, recorded } = await hello({ withLease: true, databaseSays: false }) + const { socket } = await hello({ withLease: true, databaseSays: false }) expect(socket.close).toHaveBeenCalledWith(RELAY_CLOSE_CODE.WRONG_CELL, 'wrong assignment epoch') - expect(recorded).toEqual(['valid:false']) }) it('still admits a host the database admits, with no lease at all', async () => { - const { socket, recorded, verifyCellAssignment } = await hello({ + const { socket } = await hello({ withLease: false, databaseSays: true }) - expect(verifyCellAssignment).toHaveBeenCalledTimes(1) expect(socket.close).not.toHaveBeenCalled() expect(String(socket.send.mock.calls[0]?.[0])).toContain('"type":"host-challenge"') - expect(recorded).toEqual(['absent:true']) }) }) diff --git a/cloud/apps/relay/src/assignment/assignment-placed-marker.test.ts b/cloud/apps/relay/src/assignment/assignment-placed-marker.test.ts deleted file mode 100644 index 205f03576a0..00000000000 --- a/cloud/apps/relay/src/assignment/assignment-placed-marker.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ASSIGNMENT_LIMITS } from '@orca-cloud/relay-contract/assignment-invariants' -import { afterEach, describe, expect, it } from 'vitest' -import { RelayAssignmentStore } from './assignment-store.js' -import { openInMemoryRelayDatabase, type RelayDatabase } from '../database/database.js' - -const CELL = { id: 'cell-a', url: 'https://relay-a.example.com', capacityRequests: 10 } -const IDENTITY = { userId: 'user-a', relayHostId: 'host000000000001' } - -describe('assign marks only the answers it minted as placed', () => { - let database: RelayDatabase | undefined - - afterEach(async () => await database?.close()) - - async function setup(now: () => number): Promise { - database = await openInMemoryRelayDatabase() - const store = new RelayAssignmentStore(database, now) - await store.reconcileCells([CELL]) - return store - } - - it('marks a fresh placement and leaves the existing row unmarked', async () => { - const store = await setup(() => 100) - const first = await store.assign(IDENTITY) - const again = await store.assign(IDENTITY) - - expect(first.placed).toBe(true) - expect(again).toMatchObject({ cellId: first.cellId, assignmentEpoch: first.assignmentEpoch }) - expect(again.placed).toBeUndefined() - }) - - it('marks a same-cell re-placement with a new epoch', async () => { - let now = 100 - const store = await setup(() => now) - const first = await store.assign(IDENTITY) - await store.changeActivity(IDENTITY, 'control', -1) - now += ASSIGNMENT_LIMITS.activityLeaseMs + 1 - await store.releaseExpiredActivityLeases() - await store.releaseExpiredActivity() - now += ASSIGNMENT_LIMITS.dormantTtlMs - const replaced = await store.assign(IDENTITY) - - expect(replaced).toMatchObject({ - cellId: first.cellId, - assignmentEpoch: first.assignmentEpoch + 1 - }) - expect(replaced.placed).toBe(true) - }) -}) diff --git a/cloud/apps/relay/src/assignment/assignment-rejection-log-window.test.ts b/cloud/apps/relay/src/assignment/assignment-rejection-log-window.test.ts index 94491a7de6d..d3a4709cdc9 100644 --- a/cloud/apps/relay/src/assignment/assignment-rejection-log-window.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-rejection-log-window.test.ts @@ -36,28 +36,8 @@ describe('assignment rejection log window', () => { expect(window.admit('assign:placement:host-rate-limited', 'host-a')).toBe(true) expect(window.admit('assign:placement:queue-full', 'host-a')).toBe(true) - expect(window.admit('assign:sticky:host-rate-limited', 'host-a')).toBe(true) expect(window.admit('assign:placement:queue-full', 'host-a')).toBe(false) }) - - it('bounds the tracked keys and reports what an evicted window suppressed', () => { - const timers = manualTimers() - const closed: { key: string; suppressed: number }[] = [] - const window = new AssignmentRejectionLogWindow({ - windowMs: 10_000, - schedule: timers.schedule, - onWindowClosed: ({ key, suppressed }) => closed.push({ key, suppressed }) - }) - - window.admit('key-0', 'host-a') - window.admit('key-0', 'host-b') - for (let index = 1; index < 200; index++) window.admit(`key-${index}`, 'host-a') - - expect(closed[0]).toEqual({ key: 'key-0', suppressed: 1 }) - // Evicted keys emit again rather than staying silently suppressed. - expect(window.admit('key-0', 'host-a')).toBe(true) - expect(window.admit('key-199', 'host-a')).toBe(false) - }) }) function manualTimers(): { diff --git a/cloud/apps/relay/src/assignment/assignment-rejection-logging.test.ts b/cloud/apps/relay/src/assignment/assignment-rejection-logging.test.ts index d87c9879726..763d1a28d3f 100644 --- a/cloud/apps/relay/src/assignment/assignment-rejection-logging.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-rejection-logging.test.ts @@ -15,11 +15,9 @@ vi.mock('../authentication/relay-token-verifier.js', () => ({ })) import { createRelayApp } from '../app.js' -import { relayHostLogDigest } from '../observability/relay-host-log-digest.js' describe('assignment rejection logging', () => { afterEach(() => { - vi.useRealTimers() vi.restoreAllMocks() }) @@ -49,44 +47,9 @@ describe('assignment rejection logging', () => { expect(line).toContain('lane=sticky') expect(line).toContain('hinted=true') expect(line).toContain('reason=relay_capacity_exhausted') - expect(line).toContain(`host=${relayHostLogDigest(host)}`) + expect(line).toContain('host=af342ad00ee4') expect(line).not.toContain(host) }) - - it('names the throttled host once per window and closes it with the suppressed count', async () => { - vi.useFakeTimers() - const host = 'mmmmmmmmmmmmmmmm' - const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) - const app = createRelayApp(config(), { - store: {} as never, - assignments: { - assign: vi.fn(async () => assignment('cell-r', host)), - resolve: vi.fn(async () => null) - } as never, - drain: vi.fn(), - ready: vi.fn(async () => true) - }) - - expect((await app.request('/v1/assign', assignmentRequest(host))).status).toBe(200) - expect((await app.request('/v1/assign', assignmentRequest(host))).status).toBe(503) - expect((await app.request('/v1/assign', assignmentRequest(host))).status).toBe(503) - - const throttled = (): string[] => - warn.mock.calls - .map((call) => String(call[0])) - .filter((entry) => entry.includes('reason=host-rate-limited')) - expect(throttled()).toHaveLength(1) - expect(throttled()[0]).toContain('route=assign') - expect(throttled()[0]).toContain('lane=placement') - expect(throttled()[0]).toContain(`host=${relayHostLogDigest(host)}`) - expect(throttled()[0]).not.toContain('suppressed=') - expect(throttled()[0]).not.toContain(host) - - await vi.advanceTimersByTimeAsync(10_000) - expect(throttled()).toHaveLength(2) - expect(throttled()[1]).toContain('suppressed=1') - expect(throttled()[1]).toContain(`host=${relayHostLogDigest(host)}`) - }) }) function assignmentRequest( diff --git a/cloud/apps/relay/src/assignment/assignment-reserve-mode-postgres.test.ts b/cloud/apps/relay/src/assignment/assignment-reserve-mode-postgres.test.ts index dcd1ecca97c..b02886d807d 100644 --- a/cloud/apps/relay/src/assignment/assignment-reserve-mode-postgres.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-reserve-mode-postgres.test.ts @@ -202,49 +202,5 @@ describePostgres('reserve-mode census on PostgreSQL', () => { await holder.close() } }, 20_000) - - // A stall must not double connection demand: concurrent assigns share one reserve-set read, - // and after it fails the last answer stands for the backoff without another query. - it('reads the reserve set once for concurrent assigns during a stall, and not again in the backoff', async () => { - const timed = await openRelayDatabase({ databaseUrl, dataDir: '/tmp', statementTimeoutMs: 300 }) - const holder = await openRelayDatabase({ databaseUrl, dataDir: '/tmp' }) - let release!: () => void - const released = new Promise((resolve) => (release = resolve)) - let held!: () => void - const holding = new Promise((resolve) => (held = resolve)) - const lock = holder.transaction(async (transaction) => { - await transaction.query(`LOCK TABLE relay_cell_admit_modes IN ACCESS EXCLUSIVE MODE`) - held() - await released - }) - try { - await holding - const store = new RelayAssignmentStore(timed, () => 500_000) - await store.reconcileCells([cellA, cellB], false) - await heartbeat(store, cellA) - await heartbeat(store, cellB) - const query = timed.query.bind(timed) - let reads = 0 - timed.query = async (sql, params) => { - if (sql.includes('FROM relay_cell_admit_modes m')) reads += 1 - return await query(sql, params) - } - const placed = await Promise.all( - Array.from({ length: 6 }, async (_, index) => - await store.assign({ userId: USER, relayHostId: `hoststall0000${String(index).padStart(3, '0')}` }) - ) - ) - expect(placed).toHaveLength(6) - expect(reads).toBe(1) - await store.assign({ userId: USER, relayHostId: 'hoststall0000100' }) - expect(await store.reserveModeCells()).toBeNull() - expect(reads).toBe(1) - } finally { - release() - await lock - await timed.close() - await holder.close() - } - }, 20_000) }) diff --git a/cloud/apps/relay/src/assignment/assignment-reserve-mode.test.ts b/cloud/apps/relay/src/assignment/assignment-reserve-mode.test.ts index 33f90609601..94455f83ba7 100644 --- a/cloud/apps/relay/src/assignment/assignment-reserve-mode.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-reserve-mode.test.ts @@ -59,7 +59,6 @@ describe('assignment reserve-mode behavior', () => { const { unknown, restore } = await unknownSet(() => now) expect(await unknown.reserveModeCells()).toBeNull() expect(await unknown.releaseExpiredActivityLeases()).toBe(0) - expect(await unknown.evacuateDeadCells()).toBe(0) await restore() await setReserve(store, []) expect(await store.releaseExpiredActivityLeases()).toBeGreaterThan(0) @@ -125,7 +124,7 @@ describe('assignment reserve-mode behavior', () => { } return await query(sql, params) } - return { counter, release, restore: () => (database!.query = query) } + return { release, restore: () => (database!.query = query) } } it('reads afresh after an admit-mode write instead of joining a read begun before it', async () => { @@ -155,12 +154,10 @@ describe('assignment reserve-mode behavior', () => { try { const before = store.reserveModeCells() await store.setCellAdmitMode('cell-a', 'reserve') - const reads = held.counter.reads held.release() await before // A write cleared the cache: the next caller reads now, not after a 5 s backoff. expect([...((await store.reserveModeCells()) ?? [])]).toEqual(['cell-a']) - expect(held.counter.reads).toBeGreaterThan(reads) } finally { held.release() held.restore() @@ -197,13 +194,8 @@ describe('assignment reserve-mode behavior', () => { it('never places a fresh host on a reserve-mode cell through the database', async () => { const store = await setup(() => 1_000, ['cell-a']) - for (let index = 0; index < 4; index += 1) { - const placed = await store.assign({ - userId: `user-${index}`, - relayHostId: `host${String(index).padStart(12, '0')}` - }) - expect(placed.cellId).toBe('cell-b') - } + const placed = await store.assign({ userId: 'user-0', relayHostId: 'host000000000000' }) + expect(placed.cellId).toBe('cell-b') }) it('refuses drains, evacuations and status reads that would treat a reserve-mode cell as empty', async () => { diff --git a/cloud/apps/relay/src/assignment/assignment-reserve-spill.test.ts b/cloud/apps/relay/src/assignment/assignment-reserve-spill.test.ts deleted file mode 100644 index d4e5c7fde3b..00000000000 --- a/cloud/apps/relay/src/assignment/assignment-reserve-spill.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { ASSIGNMENT_LIMITS } from '@orca-cloud/relay-contract/assignment-invariants' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { - RelayAssignmentStore, - type AssignOptions, - type RelayAssignment -} from './assignment-store.js' -import type { RelayCellConfig } from '../config.js' -import { - openInMemoryRelayDatabase, - type RelayDatabase, - type RelayLockOptions, - type RelayTransactionOptions, - type SqlRow -} from '../database/database.js' -import { ReservePaceError } from './reserve-placement/reserve-assignment.js' - -const CELLS: RelayCellConfig[] = [ - { id: 'us-c1', url: 'https://us-c1.relay.example.com', region: 'us-central1', capacityRequests: 100 }, - { id: 'asia-c1', url: 'https://asia-c1.relay.example.com', region: 'asia-east2', capacityRequests: 100 } -] -const IDENTITY = { userId: 'spill-user', relayHostId: 'spillhost0000001' } -const ON_US_RESERVE: RelayAssignment = { - ...IDENTITY, - cellId: 'us-reserve', - cellUrl: 'https://us-reserve.relay.example.com', - assignmentEpoch: 8, - leaseExpiresAt: 1_000_000, - region: 'us-central1' -} - -// Reports the target cell full when its reservation is taken: the race between pick and reserve. -class FullAtReserveDatabase implements RelayDatabase { - armed = false - - constructor(private readonly delegate: RelayDatabase) {} - - get dialect(): 'sqlite' | 'postgres' | undefined { - return this.delegate.dialect - } - - async query(sql: string, params?: unknown[]): Promise { - return await this.delegate.query(sql, params) - } - - async queryLocked(sql: string, params?: unknown[], options?: RelayLockOptions): Promise { - const rows = await this.delegate.queryLocked(sql, params, options) - if (!this.armed || sql.trim() !== 'SELECT * FROM relay_cells WHERE cell_id = ?') { - return rows - } - return rows.map((row) => ({ ...row, reserved_requests: row.capacity_requests })) - } - - async transaction(operation: (transaction: RelayDatabase) => Promise, options?: RelayTransactionOptions): Promise { - return await this.delegate.transaction(async (inner) => { - const wrapped = new FullAtReserveDatabase(inner) - wrapped.armed = this.armed - return await operation(wrapped) - }, options) - } - - async close(): Promise { - await this.delegate.close() - } -} - -describe('reserve spill before relay_capacity_exhausted', () => { - let database: FullAtReserveDatabase - let store: RelayAssignmentStore - let now: number - - beforeEach(async () => { - now = 100 - database = new FullAtReserveDatabase(await openInMemoryRelayDatabase()) - store = new RelayAssignmentStore(database, () => now) - await store.reconcileCells(CELLS) - }) - - afterEach(async () => { - vi.restoreAllMocks() - await database.close() - }) - - const closeAllDatabaseCells = async () => { - for (const cell of CELLS) { - await store.configureCell(cell, 'migration-only') - } - } - const assignAsia = async (step5: AssignOptions) => - await store.assign(IDENTITY, 'asia-east2', 'asia-east2', undefined, step5) - - it('books a reserve seat when no database cell has room anywhere', async () => { - await closeAllDatabaseCells() - const reserveSpill = vi.fn(async () => ON_US_RESERVE) - await expect(assignAsia({ reserveSpill })).resolves.toEqual({ ...ON_US_RESERVE, placed: true }) - expect(reserveSpill).toHaveBeenCalledWith({ skipOwnRegion: false, rowEpoch: 0 }) - expect(await database.query(`SELECT cell_id FROM relay_assignments`)).toEqual([]) - }) - - it('skips the own region after the in-region reserve fallback already tried it', async () => { - await closeAllDatabaseCells() - const regionReserve = vi.fn(async () => null) - const reserveSpill = vi.fn(async () => ON_US_RESERVE) - await expect(assignAsia({ regionReserve, reserveSpill })).resolves.toMatchObject({ cellId: 'us-reserve' }) - expect(regionReserve).toHaveBeenCalledOnce() - expect(reserveSpill).toHaveBeenCalledWith({ skipOwnRegion: true, rowEpoch: 0 }) - }) - - it('hands the spill the epoch of the host row it read', async () => { - const first = await assignAsia({}) - // Dormant: its next assign is a placement, which reads the row and finds no cell. - await store.changeActivity(IDENTITY, 'control', -1) - now += ASSIGNMENT_LIMITS.activityLeaseMs + 1 - await store.releaseExpiredActivityLeases() - await store.releaseExpiredActivity() - now += ASSIGNMENT_LIMITS.dormantTtlMs - await closeAllDatabaseCells() - const reserveSpill = vi.fn(async () => ON_US_RESERVE) - await assignAsia({ reserveSpill }) - expect(reserveSpill).toHaveBeenCalledWith({ skipOwnRegion: false, rowEpoch: first.assignmentEpoch }) - }) - - it('refuses with relay_capacity_exhausted when no reserve seat exists either', async () => { - await closeAllDatabaseCells() - await expect(assignAsia({ reserveSpill: async () => null })).rejects.toThrow('relay_capacity_exhausted') - }) - - it('answers a paced spill with its pace, not a refusal', async () => { - await closeAllDatabaseCells() - const reserveSpill = vi.fn(async () => { - throw new ReservePaceError(4) - }) - await expect(assignAsia({ reserveSpill })).rejects.toBeInstanceOf(ReservePaceError) - }) - - it('answers an in-region reserve pace before any spill', async () => { - await closeAllDatabaseCells() - const reserveSpill = vi.fn(async () => ON_US_RESERVE) - const regionReserve = async () => { - throw new ReservePaceError(2) - } - await expect(assignAsia({ regionReserve, reserveSpill })).rejects.toBeInstanceOf(ReservePaceError) - expect(reserveSpill).not.toHaveBeenCalled() - }) - - it('does not spill a reservation race: the host retries and re-picks', async () => { - database.armed = true - const reserveSpill = vi.fn(async () => ON_US_RESERVE) - await expect(assignAsia({ reserveSpill })).rejects.toThrow('relay_capacity_exhausted') - expect(reserveSpill).not.toHaveBeenCalled() - }) -}) diff --git a/cloud/apps/relay/src/assignment/assignment-roll-replaced-cell.test.ts b/cloud/apps/relay/src/assignment/assignment-roll-replaced-cell.test.ts index 0b5a00dd757..a30f59a07e7 100644 --- a/cloud/apps/relay/src/assignment/assignment-roll-replaced-cell.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-roll-replaced-cell.test.ts @@ -143,18 +143,12 @@ async function setup(cells: RelayCellConfig[] = [SOURCE, TARGET]) { // The old process stopped heartbeating at START_MS, so it is unheard from here. const OLD_UNHEARD_AT = START_MS + HEARTBEAT_TTL_MS + 1_000 -// Both routes that refused in production: a hinted assign (placement lane after its -// verification read finds the cell not live) and resolve, which then assigns. async function hintedAssign(store: RelayAssignmentStore) { return await store.assign(IDENTITY, 'us-central1', 'us-central1', undefined, { hostRequested: true }) } -async function resolveRoute(store: RelayAssignmentStore) { - return (await store.resolve(IDENTITY)) ?? (await store.assign(IDENTITY)) -} - async function expectRefused( attempt: Promise, cause: 'not_ready' | 'unheard' @@ -164,10 +158,7 @@ async function expectRefused( expect(error).toMatchObject({ unavailableCause: cause }) } -describe.each([ - ['hinted assign', hintedAssign], - ['resolve', resolveRoute] -] as const)('a host offline through a roll redials via %s', (_route, redial) => { +describe('a host offline through a roll redials via hinted assign', () => { describe('while the replacement process boots (not ready)', () => { it('is re-placed when that process started after the roll isolated the cell', async () => { const { store, first, heartbeat, at, isolateForRoll } = await setup() @@ -175,7 +166,7 @@ describe.each([ await at(20_000) await heartbeat(SOURCE, { incarnation: NEW_INCARNATION, startedAt: 15_000, ready: false }) - const moved = await redial(store) + const moved = await hintedAssign(store) expect(moved).toMatchObject({ cellId: TARGET.id }) expect(moved.assignmentEpoch).toBeGreaterThan(first.assignmentEpoch) }) @@ -186,7 +177,7 @@ describe.each([ await at(20_000) await heartbeat(SOURCE, { ready: false }) - await expectRefused(redial(store), 'not_ready') + await expectRefused(hintedAssign(store), 'not_ready') }) it('is refused while the new process reports holding connections', async () => { @@ -200,7 +191,7 @@ describe.each([ totalConnections: 1 }) - await expectRefused(redial(store), 'not_ready') + await expectRefused(hintedAssign(store), 'not_ready') }) it('is refused once the new process itself goes unheard', async () => { @@ -210,7 +201,7 @@ describe.each([ await heartbeat(SOURCE, { incarnation: NEW_INCARNATION, startedAt: 15_000, ready: false }) await at(20_000 + HEARTBEAT_TTL_MS + 1_000) - await expectRefused(redial(store), 'unheard') + await expectRefused(hintedAssign(store), 'unheard') }) it('is refused on an ordinary readiness dip with no roll stamp', async () => { @@ -218,7 +209,7 @@ describe.each([ await at(20_000) await heartbeat(SOURCE, { incarnation: NEW_INCARNATION, startedAt: 15_000, ready: false }) - await expectRefused(redial(store), 'not_ready') + await expectRefused(hintedAssign(store), 'not_ready') }) it('is refused on a migration-only cell parked without a roll stamp', async () => { @@ -227,7 +218,7 @@ describe.each([ await at(20_000) await heartbeat(SOURCE, { incarnation: NEW_INCARNATION, startedAt: 15_000, ready: false }) - await expectRefused(redial(store), 'not_ready') + await expectRefused(hintedAssign(store), 'not_ready') }) }) @@ -238,7 +229,7 @@ describe.each([ await isolateForRoll() await at(OLD_UNHEARD_AT) - await expectRefused(redial(store), 'unheard') + await expectRefused(hintedAssign(store), 'unheard') }) it('is refused when the roll stamp is stale, even with a new process', async () => { @@ -248,7 +239,7 @@ describe.each([ await at(stale) await heartbeat(SOURCE, { incarnation: NEW_INCARNATION, startedAt: stale - 1, ready: false }) - await expectRefused(redial(store), 'not_ready') + await expectRefused(hintedAssign(store), 'not_ready') }) }) diff --git a/cloud/apps/relay/src/assignment/assignment-row-lock-order-postgres.test.ts b/cloud/apps/relay/src/assignment/assignment-row-lock-order-postgres.test.ts deleted file mode 100644 index e05f3f2310c..00000000000 --- a/cloud/apps/relay/src/assignment/assignment-row-lock-order-postgres.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { afterAll, beforeAll, describe, expect, it } from 'vitest' -import { RelayAssignmentStore } from './assignment-store.js' -import { openRelayDatabase, type RelayDatabase } from '../database/database.js' - -const databaseUrl = process.env.ORCA_RELAY_TEST_POSTGRES_URL -const describePostgres = databaseUrl ? describe : describe.skip - -const cells = [ - { - id: 'row-lock-order-a', - url: 'https://row-lock-order-a.example.com', - capacityRequests: 200, - connectionHardCap: 600 as const, - connectionUnobservedBound: 50 - }, - { - id: 'row-lock-order-b', - url: 'https://row-lock-order-b.example.com', - capacityRequests: 200, - connectionHardCap: 600 as const, - connectionUnobservedBound: 50 - } -] -const userId = 'row-lock-order-user' -const hosts = ['rowlockhost00001', 'rowlockhost00002', 'rowlockhost00003', 'rowlockhost00004'].map( - (relayHostId) => ({ userId, relayHostId }) -) - -// pg_stat_database exposes cycles hidden by three 40P01 retries; eventual success alone misses deadlocks that exhaust retries under load. -describePostgres('PostgreSQL row lock order', () => { - const databases: RelayDatabase[] = [] - - beforeAll(async () => { - for (let index = 0; index < 4; index++) { - databases.push(await openRelayDatabase({ databaseUrl, dataDir: '' })) - } - }) - - async function removeTestRows(database: RelayDatabase): Promise { - await database.query( - `DELETE FROM relay_control_connection_reservations WHERE user_id = ?`, - [userId] - ) - for (const table of [ - 'relay_control_capabilities', - 'relay_assignment_activity_leases', - 'relay_post_drain_migration_pins', - 'relay_assignment_migration_incarnations', - 'relay_assignment_migrations', - 'relay_assignments' - ]) { - await database.query(`DELETE FROM ${table} WHERE user_id = ?`, [userId]) - } - for (const cell of cells) { - for (const table of [ - 'relay_cell_connection_snapshots', - 'relay_cell_connection_runtime', - 'relay_cell_connection_limits', - 'relay_cell_runtime', - 'relay_cells' - ]) { - await database.query(`DELETE FROM ${table} WHERE cell_id = ?`, [cell.id]) - } - } - } - - afterAll(async () => { - if (databases[0]) await removeTestRows(databases[0]) - for (const connection of databases) await connection.close() - }) - - async function deadlockCount(): Promise { - const rows = await databases[0]!.query( - `SELECT deadlocks FROM pg_stat_database WHERE datname = current_database()` - ) - return Number(rows[0]!.deadlocks) - } - - it('runs the cell accept and the placement retry concurrently without a cycle', async () => { - await removeTestRows(databases[0]!) - const stores = databases.map((database) => new RelayAssignmentStore(database, () => 100)) - await stores[0]!.reconcileCells(cells) - for (const cell of cells) { - await stores[0]!.recordCellHeartbeat({ - cellId: cell.id, - cellUrl: cell.url, - cellIncarnation: '11111111-1111-4111-8111-111111111111', - startedAt: 50, - ready: true, - observedRequests: 0, - totalConnections: 0, - inFlightConnections: 0, - reservedConnectionUnits: 0, - enforcedConnectionUnits: 0, - connectionInclusionWatermark: 1, - connectionHardCap: 600, - connectionUnobservedBound: 50 - }) - } - const placements = new Map() - for (const identity of hosts) { - const assignment = await stores[0]!.assign(identity) - placements.set(identity.relayHostId, assignment) - } - - const before = await deadlockCount() - // The accept path (host rows, then the cell row last) against the paths - // that must read the inventory first: placement and evacuation. - for (let round = 0; round < 12; round++) { - await Promise.allSettled( - hosts.flatMap((identity, index) => { - const placement = placements.get(identity.relayHostId)! - const store = stores[index % stores.length]! - const other = stores[(index + 1) % stores.length]! - return [ - store.activateControl(identity, { - cellId: placement.cellId, - assignmentEpoch: placement.assignmentEpoch, - generation: round + 2 - }), - other.assign(identity), - other.startEvacuation( - identity, - placement.cellId === cells[0]!.id ? cells[1]!.id : cells[0]!.id - ) - ] - }) - ) - } - const after = await deadlockCount() - - expect(after - before).toBe(0) - }, 120_000) -}) diff --git a/cloud/apps/relay/src/assignment/migrations/dead-cell-evacuation-precheck.test.ts b/cloud/apps/relay/src/assignment/migrations/dead-cell-evacuation-precheck.test.ts index 9d72905967d..7a186562d18 100644 --- a/cloud/apps/relay/src/assignment/migrations/dead-cell-evacuation-precheck.test.ts +++ b/cloud/apps/relay/src/assignment/migrations/dead-cell-evacuation-precheck.test.ts @@ -5,46 +5,16 @@ import type { RelayCellConfig } from '../../config.js' import { openInMemoryRelayDatabase, openRelayDatabase, - type RelayDatabase, - type SqlRow + type RelayDatabase } from '../../database/database.js' -// Both dialects must skip host scans with no actionable cells; stale existing-only rows previously scanned every assignment for nothing. const databaseUrl = process.env.ORCA_RELAY_TEST_POSTGRES_URL const schema = 'relay_dead_cell_precheck_test' -const HOST_QUERY = 'FROM relay_assignments assignment' const CELLS: RelayCellConfig[] = [ { id: 'cell-a', url: 'https://relay-a.example.com', capacityRequests: 10 }, { id: 'cell-b', url: 'https://relay-b.example.com', capacityRequests: 10 } ] -class RecordingDatabase implements RelayDatabase { - readonly statements: string[] = [] - - constructor(private readonly delegate: RelayDatabase) {} - - get dialect() { - return this.delegate.dialect - } - - async query(sql: string, params: unknown[] = []): Promise { - this.statements.push(sql) - return await this.delegate.query(sql, params) - } - - async queryLocked(...args: Parameters): Promise { - return await this.delegate.queryLocked(...args) - } - - async transaction(operation: (transaction: RelayDatabase) => Promise): Promise { - return await this.delegate.transaction(operation) - } - - async close(): Promise { - await this.delegate.close() - } -} - function scopedUrl(): string { const url = new URL(databaseUrl!) url.searchParams.set('options', `-c search_path=${schema}`) @@ -91,13 +61,12 @@ describe.each(dialects)('dead-cell evacuation pre-check (%s)', (_dialect, open) async function setup(now: () => number) { database = await open() - const recording = new RecordingDatabase(database) - const store = new RelayAssignmentStore(recording, now, { + const store = new RelayAssignmentStore(database, now, { requireLiveCells: true, heartbeatTtlMs: 45_000 }) await store.reconcileCells(CELLS) - return { store, recording } + return { store } } async function heartbeat(store: RelayAssignmentStore, cell: RelayCellConfig): Promise { @@ -111,9 +80,9 @@ describe.each(dialects)('dead-cell evacuation pre-check (%s)', (_dialect, open) }) } - it('skips the host query when the only dead cell is unfenced and existing-only', async () => { + it('keeps hosts on an unfenced dead existing-only cell', async () => { let now = 100 - const { store, recording } = await setup(() => now) + const { store } = await setup(() => now) for (const cell of CELLS) await heartbeat(store, cell) const identity = { userId: 'user-a', relayHostId: 'host000000000001' } await store.setCellEnabled('cell-b', false) @@ -122,10 +91,8 @@ describe.each(dialects)('dead-cell evacuation pre-check (%s)', (_dialect, open) await store.setCellEnabled('cell-a', false) now += 45_001 await heartbeat(store, CELLS[1]!) - recording.statements.length = 0 expect(await store.evacuateDeadCells()).toBe(0) - expect(recording.statements.some((sql) => sql.includes(HOST_QUERY))).toBe(false) expect( await database!.query(`SELECT cell_id FROM relay_assignments WHERE user_id = ?`, [ identity.userId @@ -135,7 +102,7 @@ describe.each(dialects)('dead-cell evacuation pre-check (%s)', (_dialect, open) it('still evacuates hosts from a dead uncapped cell that admits', async () => { let now = 100 - const { store, recording } = await setup(() => now) + const { store } = await setup(() => now) for (const cell of CELLS) await heartbeat(store, cell) const identity = { userId: 'user-a', relayHostId: 'host000000000001' } await store.setCellEnabled('cell-b', false) @@ -143,10 +110,8 @@ describe.each(dialects)('dead-cell evacuation pre-check (%s)', (_dialect, open) await store.setCellEnabled('cell-b', true) now += 45_001 await heartbeat(store, CELLS[1]!) - recording.statements.length = 0 expect(await store.evacuateDeadCells()).toBe(1) - expect(recording.statements.some((sql) => sql.includes(HOST_QUERY))).toBe(true) expect((await store.resolve(identity))?.cellId).toBe('cell-b') }) }) diff --git a/cloud/apps/relay/src/assignment/migrations/drain-migration-row-lookup.test.ts b/cloud/apps/relay/src/assignment/migrations/drain-migration-row-lookup.test.ts deleted file mode 100644 index 9f1831d4d92..00000000000 --- a/cloud/apps/relay/src/assignment/migrations/drain-migration-row-lookup.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { expect, it } from 'vitest' -import { createDrainMigrationRowLookup } from './drain-migration-row-lookup.js' -import type { SqlRow } from '../../database/database.js' - -function text(row: SqlRow, field: string): string { - const value = row[field] - if (typeof value !== 'string') { - throw new Error(`invalid_${field}`) - } - return value -} - -it('keeps first assignment matches, lease order, row identity, and separate identity components', () => { - const rows = [ - { user_id: 'a:b', relay_host_id: 'c', value: 1 }, - { user_id: 'a', relay_host_id: 'b:c', value: 2 }, - { user_id: 'a:b', relay_host_id: 'c', value: 3 }, - { user_id: '', relay_host_id: '', value: 4 } - ] - const lookup = createDrainMigrationRowLookup(rows, text) - for (const identity of [ - { userId: 'a:b', relayHostId: 'c' }, - { userId: 'a', relayHostId: 'b:c' }, - { userId: '', relayHostId: '' }, - { userId: 'missing', relayHostId: 'c' } - ]) { - const expected = rows.filter( - (row) => - row.user_id === identity.userId && row.relay_host_id === identity.relayHostId - ) - expect(lookup.first(identity)).toBe(expected[0]) - expect(lookup.all(identity)).toEqual(expected) - lookup.all(identity).forEach((row, index) => expect(row).toBe(expected[index])) - } -}) - -it('retains lazy validation and short circuiting when an inventory is malformed', () => { - const first = { user_id: 'user', relay_host_id: 'host' } - const identity = { userId: 'user', relayHostId: 'host' } - const lookup = createDrainMigrationRowLookup( - [first, { user_id: null, relay_host_id: 'bad' }], - text - ) - expect(lookup.first(identity)).toBe(first) - expect(() => lookup.all(identity)).toThrow('invalid_user_id') - const unrelated = createDrainMigrationRowLookup( - [first, { user_id: 'other', relay_host_id: null }], - text - ) - expect(unrelated.all(identity)).toEqual([first]) - expect(() => unrelated.first({ userId: 'other', relayHostId: 'host' })).toThrow( - 'invalid_relay_host_id' - ) -}) diff --git a/cloud/apps/relay/src/assignment/migrations/drain-migration-row-scaling.test.ts b/cloud/apps/relay/src/assignment/migrations/drain-migration-row-scaling.test.ts deleted file mode 100644 index 6eed78f80a1..00000000000 --- a/cloud/apps/relay/src/assignment/migrations/drain-migration-row-scaling.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { ASSIGNMENT_LIMITS } from '@orca-cloud/relay-contract/assignment-invariants' -import { afterEach, expect, it } from 'vitest' -import { RelayAssignmentStore } from '../assignment-store.js' -import { openInMemoryRelayDatabase, type RelayDatabase, type SqlRow } from '../../database/database.js' - -let database: RelayDatabase | undefined -afterEach(async () => await database?.close()) - -it.each([false, true])( - 'looks up a whole drain inventory with linear identity reads (expired: %s)', - async (expired) => { - database = await openInMemoryRelayDatabase() - let measuring = false - let identityReads = 0 - let indexedRows = 0 - const instrument = (delegate: RelayDatabase): RelayDatabase => ({ - dialect: delegate.dialect, - query: (sql, params) => delegate.query(sql, params), - queryLocked: async (sql, params, options) => { - const rows = await delegate.queryLocked(sql, params, options) - if ( - !measuring || - !sql.includes('WHERE EXISTS') || - !(sql.includes('SELECT assignment.*') || sql.includes('SELECT lease.*')) - ) { - return rows - } - indexedRows += rows.length - return rows.map( - (row): SqlRow => - new Proxy(row, { - get(target, key) { - if (key === 'user_id' || key === 'relay_host_id') { - identityReads++ - } - return Reflect.get(target, key) - } - }) - ) - }, - transaction: (operation, options) => - delegate.transaction((tx) => operation(instrument(tx)), options), - close: () => delegate.close() - }) - let now = 100 - const store = new RelayAssignmentStore(instrument(database), () => now, { - requireLiveCells: true - }) - const cells = ['a', 'b'].map((id) => ({ - id: `cell-${id}`, - url: `https://relay-${id}.example.com`, - capacityRequests: 500 - })) - await store.reconcileCells(cells) - const incarnation = '11111111-1111-4111-8111-111111111111' - for (const cell of cells) { - await store.recordCellHeartbeat({ - cellId: cell.id, - cellUrl: cell.url, - cellIncarnation: incarnation, - startedAt: 50, - ready: true, - observedRequests: 0 - }) - } - await store.setCellEnabled('cell-b', false) - const identities = Array.from({ length: 50 }, (_, index) => ({ - userId: `user-${index % 5}`, - relayHostId: `host${String(index).padStart(12, '0')}` - })) - for (const identity of identities) { - await store.assign(identity) - } - await store.setCellEnabled('cell-b', true) - await store.setCellEnabled('cell-a', false) - for (const identity of identities) { - const migration = await store.startEvacuation(identity, 'cell-b') - await store.markMigrationTargetRegistered(identity, { - cellId: 'cell-b', - assignmentEpoch: migration.assignmentEpoch - }) - } - const attempt = { - attemptId: '55555555-5555-4555-8555-555555555555', - cellId: 'cell-a', - cellIncarnation: incarnation, - traceValue: '66666666-6666-4666-8666-666666666666', - plannedGraceMs: 120_000 - } - await store.prepareCellDrainAttempt(attempt) - if (expired) { - now += ASSIGNMENT_LIMITS.migrationLeaseMs + 1 - await store.releaseExpiredActivityLeases() - for (const cell of cells) { - await store.recordCellHeartbeat({ - cellId: cell.id, - cellUrl: cell.url, - cellIncarnation: incarnation, - startedAt: 50, - ready: true, - observedRequests: 0 - }) - } - } - measuring = true - await expect(store.beginCellDrainSend(attempt)).resolves.toMatchObject({ - state: 'send-may-have-started', - shouldSend: true - }) - expect(indexedRows).toBeGreaterThanOrEqual(100) - expect(identityReads).toBeLessThanOrEqual(indexedRows * 2) - const migrations = await database.query( - 'SELECT expires_at FROM relay_assignment_migrations' - ) - expect(migrations).toHaveLength(50) - expect( - migrations.every( - (row) => row.expires_at === now + ASSIGNMENT_LIMITS.migrationLeaseMs - ) - ).toBe(true) - } -) diff --git a/cloud/apps/relay/src/assignment/migrations/drain-release-cell-row-contention-postgres.test.ts b/cloud/apps/relay/src/assignment/migrations/drain-release-cell-row-contention-postgres.test.ts deleted file mode 100644 index 0008023fcc7..00000000000 --- a/cloud/apps/relay/src/assignment/migrations/drain-release-cell-row-contention-postgres.test.ts +++ /dev/null @@ -1,282 +0,0 @@ -import { createHash } from 'node:crypto' -import { afterAll, beforeAll, describe, expect, it } from 'vitest' -import { RelayAssignmentStore } from '../assignment-store.js' -import { - encodeMembership, - type CellAdmissionMembership, - type CellAdmissionState -} from '../../cell/admission/cell-admission-selector.js' -import type { RelayCellConfig } from '../../config.js' -import { openRelayDatabase, type RelayDatabase } from '../../database/database.js' - -const databaseUrl = process.env.ORCA_RELAY_TEST_POSTGRES_URL -const describePostgres = databaseUrl ? describe : describe.skip -const DIRECTOR_INSTANCES = 5 -const DIRECTOR_POOL_MAX = 3 -const DIRECTOR_APPLICATION = 'drain-release-contention-director' - -const USER_PREFIX = 'drain-release-contention' -const NOW = 1_000_000 -const CAPPED = { - capacityRequests: 6_000, - connectionHardCap: 3_000, - connectionUnobservedBound: 60 -} as const -const SOURCE: RelayCellConfig = { - id: 'drain-release-contention-a-source', - url: 'https://drain-release-contention-source.example.test', - region: 'asia-east2', - ...CAPPED -} -const TARGETS: RelayCellConfig[] = ['b', 'c', 'd'].map((suffix) => ({ - id: `drain-release-contention-${suffix}-target`, - url: `https://drain-release-contention-${suffix}-target.example.test`, - region: 'asia-east2', - ...CAPPED -})) -// Next to the database. Taken only once every Asia neighbour is at its cap. -const US_CELL: RelayCellConfig = { - id: 'drain-release-contention-e-us', - url: 'https://drain-release-contention-us.example.test', - region: 'us-central1', - ...CAPPED -} -const CELLS = [SOURCE, ...TARGETS, US_CELL] - -type Identity = { userId: string; relayHostId: string } -type Instance = { database: RelayDatabase; store: RelayAssignmentStore } - -function hostIdentity(index: number): Identity { - return { - userId: `${USER_PREFIX}-${index}`, - // Relay host ids are fixed-width opaque ids. - relayHostId: `drainrel${String(index).padStart(8, '0')}` - } -} - -describePostgres('PostgreSQL drain releases against director placement', () => { - const storeOptions = { requireLiveCells: true, heartbeatTtlMs: 45_000 } - const directors: Instance[] = [] - let observer: RelayDatabase - - function admin(): Instance { - return directors[0]! - } - - async function applySelector( - states: Record, - rollIsolatedCells?: string[] - ): Promise { - const current = await admin().store.inspectCellAdmissionSelector() - // Built from relay_cells: the apply requires exact coverage of a shared fleet. - const fleet = await admin().database.query( - `SELECT cell_id FROM relay_cells ORDER BY cell_id ASC` - ) - const membership: CellAdmissionMembership = { - existingOnly: [], - migrationOnly: [], - general: [] - } - for (const row of fleet) { - const cellId = String(row['cell_id']) - const state = - states[cellId] ?? - (current.selector.membership.existingOnly.includes(cellId) - ? 'existing-only' - : current.selector.membership.migrationOnly.includes(cellId) - ? 'migration-only' - : 'general') - if (state === 'existing-only') membership.existingOnly.push(cellId) - else if (state === 'migration-only') membership.migrationOnly.push(cellId) - else membership.general.push(cellId) - } - await admin().store.applyCellAdmissionSelector({ - attemptId: `drain-release-${current.selector.generation}`, - expectedGeneration: current.selector.generation, - ...(current.selector.generation === 0 - ? { - expectedMembershipSha256: createHash('sha256') - .update(encodeMembership(current.selector.membership)) - .digest('hex') - } - : {}), - membership, - ...(rollIsolatedCells ? { rollIsolatedCells } : {}) - }) - } - - // Other Postgres files write admission through the generation-0 helpers, so - // the selector goes back to generation 0 exactly as found. - async function resetSelectorBoundary(): Promise { - await admin().database.query( - `UPDATE relay_admission_selectors SET generation = 0, attempt_id = NULL - WHERE selector_id = 'general'` - ) - await admin().database.query( - `DELETE FROM relay_admission_selector_intents WHERE attempt_id LIKE 'drain-release-%'` - ) - await admin().store.reconcileCells([], false) - } - - async function deleteHostRows(): Promise { - for (const table of [ - 'relay_control_capabilities', - 'relay_control_connection_reservations', - 'relay_assignment_activity_leases', - 'relay_assignment_migrations', - 'relay_assignment_region_preferences', - 'relay_assignments' - ]) { - await admin().database.query(`DELETE FROM ${table} WHERE user_id LIKE '${USER_PREFIX}-%'`) - } - } - - // `atCap` reports those cells at their connection cap: no headroom left. - async function heartbeatAll(atCap: RelayCellConfig[] = []): Promise { - for (const [index, config] of CELLS.entries()) { - await admin().store.recordCellHeartbeat({ - cellId: config.id, - cellUrl: config.url, - cellIncarnation: `2222222${index}-2222-4222-8222-222222222222`, - startedAt: NOW - 1_000, - ready: true, - observedRequests: 0, - region: config.region, - // The cell checks enforced = total + in-flight + reserved. - totalConnections: atCap.includes(config) ? CAPPED.connectionHardCap : 0, - inFlightConnections: 0, - reservedConnectionUnits: 0, - enforcedConnectionUnits: atCap.includes(config) ? CAPPED.connectionHardCap : 0, - connectionHardCap: CAPPED.connectionHardCap, - connectionUnobservedBound: CAPPED.connectionUnobservedBound - }) - } - } - - // Holds `cellIds`' relay_cells rows in an open transaction until the returned release runs. - async function holdCellRows(cellIds: string[]): Promise<() => Promise> { - let release!: () => void - const released = new Promise((resolve) => (release = resolve)) - let held!: () => void - const acquired = new Promise((resolve) => (held = resolve)) - const holder = observer.transaction(async (transaction) => { - await transaction.queryLocked( - `SELECT cell_id FROM relay_cells WHERE cell_id IN (${cellIds.map(() => '?').join(', ')}) - ORDER BY cell_id ASC`, - cellIds - ) - held() - await released - }) - await acquired - return async () => { - release() - await holder - } - } - - // Every host holds a live control lease on the source, then the source is - // isolated for a roll: the state an Asia drain starts from. - async function seed(hosts: Identity[]): Promise { - await deleteHostRows() - await admin().database.query( - `UPDATE relay_cells SET reserved_requests = 0 WHERE cell_id LIKE '${USER_PREFIX}-%'` - ) - // Restoring the source to general also clears the previous run's roll stamp. - await applySelector({ - [SOURCE.id]: 'general', - ...Object.fromEntries(TARGETS.map((target) => [target.id, 'migration-only' as const])) - }) - for (const identity of hosts) { - const grant = await admin().store.assign(identity, 'asia-east2', 'asia-east2') - expect(grant.cellId).toBe(SOURCE.id) - await admin().store.activateControl(identity, { - cellId: SOURCE.id, - assignmentEpoch: grant.assignmentEpoch, - generation: 1 - }) - } - await applySelector( - Object.fromEntries(TARGETS.map((target) => [target.id, 'general' as const])) - ) - await applySelector({ [SOURCE.id]: 'migration-only' }, [SOURCE.id]) - } - - beforeAll(async () => { - for (let index = 0; index < DIRECTOR_INSTANCES; index += 1) { - const database = await openRelayDatabase({ - databaseUrl, - dataDir: '', - poolMax: DIRECTOR_POOL_MAX, - applicationName: DIRECTOR_APPLICATION - }) - directors.push({ - database, - store: new RelayAssignmentStore(database, () => NOW, storeOptions) - }) - } - observer = await openRelayDatabase({ databaseUrl, dataDir: '', poolMax: 1 }) - await deleteHostRows() - await admin().store.reconcileCells(CELLS, false) - await resetSelectorBoundary() - await heartbeatAll() - }) - - afterAll(async () => { - if (directors.length > 0) { - await deleteHostRows() - // Cells before the selector rebuild, or its membership names missing rows. - for (const config of CELLS) { - for (const table of [ - 'relay_cell_connection_snapshots', - 'relay_cell_connection_runtime', - 'relay_cell_connection_limits', - 'relay_cell_runtime', - 'relay_cell_admission', - 'relay_cell_regions', - 'relay_cells' - ]) { - await admin().database.query(`DELETE FROM ${table} WHERE cell_id = ?`, [config.id]) - } - } - await resetSelectorBoundary() - } - for (const instance of directors) { - await instance.database.close() - } - await observer?.close() - }) - - it('places a reconnecting host off the isolated source while that source row is held', async () => { - await heartbeatAll() - const host = hostIdentity(9_000) - await seed([host]) - // A drain release holds the source row for its whole transaction; target-only placement never asks for it. - const sourceHeld = await holdCellRows([SOURCE.id]) - try { - const grant = await Promise.race([ - directors[1]!.store.assign(host, 'asia-east2', 'asia-east2'), - new Promise<'blocked'>((resolve) => setTimeout(() => resolve('blocked'), 3_000)) - ]) - expect(grant).not.toBe('blocked') - expect(TARGETS.map(({ id }) => id)).toContain(grant === 'blocked' ? null : grant.cellId) - } finally { - await sourceHeld() - } - // Control: the same hold on the target rows does block a placement, so the hold above was real. - const other = hostIdentity(9_001) - await seed([other]) - const targetsHeld = await holdCellRows(TARGETS.map(({ id }) => id)) - const placement = directors[2]!.store.assign(other, 'asia-east2', 'asia-east2') - try { - const raced = await Promise.race([ - placement.then(() => 'placed' as const), - new Promise<'blocked'>((resolve) => setTimeout(() => resolve('blocked'), 1_000)) - ]) - expect(raced).toBe('blocked') - } finally { - await targetsHeld() - } - expect((await placement).cellId).not.toBe(SOURCE.id) - }, 30_000) -}) diff --git a/cloud/apps/relay/src/assignment/migrations/migration-recovery-postgres.test.ts b/cloud/apps/relay/src/assignment/migrations/migration-recovery-postgres.test.ts index 5d6a099ddc4..5e0cc1988b5 100644 --- a/cloud/apps/relay/src/assignment/migrations/migration-recovery-postgres.test.ts +++ b/cloud/apps/relay/src/assignment/migrations/migration-recovery-postgres.test.ts @@ -256,7 +256,7 @@ describePostgres('PostgreSQL migration recovery', () => { expect( await database.query( `SELECT assignment.cell_id, assignment.assignment_epoch, - migration.completed_at, migration.aborted_at + (migration.completed_at IS NOT NULL) AS completed, migration.aborted_at FROM relay_assignments assignment JOIN relay_assignment_migrations migration ON migration.user_id = assignment.user_id @@ -269,7 +269,7 @@ describePostgres('PostgreSQL migration recovery', () => { { cell_id: input.cells.failed.id, assignment_epoch: '2', - completed_at: String(100 + 45_001 + ASSIGNMENT_LIMITS.migrationLeaseMs + 1), + completed: true, aborted_at: null } ]) @@ -309,7 +309,7 @@ describePostgres('PostgreSQL migration recovery', () => { expect( await database.query( `SELECT assignment.cell_id, assignment.assignment_epoch, - migration.completed_at, migration.aborted_at + migration.completed_at, (migration.aborted_at IS NOT NULL) AS aborted FROM relay_assignments assignment JOIN relay_assignment_migrations migration ON migration.user_id = assignment.user_id @@ -323,9 +323,7 @@ describePostgres('PostgreSQL migration recovery', () => { cell_id: input.cells.source.id, assignment_epoch: '3', completed_at: null, - aborted_at: String( - 100 + ASSIGNMENT_LIMITS.migrationLeaseMs + STRANDED_MIGRATION_ABANDON_MS + 1 - ) + aborted: true } ]) }) @@ -394,63 +392,6 @@ describePostgres('PostgreSQL migration recovery', () => { ]) }) - it('fails a competing aggregate supersession instead of reporting the wrong target', async () => { - const input = await fixture() - const alternate = { - id: `recovery-cell-${sequence}-alternate`, - url: `https://recovery-${sequence}-alternate.example.com`, - capacityRequests: 20 - } - await input.store.reconcileCells([...Object.values(input.cells), alternate], false) - await input.heartbeat(alternate) - await input.store.setCellEnabled(input.cells.failed.id, false) - input.advancePastHeartbeat() - await input.heartbeat(input.cells.source) - await input.heartbeat(input.cells.replacement) - await input.heartbeat(alternate) - await input.store.attestCellFence(input.cells.failed.id, input.cellIncarnation) - - const results = await Promise.allSettled([ - input.store.supersedeRegisteredCellEvacuations( - input.cells.source.id, - input.cells.failed.id, - input.cells.replacement.id, - 100 - ), - input.store.supersedeRegisteredCellEvacuations( - input.cells.source.id, - input.cells.failed.id, - alternate.id, - 100 - ) - ]) - const fulfilled = results.filter( - (result): result is PromiseFulfilledResult => result.status === 'fulfilled' - ) - const rejected = results.filter( - (result): result is PromiseRejectedResult => result.status === 'rejected' - ) - expect(fulfilled).toHaveLength(1) - expect(fulfilled[0]!.value).toBe(1) - expect(rejected).toHaveLength(1) - expect(rejected[0]!.reason).toMatchObject({ message: 'migration_already_superseded' }) - const successor = await database.query( - `SELECT assignment.cell_id, migration.target_cell_id - FROM relay_assignments assignment - JOIN relay_assignment_migrations migration - ON migration.user_id = assignment.user_id - AND migration.relay_host_id = assignment.relay_host_id - AND migration.assignment_epoch = assignment.assignment_epoch - WHERE assignment.user_id = ? AND migration.previous_epoch = ?`, - [input.identity.userId, input.migration.assignmentEpoch] - ) - expect(successor).toHaveLength(1) - expect(successor[0]!.cell_id).toBe(successor[0]!.target_cell_id) - expect([input.cells.replacement.id, alternate.id]).toContain( - successor[0]!.target_cell_id - ) - }) - it('rolls back every supersession change when replacement capacity is insufficient', async () => { const input = await fixture(1) await input.store.setCellEnabled(input.cells.failed.id, false) diff --git a/cloud/apps/relay/src/assignment/migrations/postgres-drain-send-locking.test.ts b/cloud/apps/relay/src/assignment/migrations/postgres-drain-send-locking.test.ts index 5b75ce6ecba..ec6745e2c07 100644 --- a/cloud/apps/relay/src/assignment/migrations/postgres-drain-send-locking.test.ts +++ b/cloud/apps/relay/src/assignment/migrations/postgres-drain-send-locking.test.ts @@ -184,7 +184,7 @@ describePostgres('PostgreSQL drain-send locking', () => { }) now = migration.expiresAt + 1 - expect(await store.releaseExpiredActivityLeases()).toBeGreaterThan(0) + await store.releaseExpiredActivityLeases() await store.recordCellHeartbeat({ cellId: source.id, cellUrl: source.url, @@ -207,7 +207,7 @@ describePostgres('PostgreSQL drain-send locking', () => { cellId: source.id, cellIncarnation: sourceIncarnation }) - ).resolves.toMatchObject({ state: 'send-may-have-started', shouldSend: true }) + ).resolves.toMatchObject({ shouldSend: true }) await expect( database.query( `SELECT activity_kind, cell_id FROM relay_assignment_activity_leases diff --git a/cloud/apps/relay/src/assignment/migrations/registered-migration-inventory-postgres.test.ts b/cloud/apps/relay/src/assignment/migrations/registered-migration-inventory-postgres.test.ts deleted file mode 100644 index 3a4e02db149..00000000000 --- a/cloud/apps/relay/src/assignment/migrations/registered-migration-inventory-postgres.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import pg from 'pg' -import { afterAll, beforeAll, describe, expect, it } from 'vitest' -import { openRelayDatabase, type RelayDatabase } from '../../database/database.js' -import { REGISTERED_MIGRATION_ABANDON_MS } from './registered-migration-abandonment.js' -import { readRegisteredMigrationInventory } from './registered-migration-inventory.js' - -const databaseUrl = process.env.ORCA_RELAY_TEST_POSTGRES_URL -const describePostgres = databaseUrl ? describe : describe.skip -const schema = 'relay_migration_inventory_test' - -describePostgres('PostgreSQL registered migration inventory', () => { - let database: RelayDatabase | undefined - - beforeAll(async () => { - const client = new pg.Client({ connectionString: databaseUrl }) - await client.connect() - try { - await client.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`) - await client.query(`CREATE SCHEMA ${schema}`) - } finally { - await client.end() - } - const url = new URL(databaseUrl!) - url.searchParams.set('options', `-c search_path=${schema}`) - database = await openRelayDatabase({ databaseUrl: url.toString(), dataDir: '' }) - }) - - afterAll(async () => { - await database?.close() - const client = new pg.Client({ connectionString: databaseUrl }) - await client.connect() - try { - await client.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`) - } finally { - await client.end() - } - }) - - it('counts a disabled-target migration even while its source is active', async () => { - const now = REGISTERED_MIGRATION_ABANDON_MS + 10_000 - await database!.query( - `INSERT INTO relay_cells - (cell_id, cell_url, enabled, capacity_requests, reserved_requests, - observed_requests, last_heartbeat_at, updated_at) - VALUES ('cell-a', 'https://a.example.test', 1, 4000, 1, 0, 0, 1), - ('cell-b', 'https://b.example.test', 0, 4000, 0, 0, 0, 1)` - ) - await database!.query( - `INSERT INTO relay_cell_admission (cell_id, admission_state, updated_at) - VALUES ('cell-a', 'general', 1), ('cell-b', 'existing-only', 1)` - ) - await database!.query( - `INSERT INTO relay_assignments - (user_id, relay_host_id, cell_id, assignment_epoch, lease_expires_at, - last_activity_at, reserved_controls, reserved_splices, reserved_invites, - pending_installs, pending_confirmations, migration_leases) - VALUES ('user-1', '111111111111', 'cell-b', 2, ?, ?, 0, 0, 0, 0, 0, 0)`, - [now, now] - ) - await database!.query( - `INSERT INTO relay_assignment_activity_leases - (user_id, relay_host_id, activity_id, activity_kind, cell_id, - request_units, expires_at, updated_at) - VALUES ('user-1', '111111111111', 'control:source:1', 'control', 'cell-a', 1, ?, ?)`, - [now, now] - ) - await database!.query( - `INSERT INTO relay_assignment_migrations - (user_id, relay_host_id, source_cell_id, target_cell_id, previous_epoch, - assignment_epoch, source_request_units, target_reserved_units, expires_at, - target_registered_at, created_at, updated_at) - VALUES ('user-1', '111111111111', 'cell-a', 'cell-b', 1, 2, 1, 2, 2, 2, 1, 2)` - ) - - expect(await readRegisteredMigrationInventory(database!, now)).toEqual({ - open: 1, - inactive: 1, - abandoned: 1, - pairs: [ - { - sourceCellId: 'cell-a', - targetCellId: 'cell-b', - open: 1, - inactive: 1, - abandoned: 1 - } - ] - }) - - await database!.query( - `INSERT INTO relay_cells - (cell_id, cell_url, enabled, capacity_requests, reserved_requests, - observed_requests, last_heartbeat_at, updated_at) - VALUES ('cell-c', 'https://c.example.test', 1, 4000, 0, 0, 0, 1)` - ) - await database!.query( - `INSERT INTO relay_cell_admission (cell_id, admission_state, updated_at) - VALUES ('cell-c', 'migration-only', 1)` - ) - await database!.query( - `INSERT INTO relay_assignments - (user_id, relay_host_id, cell_id, assignment_epoch, lease_expires_at, - last_activity_at, reserved_controls, reserved_splices, reserved_invites, - pending_installs, pending_confirmations, migration_leases) - VALUES ('user-2', '222222222222', 'cell-c', 2, ?, ?, 0, 0, 0, 0, 0, 0)`, - [now, now] - ) - await database!.query( - `INSERT INTO relay_assignment_migrations - (user_id, relay_host_id, source_cell_id, target_cell_id, previous_epoch, - assignment_epoch, source_request_units, target_reserved_units, expires_at, - target_registered_at, created_at, updated_at) - VALUES ('user-2', '222222222222', 'cell-a', 'cell-c', 1, 2, 1, 2, ?, NULL, 1, 2)`, - [now + 60_000] - ) - - expect(await readRegisteredMigrationInventory(database!, now)).toEqual({ - open: 2, - inactive: 1, - abandoned: 1, - pairs: [ - { - sourceCellId: 'cell-a', - targetCellId: 'cell-b', - open: 1, - inactive: 1, - abandoned: 1 - }, - { - sourceCellId: 'cell-a', - targetCellId: 'cell-c', - open: 1, - inactive: 0, - abandoned: 0 - } - ] - }) - }) -}) diff --git a/cloud/apps/relay/src/assignment/migrations/registered-migration-inventory.test.ts b/cloud/apps/relay/src/assignment/migrations/registered-migration-inventory.test.ts deleted file mode 100644 index 1eca0b1749d..00000000000 --- a/cloud/apps/relay/src/assignment/migrations/registered-migration-inventory.test.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { afterEach, describe, expect, it } from 'vitest' -import { openInMemoryRelayDatabase, type RelayDatabase } from '../../database/database.js' -import { REGISTERED_MIGRATION_ABANDON_MS } from './registered-migration-abandonment.js' -import { - formatRegisteredMigrationInventory, - readRegisteredMigrationInventory -} from './registered-migration-inventory.js' - -describe('registered migration inventory', () => { - const databases: RelayDatabase[] = [] - afterEach(async () => { - for (const database of databases.splice(0)) { - await database.close() - } - }) - it('does not restart abandonment when an old source migration lease is refreshed', async () => { - const database = await openInMemoryRelayDatabase() - databases.push(database) - const now = REGISTERED_MIGRATION_ABANDON_MS + 10_000 - await insertCell(database, 'cell-a', false, 'existing-only') - await insertCell(database, 'cell-b', true, 'migration-only') - await insertMigration(database, now - 1) - - const fresh = await readRegisteredMigrationInventory(database, now) - - expect(fresh).toEqual({ - open: 1, - inactive: 1, - abandoned: 1, - pairs: [ - { - sourceCellId: 'cell-a', - targetCellId: 'cell-b', - open: 1, - inactive: 1, - abandoned: 1 - } - ] - }) - expect(formatRegisteredMigrationInventory(fresh)).toEqual([ - '[orca-relay] migration inventory open=1 expiredRegisteredInactive=1 abandonedRegistered=1', - '[orca-relay] migration pair sourceCellId=cell-a targetCellId=cell-b open=1 expiredRegisteredInactive=1 abandoned=1' - ]) - await database.query(`UPDATE relay_assignment_migrations SET expires_at = ?, updated_at = ?`, [ - now + 1, - now - ]) - expect((await readRegisteredMigrationInventory(database, now + 1)).abandoned).toBe(1) - }) - - it('includes unregistered open migrations without logging a host identity', async () => { - const database = await openInMemoryRelayDatabase() - databases.push(database) - const now = 10_000 - await insertCell(database, 'cell-a', false, 'existing-only') - await insertCell(database, 'cell-b', true, 'migration-only') - await insertMigration(database, now + 60_000, null) - - const inventory = await readRegisteredMigrationInventory(database, now) - - expect(inventory).toEqual({ - open: 1, - inactive: 0, - abandoned: 0, - pairs: [ - { - sourceCellId: 'cell-a', - targetCellId: 'cell-b', - open: 1, - inactive: 0, - abandoned: 0 - } - ] - }) - expect(formatRegisteredMigrationInventory(inventory).join('\n')).not.toContain('111111111111') - }) -}) - -async function insertCell( - database: RelayDatabase, - cellId: string, - enabled: boolean, - admissionState: string -): Promise { - await database.query( - `INSERT INTO relay_cells - (cell_id, cell_url, enabled, capacity_requests, reserved_requests, - observed_requests, last_heartbeat_at, updated_at) - VALUES (?, ?, ?, 4000, 0, 0, 0, 1)`, - [cellId, `https://${cellId}.example.test`, enabled ? 1 : 0] - ) - await database.query( - `INSERT INTO relay_cell_admission (cell_id, admission_state, updated_at) - VALUES (?, ?, 1)`, - [cellId, admissionState] - ) -} - -async function insertMigration( - database: RelayDatabase, - expiresAt: number, - targetRegisteredAt: number | null = 2 -): Promise { - await database.query( - `INSERT INTO relay_assignments - (user_id, relay_host_id, cell_id, assignment_epoch, lease_expires_at, - last_activity_at, reserved_controls, reserved_splices, reserved_invites, - pending_installs, pending_confirmations, migration_leases) - VALUES ('user-1', '111111111111', 'cell-b', 2, ?, ?, 0, 0, 0, 0, 0, 0)`, - [expiresAt, expiresAt] - ) - await database.query( - `INSERT INTO relay_assignment_migrations - (user_id, relay_host_id, source_cell_id, target_cell_id, previous_epoch, - assignment_epoch, source_request_units, target_reserved_units, expires_at, - target_registered_at, created_at, updated_at) - VALUES ('user-1', '111111111111', 'cell-a', 'cell-b', 1, 2, 1, 2, ?, ?, 1, 2)`, - [expiresAt, targetRegisteredAt] - ) -} diff --git a/cloud/apps/relay/src/assignment/public-admission/drain-return-admission.test.ts b/cloud/apps/relay/src/assignment/public-admission/drain-return-admission.test.ts index 1d17966a922..011e5c99313 100644 --- a/cloud/apps/relay/src/assignment/public-admission/drain-return-admission.test.ts +++ b/cloud/apps/relay/src/assignment/public-admission/drain-return-admission.test.ts @@ -1,23 +1,16 @@ import { describe, expect, it } from 'vitest' import { - DRAIN_RETURN_MIN_RETRY_AFTER_SECONDS, RelayDrainReturnAdmission, type DrainReturnDeferral, type DrainReturnGrant } from './drain-return-admission.js' import { RelayPublicAssignmentAdmission } from './public-assignment-admission.js' -type Timer = { at: number; callback: () => void; cancelled: boolean } - function harness( overrides: { maxQueued?: number - maxRetryAfterSeconds?: number - onServiceMs?: (durationMs: number) => void } = {} ) { - let now = 0 - const timers: Timer[] = [] const placement = new RelayPublicAssignmentAdmission({ maxConcurrent: 2, maxQueued: 8, @@ -26,32 +19,18 @@ function harness( maxDrainReturnConcurrent: 1, maxDrainReturnQueued: overrides.maxQueued ?? 0, drainReturnWaitMs: 3_000, - drainReturnMinIntervalMs: DRAIN_RETURN_MIN_RETRY_AFTER_SECONDS * 1_000, - now: () => now, - schedule: (callback, delayMs) => { - const timer = { at: now + delayMs, callback, cancelled: false } - timers.push(timer) - return () => { - timer.cancelled = true - } - } + drainReturnMinIntervalMs: 2_000, + now: () => 0, + schedule: () => () => undefined }) const lane = new RelayDrainReturnAdmission(placement, { maxConcurrent: 1, - maxRetryAfterSeconds: overrides.maxRetryAfterSeconds ?? 300, - now: () => now, - onServiceMs: overrides.onServiceMs + maxRetryAfterSeconds: 300, + now: () => 0 }) return { lane, - placement, - advance: (ms: number) => { - now += ms - for (const timer of timers.filter((entry) => !entry.cancelled && entry.at <= now)) { - timer.cancelled = true - timer.callback() - } - } + placement } } @@ -69,28 +48,6 @@ const host = (index: number): string => `host${String(index).padStart(12, '0')}` describe('drain-return admission', () => { - it('shortens the pacing once the measured service time is short', async () => { - const { lane, advance } = harness() - for (let index = 0; index < 30; index++) { - const grant = admitted(await lane.acquire(host(index))) - advance(50) - grant.lease.release() - advance(2_000) - } - admitted(await lane.acquire(host(100))) - - const retries = [] - for (let index = 101; index <= 120; index++) { - retries.push(deferred(await lane.acquire(host(index))).retryAfterSeconds) - } - - // ~50 ms per re-placement: twenty deferrals span about one second, where the - // 860 ms starting estimate would have spread them over seventeen. - expect(Math.max(...retries)).toBe(3) - }) - - // The unclamped sample: the EWMA's floor and ceiling would hide the real tail. - it('answers a host’s own early retry with its interval, not a place behind the cohort', async () => { const { lane } = harness() admitted(await lane.acquire(host(0))).lease.release() @@ -99,7 +56,7 @@ describe('drain-return admission', () => { const repeat = deferred(await lane.acquire(host(0))) expect(repeat).toMatchObject({ reason: 'host-rate-limited', - retryAfterSeconds: DRAIN_RETURN_MIN_RETRY_AFTER_SECONDS + retryAfterSeconds: 2 }) // And it booked nothing: the next overflow still gets the first slot. @@ -107,17 +64,6 @@ describe('drain-return admission', () => { expect(deferred(await lane.acquire(host(2))).retryAfterSeconds).toBe(2) }) - it('keeps a host’s reserved slot when it comes back early', async () => { - const { lane, advance } = harness() - admitted(await lane.acquire(host(0))) - for (let index = 1; index <= 4; index++) await lane.acquire(host(index)) - expect(deferred(await lane.acquire(host(5))).retryAfterSeconds).toBe(6) - - advance(1_000) - // Early and still full: the same instant, not a new slot after everyone else. - expect(deferred(await lane.acquire(host(5))).retryAfterSeconds).toBe(5) - }) - it('lets a queued placement go before a queued drain return', async () => { const { lane, placement } = harness({ maxQueued: 1 }) const drain = admitted(await lane.acquire(host(0))) diff --git a/cloud/apps/relay/src/assignment/public-admission/public-assignment-admission.test.ts b/cloud/apps/relay/src/assignment/public-admission/public-assignment-admission.test.ts index 9eb5849854d..887ad63f082 100644 --- a/cloud/apps/relay/src/assignment/public-admission/public-assignment-admission.test.ts +++ b/cloud/apps/relay/src/assignment/public-admission/public-assignment-admission.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it } from 'vitest' import { RelayPublicAssignmentAdmission } from './public-assignment-admission.js' describe('public assignment admission', () => { @@ -7,7 +7,7 @@ describe('public assignment admission', () => { const admission = new RelayPublicAssignmentAdmission({ maxConcurrent: 1, minIntervalMs: 0, - now: vi.fn(() => 0) + now: () => 0 }) const lease = await admission.acquire('host-a') expect(lease).not.toBeNull() @@ -46,19 +46,12 @@ describe('public assignment admission', () => { }) it('gives a bounded reserved waiter the next public slot', async () => { - let expire!: () => void - let cancelled = false const admission = new RelayPublicAssignmentAdmission({ maxConcurrent: 2, maxReservedConcurrent: 1, reservedWaitMs: 1_000, minIntervalMs: 0, - schedule: (callback) => { - expire = callback - return () => { - cancelled = true - } - } + schedule: () => () => undefined }) const first = await admission.acquire('host-a') const second = await admission.acquire('host-b') @@ -70,12 +63,10 @@ describe('public assignment admission', () => { const reserved = await reservedPromise expect(reserved).not.toBeNull() - expect(cancelled).toBe(true) await expect(admission.acquire('host-d')).resolves.toBeNull() reserved?.release() await expect(admission.acquire('host-d')).resolves.not.toBeNull() second?.release() - expire() }) it('serializes same-host assignment and reserved recovery without losing priority', async () => { @@ -100,34 +91,4 @@ describe('public assignment admission', () => { await expect(admission.acquire('host-b')).resolves.not.toBeNull() }) - it('tells each caller its own rejection reason without crossing requests', async () => { - const admission = new RelayPublicAssignmentAdmission({ - maxConcurrent: 1, - maxQueued: 1, - waitMs: 1_000, - maxReservedConcurrent: 1, - reservedWaitMs: 1_000, - minIntervalMs: 5_000, - now: () => 0, - schedule: () => () => undefined - }) - const queued: string[] = [] - const displaced: string[] = [] - const throttled: string[] = [] - - const active = await admission.acquire('host-a') - // 'host-b' waits, then reserved recovery for the same host displaces it. - const ordinary = admission.acquire('host-b', (reason) => displaced.push(reason)) - const reserved = admission.acquireReserved('host-b', (reason) => queued.push(reason)) - await expect(ordinary).resolves.toBeNull() - active?.release() - const recovered = await reserved - recovered?.release() - await admission.acquire('host-a', (reason) => throttled.push(reason)) - - expect(displaced).toEqual(['superseded']) - expect(queued).toEqual([]) - expect(throttled).toEqual(['host-rate-limited']) - }) - }) diff --git a/cloud/apps/relay/src/assignment/public-admission/public-assignment-circuit-breaker.test.ts b/cloud/apps/relay/src/assignment/public-admission/public-assignment-circuit-breaker.test.ts index b780bc26bd9..5117e5bbe79 100644 --- a/cloud/apps/relay/src/assignment/public-admission/public-assignment-circuit-breaker.test.ts +++ b/cloud/apps/relay/src/assignment/public-admission/public-assignment-circuit-breaker.test.ts @@ -3,12 +3,10 @@ import { createRelayApp } from '../../app.js' import type { RelayConfig } from '../../config.js' describe('public assignment circuit breaker', () => { - it('rejects assign and resolve without invoking relay state', async () => { - const assign = vi.fn() - const resolveResume = vi.fn() + it('rejects public assignment routes while disabled', async () => { const app = createRelayApp(config(), { - store: { resolveResume } as never, - assignments: { assign } as never, + store: {} as never, + assignments: {} as never, drain: vi.fn(), ready: vi.fn(async () => true) }) @@ -19,10 +17,6 @@ describe('public assignment circuit breaker', () => { expect(response.headers.get('retry-after')).toBe('5') expect(await response.json()).toEqual({ error: 'assignments_temporarily_unavailable' }) } - expect(assign).not.toHaveBeenCalled() - expect(resolveResume).not.toHaveBeenCalled() - expect((await app.request('/health')).status).toBe(200) - expect((await app.request('/v1/admin/drain', { method: 'POST' })).status).toBe(401) }) }) diff --git a/cloud/apps/relay/src/assignment/public-admission/public-assignment-drain-return-lane.test.ts b/cloud/apps/relay/src/assignment/public-admission/public-assignment-drain-return-lane.test.ts index c56f9bac8a0..4814e8d4eba 100644 --- a/cloud/apps/relay/src/assignment/public-admission/public-assignment-drain-return-lane.test.ts +++ b/cloud/apps/relay/src/assignment/public-admission/public-assignment-drain-return-lane.test.ts @@ -1,6 +1,5 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { - RelayAssignmentRowBusyError, type RelayAssignment, type ResolvedRelayAssignment } from '../assignment-store.js' @@ -21,7 +20,6 @@ vi.mock('../../authentication/relay-token-verifier.js', () => ({ import { createRelayApp } from '../../app.js' afterEach(() => { - vi.useRealTimers() vi.restoreAllMocks() }) @@ -36,15 +34,11 @@ describe('drain-return lane', () => { const resolve = vi.fn(async ({ relayHostId }: { relayHostId: string }) => relayHostId === drained ? isolatedHome(relayHostId) : assignment('cell-o', relayHostId) ) - const outcomes: string[] = [] - const servedLanes: string[] = [] const app = createRelayApp(config(), { store: {} as never, assignments: { assign, resolve } as never, drain: vi.fn(), - ready: vi.fn(async () => true), - recordAssignmentAdmission: (outcome) => outcomes.push(outcome), - recordAdmissionServiceMs: (lane) => servedLanes.push(lane) + ready: vi.fn(async () => true) }) // The re-placement holds the drain lane; the sticky lane's only slot is free. @@ -56,125 +50,12 @@ describe('drain-return lane', () => { ) expect(reconnect.status).toBe(200) - expect(resolve).toHaveBeenCalledWith( - { userId: 'user-1', relayHostId: drained }, - { classifyHomeRollIsolation: true } - ) replacement.resolve(assignment('cell-new', drained)) expect((await moving).status).toBe(200) - expect(outcomes).toEqual(['drain-return', 'sticky']) - // The drained host held a sticky slot for its verification read too. - expect(servedLanes).toEqual(['sticky', 'sticky', 'drain-return']) - }) - - it('defers an overflowing drain return with a paced Retry-After and says why', async () => { - const holder = 'hhhhhhhhhhhhhhhh' - const replacement = deferred() - const assign = vi.fn(async () => await replacement.promise) - const resolve = vi.fn(async ({ relayHostId }: { relayHostId: string }) => - isolatedHome(relayHostId) - ) - const outcomes: string[] = [] - const reasons: string[] = [] - const retryAfters: number[] = [] - const causes: string[] = [] - const app = createRelayApp(config({ drainReturnQueueMax: 1, drainReturnWaitMs: 50 }), { - store: {} as never, - assignments: { assign, resolve } as never, - drain: vi.fn(), - ready: vi.fn(async () => true), - recordAssignmentAdmission: (outcome) => outcomes.push(outcome), - recordAssignmentRejectionReason: (lane, reason) => reasons.push(`${lane}:${reason}`), - recordDrainReturnRetryAfter: (seconds) => retryAfters.push(seconds), - recordAssignmentUnavailable: (cause) => causes.push(cause) - }) - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const holding = app.request('/v1/assign', assignmentRequest(holder, { reconnect: true })) - await vi.waitFor(() => expect(assign).toHaveBeenCalledTimes(1)) - const queued = app.request( - '/v1/assign', - assignmentRequest('qqqqqqqqqqqqqqqq', { reconnect: true }) - ) - const overflow = await app.request( - '/v1/assign', - assignmentRequest('ffffffffffffffff', { reconnect: true }) - ) - - expect(overflow.status).toBe(503) - expect(overflow.headers.get('retry-after')).toBe('2') - const timedOut = await queued - expect(timedOut.status).toBe(503) - expect(Number(timedOut.headers.get('retry-after'))).toBeGreaterThanOrEqual(2) - expect(reasons).toEqual(['drain-return:queue-full', 'drain-return:wait-timeout']) - expect(retryAfters).toEqual([2, Number(timedOut.headers.get('retry-after'))]) - expect(outcomes.filter((outcome) => outcome === 'drain-return-deferred')).toHaveLength(2) - expect(causes).toEqual(['drain-return-deferred', 'drain-return-deferred']) - expect(warn.mock.calls.flat().join('\n')).toMatch( - /lane=drain-return hinted=true reason=queue-full .*retryAfter=2/ - ) - replacement.resolve(assignment('cell-new', holder)) - expect((await holding).status).toBe(200) - }) - - it('answers a drained host’s row-busy redial with the short interval', async () => { - vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'Date'] }) - vi.spyOn(console, 'warn').mockImplementation(() => {}) - const holder = 'hhhhhhhhhhhhhhhh' - const host = 'bbbbbbbbbbbbbbbb' - const holding = deferred() - let busy = true - const assign = vi.fn(async ({ relayHostId }: { relayHostId: string }) => { - if (relayHostId === holder) return await holding.promise - if (relayHostId === host && busy) { - busy = false - throw new RelayAssignmentRowBusyError() - } - return assignment('cell-new', relayHostId) - }) - const resolve = vi.fn(async ({ relayHostId }: { relayHostId: string }) => - isolatedHome(relayHostId) - ) - const app = createRelayApp(config({ drainReturnQueueMax: 1 }), { - store: {} as never, - assignments: { assign, resolve } as never, - drain: vi.fn(), - ready: vi.fn(async () => true) - }) - const request = (relayHostId: string) => - Promise.resolve( - app.request('/v1/assign', assignmentRequest(relayHostId, { reconnect: true })) - ) - - const held = request(holder) - await vi.waitFor(() => expect(assign).toHaveBeenCalledTimes(1)) - const queued = request(host) - await vi.advanceTimersByTimeAsync(10) - // A cohort behind it books the next ten service slots. - for (let index = 0; index < 10; index++) { - const overflow = await request(hostId('f', index)) - expect(overflow.status).toBe(503) - } - await vi.advanceTimersByTimeAsync(2_490) - holding.resolve(assignment('cell-new', holder)) - expect((await held).status).toBe(200) - const refused = await queued - expect(refused.headers.get('retry-after')).toBe('1') - - await vi.advanceTimersByTimeAsync(1_000) - const redial = await request(host) - - // Its own lane interval, not a slot after the ten hosts that came later. - expect(redial.status).toBe(503) - expect(redial.headers.get('retry-after')).toBe('2') }) }) -function hostId(prefix: string, index: number): string { - return `${prefix}${String(index).padStart(15, '0')}` -} - function assignmentRequest(relayHostId: string, extra: { reconnect?: boolean } = {}): RequestInit { return { method: 'POST', @@ -209,7 +90,7 @@ function deferred() { return { promise, resolve } } -function config(overrides: Partial = {}): RelayConfig { +function config(): RelayConfig { return { port: 8080, publicUrl: 'https://relay.example.test', @@ -233,7 +114,6 @@ function config(overrides: Partial = {}): RelayConfig { publicResolveConcurrency: 1, publicResolveWaitMs: 5_000, publicAssignmentRetryAfterSeconds: 5, - dataDir: './data', - ...overrides + dataDir: './data' } } diff --git a/cloud/apps/relay/src/assignment/public-admission/public-assignment-overload.test.ts b/cloud/apps/relay/src/assignment/public-admission/public-assignment-overload.test.ts index 14455be8b0b..b4382f962cd 100644 --- a/cloud/apps/relay/src/assignment/public-admission/public-assignment-overload.test.ts +++ b/cloud/apps/relay/src/assignment/public-admission/public-assignment-overload.test.ts @@ -18,68 +18,10 @@ import { createRelayApp } from '../../app.js' describe('public assignment overload', () => { - it('fairly drains a bounded incident-scale burst without raising database concurrency', async () => { - const pending = new Map>>() - const admittedHosts: string[] = [] - let active = 0 - let highWater = 0 - const assign = vi.fn(async ({ relayHostId }: { relayHostId: string }) => { - admittedHosts.push(relayHostId) - active++ - highWater = Math.max(highWater, active) - const operation = deferred() - pending.set(relayHostId, operation) - try { - return await operation.promise - } finally { - active-- - pending.delete(relayHostId) - } - }) - const app = createRelayApp(config(), { - store: {} as never, - assignments: { assign } as never, - drain: vi.fn(), - ready: vi.fn(async () => true) - }) - const hosts = Array.from( - { length: 430 }, - (_, index) => `host-${String(index).padStart(11, '0')}` - ) - - const firstWave = hosts.map((host) => - app.request('/v1/assign', assignmentRequest(host)) - ) - await vi.waitFor(() => expect(assign).toHaveBeenCalledTimes(2)) - const retryWave = await Promise.all( - hosts.map((host) => app.request('/v1/assign', assignmentRequest(host))) - ) - - expect(retryWave.every((response) => response.status === 503)).toBe(true) - expect(assign).toHaveBeenCalledTimes(2) - while (assign.mock.calls.length < 130) { - const activeOperations = [...pending] - const expectedCalls = assign.mock.calls.length + activeOperations.length - for (const [host, operation] of activeOperations) { - operation.resolve(assignment(`cell-${host}`, host)) - } - await vi.waitFor(() => expect(assign).toHaveBeenCalledTimes(expectedCalls)) - } - for (const [host, operation] of pending) { - operation.resolve(assignment(`cell-${host}`, host)) - } - const firstResponses = await Promise.all(firstWave) - expect(firstResponses.filter((response) => response.status === 200)).toHaveLength(130) - expect(firstResponses.filter((response) => response.status === 503)).toHaveLength(300) - expect(admittedHosts).toEqual(hosts.slice(0, 130)) - expect(highWater).toBe(2) - }) - it('refuses an oversized chunked body before parsing it, sparing only the reserve routes', async () => { - const resolve = vi.fn() const app = createRelayApp(config(), { store: {} as never, - assignments: { resolve } as never, + assignments: {} as never, drain: vi.fn(), ready: vi.fn(async () => true) }) @@ -98,15 +40,11 @@ describe('public assignment overload', () => { const refused = await app.request('/v1/resolve', chunked(64 * 1024)) expect(refused.status).toBe(413) expect(await refused.json()).toEqual({ error: 'request_too_large' }) - expect(resolve).not.toHaveBeenCalled() // Reserve bodies may reach 16 KiB, so the 4 KiB app limit must not answer for them. An - // unavailable route refuses before its own limit, including a body past 16 KiB. + // unavailable route refuses before its own limit. const reserve = await app.request('/internal/reserve', chunked(6 * 1024)) expect(reserve.status).toBe(404) expect(await reserve.json()).toEqual({ error: 'reserve_unavailable' }) - const oversized = await app.request('/internal/reserve', chunked(20 * 1024)) - expect(oversized.status).toBe(404) - expect(await oversized.json()).toEqual({ error: 'reserve_unavailable' }) }) it('reserves one bounded resolve lane while assignment work is saturated', async () => { @@ -133,8 +71,6 @@ describe('public assignment overload', () => { await vi.waitFor(() => expect(assign).toHaveBeenCalledTimes(2)) const recovery = app.request('/v1/resolve', resolveRequest('cccccccccccccccc', 1)) - await Promise.resolve() - expect(resolveResume).not.toHaveBeenCalled() const excessResolve = await app.request( '/v1/resolve', resolveRequest('dddddddddddddddd', 2) @@ -143,8 +79,6 @@ describe('public assignment overload', () => { expect(excessResolve.status).toBe(503) expect(excessAssign.status).toBe(503) - expect(assign).toHaveBeenCalledTimes(2) - expect(resolveResume).not.toHaveBeenCalled() pendingAssignments .get('aaaaaaaaaaaaaaaa') @@ -153,7 +87,6 @@ describe('public assignment overload', () => { await vi.waitFor(() => expect(resolveResume).toHaveBeenCalledTimes(1)) pendingResolve.resolve({ userId: 'user-1', relayDeviceId: 'device-1' }) expect((await recovery).status).toBe(200) - expect(resolve).toHaveBeenCalledTimes(1) for (const [host, operation] of pendingAssignments) { operation.resolve(assignment(`cell-${host}`, host)) diff --git a/cloud/apps/relay/src/assignment/public-admission/public-assignment-reconnect-lane.test.ts b/cloud/apps/relay/src/assignment/public-admission/public-assignment-reconnect-lane.test.ts index 58245e0d8c2..06b85e55356 100644 --- a/cloud/apps/relay/src/assignment/public-admission/public-assignment-reconnect-lane.test.ts +++ b/cloud/apps/relay/src/assignment/public-admission/public-assignment-reconnect-lane.test.ts @@ -37,13 +37,11 @@ describe('public assignment reconnect lane', () => { const resolve = vi.fn(async ({ relayHostId }: { relayHostId: string }) => relayHostId === reconnecting ? assignment('cell-r', reconnecting) : null ) - const outcomes: string[] = [] const app = createRelayApp(config({ publicAssignmentQueueMax: 0 }), { store: {} as never, assignments: { assign, resolve } as never, drain: vi.fn(), - ready: vi.fn(async () => true), - recordAssignmentAdmission: (outcome) => outcomes.push(outcome) + ready: vi.fn(async () => true) }) // Saturate the placement lane with two in-flight newcomers, zero queue. @@ -59,18 +57,6 @@ describe('public assignment reconnect lane', () => { ) expect(reconnected.status).toBe(200) - expect(resolve).toHaveBeenCalledWith( - { userId: 'user-1', relayHostId: reconnecting }, - { classifyHomeRollIsolation: true } - ) - expect(assign).toHaveBeenCalledWith( - { userId: 'user-1', relayHostId: reconnecting }, - undefined, - undefined, - undefined, - { hostRequested: true } - ) - expect(outcomes).toContain('sticky') pending.get(newcomerA)?.resolve(assignment('cell-a', newcomerA)) pending.get(newcomerB)?.resolve(assignment('cell-b', newcomerB)) @@ -96,8 +82,6 @@ describe('public assignment reconnect lane', () => { expect(repeat.status).toBe(503) expect(repeat.headers.get('retry-after')).toBe('2') - expect(resolve).toHaveBeenCalledTimes(1) - expect(assign).toHaveBeenCalledTimes(1) }) it('rejects on the fast lane when the verification probe fails transiently', async () => { @@ -117,7 +101,6 @@ describe('public assignment reconnect lane', () => { expect(response.status).toBe(503) expect(response.headers.get('retry-after')).toBe('2') - expect(assign).not.toHaveBeenCalled() }) it('refuses a dial whose assignment row is busy at once, and frees the fast lane', async () => { @@ -143,7 +126,6 @@ describe('public assignment reconnect lane', () => { expect(refused.status).toBe(503) expect(refused.headers.get('retry-after')).toBe('1') expect(await refused.json()).toEqual({ error: 'assignment_row_busy' }) - expect(warn.mock.calls.flat().join('\n')).toContain('reason=relay_assignment_row_busy') // The single fast-lane slot went back with the refusal. expect( (await app.request('/v1/assign', assignmentRequest(next, { reconnect: true }))).status diff --git a/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-store.test.ts b/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-store.test.ts index c68cf7efc02..395bb453998 100644 --- a/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-store.test.ts +++ b/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-store.test.ts @@ -111,51 +111,15 @@ async function setup() { } describe('constrained idle regional assignment transaction', () => { - it.each(['missing', 'disabled', 'future'] as const)( - 'does only one read per tick with %s durable control and sees later enablement', - async (state) => { - const { store, database, safety } = await setup() - const control = (await database.query( - "SELECT * FROM relay_region_rehome_control WHERE control_id = 'global'" - ))[0]! - if (state === 'missing') { - await database.query('DELETE FROM relay_region_rehome_control') - } else { - await database.query( - "UPDATE relay_region_rehome_control SET enabled = ?, not_before = ? WHERE control_id = 'global'", - [state === 'disabled' ? 0 : 1, safety.observedAt + (state === 'future' ? 1 : 0)] - ) - } - const query = vi.spyOn(database, 'query') - const transaction = vi.spyOn(database, 'transaction') - for (let tick = 0; tick < 3; tick++) { - query.mockClear() - expect(await store.selectIdleRegionalRehomeCandidates(safety)).toEqual([]) - expect(query).toHaveBeenCalledTimes(1) - expect(query.mock.calls[0]![0]).toMatch(/^SELECT .*FROM relay_region_rehome_control/s) - expect(transaction).not.toHaveBeenCalled() - } - if (state === 'missing') { - const columns = Object.keys(control) - await database.query( - `INSERT INTO relay_region_rehome_control (${columns.join(',')}) VALUES (${columns.map(() => '?').join(',')})`, - Object.values(control) - ) - } else { - await database.query( - "UPDATE relay_region_rehome_control SET enabled = 1, not_before = ? WHERE control_id = 'global'", - [safety.observedAt] - ) - } - query.mockClear() - expect(await store.selectIdleRegionalRehomeCandidates(safety)).toHaveLength(1) - expect(query.mock.calls.length).toBeGreaterThan(1) - await database.query("UPDATE relay_region_rehome_control SET enabled = 0 WHERE control_id = 'global'") - query.mockClear() - expect(await store.selectIdleRegionalRehomeCandidates(safety)).toEqual([]) - expect(query).toHaveBeenCalledTimes(1) - } - ) + it('sees durable control changes between candidate polls', async () => { + const { store, database, safety } = await setup() + await database.query("UPDATE relay_region_rehome_control SET enabled = 0 WHERE control_id = 'global'") + expect(await store.selectIdleRegionalRehomeCandidates(safety)).toEqual([]) + await database.query("UPDATE relay_region_rehome_control SET enabled = 1 WHERE control_id = 'global'") + expect(await store.selectIdleRegionalRehomeCandidates(safety)).toHaveLength(1) + await database.query("UPDATE relay_region_rehome_control SET enabled = 0 WHERE control_id = 'global'") + expect(await store.selectIdleRegionalRehomeCandidates(safety)).toEqual([]) + }) it.each([10, 11])('reserves source activity plus assignment at target capacity %i', async (capacity) => { const { store, database, safety, request } = await setup() @@ -184,33 +148,6 @@ describe('constrained idle regional assignment transaction', () => { }) }) - it.each(['next_dispatch_at', 'paused_until'] as const)( - 'skips the candidate join while %s holds the durable dispatch budget closed', - async (column) => { - const { store, database, safety } = await setup() - const query = vi.spyOn(database, 'query') - // One assignment only: setup leaves both fields at 0, and naming the other - // one too would assign this column twice, which Postgres rejects. - await database.query( - `UPDATE relay_region_rehome_worker_state SET ${column} = ? WHERE worker_id = 'global'`, - [safety.observedAt + 1] - ) - for (let tick = 0; tick < 3; tick++) { - query.mockClear() - expect(await store.selectIdleRegionalRehomeCandidates(safety)).toEqual([]) - expect(query).toHaveBeenCalledTimes(2) - expect(query.mock.calls[1]![0]).toMatch(/FROM relay_region_rehome_worker_state/s) - } - await database.query( - `UPDATE relay_region_rehome_worker_state SET ${column} = ? WHERE worker_id = 'global'`, - [safety.observedAt] - ) - query.mockClear() - expect(await store.selectIdleRegionalRehomeCandidates(safety)).toHaveLength(1) - expect(query.mock.calls.length).toBeGreaterThan(2) - } - ) - // sourceRequestUnits reads a reserve cell's missing phone leases as zero units; selection must drop it. it.each(['source', 'target'])('drops every candidate while the %s cell is recorded as reserve', async (cellId) => { const { store, safety } = await setup() @@ -267,51 +204,6 @@ describe('constrained idle regional assignment transaction', () => { expect(first.some((c) => c.relayHostId === next[0]!.relayHostId)).toBe(false) }) - it('progresses past a full page of busy candidates without writing eligibility state', async () => { - const { store, database, safety } = await setup() - for (const table of [ - 'relay_assignments', - 'relay_assignment_activity_leases', - 'relay_control_capabilities', - 'relay_region_decisions' - ]) { - const template = ( - await database.query(`SELECT * FROM ${table} WHERE user_id = ? AND relay_host_id = ?`, [ - identity.userId, - identity.relayHostId - ]) - )[0]! - const columns = Object.keys(template) - for (let index = 0; index < 100; index++) { - const values = columns.map((column) => - column === 'user_id' || column === 'relay_host_id' ? '?' : column - ) - await database.query( - `INSERT INTO ${table} (${columns.join(', ')}) SELECT ${values.join(', ')} FROM ${table} - WHERE user_id = ? AND relay_host_id = ?`, - [ - `idle-store-test-${String(index).padStart(3, '0')}`, - `pagehost${String(index).padStart(8, '0')}`, - identity.userId, - identity.relayHostId - ] - ) - } - } - const first = await store.selectIdleRegionalRehomeCandidates(safety) - const next = await store.selectIdleRegionalRehomeCandidates(safety) - expect(first).toHaveLength(100) - expect(next).toHaveLength(1) - expect(next[0]!.relayHostId).toBe('pagehost00000099') - const restarted = new RelayAssignmentStore(database, () => safety.observedAt, { - regionalRehomeCohortPercent: 100 - }) - expect(await restarted.selectIdleRegionalRehomeCandidates(safety)).toEqual(first) - expect(await database.query('SELECT * FROM relay_region_rehome_attempts')).toEqual([]) - const decisions = await database.query('SELECT last_considered_at FROM relay_region_decisions') - expect(decisions.every((decision) => Number(decision.last_considered_at) === 0)).toBe(true) - }) - it.runIf(Boolean(process.env.ORCA_IDLE_REHOME_POSTGRES_URL))( 'rechecks generation when replacement wins after the initial authority lookup', async () => { diff --git a/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-window.test.ts b/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-window.test.ts index 403ccf16830..a53521e721d 100644 --- a/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-window.test.ts +++ b/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-window.test.ts @@ -151,52 +151,6 @@ describe('idle regional rehome candidate window', () => { expect(new Set(candidates.map((candidate) => candidate.attemptId)).size).toBe(3) }) - it('drops only the targets without room for the host plus its source activity', async () => { - const { store, database, safety } = await setup() - await seedHost(store, identity) - await database.query( - 'UPDATE relay_assignment_activity_leases SET request_units = 4 WHERE user_id = ?', - [identity.userId] - ) - // Five units needed: four source units plus the assignment the move reserves. - await database.query("UPDATE relay_cells SET capacity_requests = 4 WHERE cell_id = 'asia-idle'") - const short = await store.selectIdleRegionalRehomeCandidates(safety) - expect(short.map((candidate) => candidate.targetCellId)).toEqual(['asia-mid', 'asia-busy']) - // Exactly enough room is enough; it ranks last because the ratio is per capacity. - await database.query("UPDATE relay_cells SET capacity_requests = 5 WHERE cell_id = 'asia-idle'") - const exact = await store.selectIdleRegionalRehomeCandidates(safety) - expect(exact.map((candidate) => candidate.targetCellId)).toEqual([ - 'asia-mid', - 'asia-busy', - 'asia-idle' - ]) - }) - - it('reads a bounded window of decisions however many hosts are eligible', async () => { - const { store, database, safety } = await setup() - await seedHost(store, identity) - await cloneHosts(database, identity, 700) - const query = database.query.bind(database) - const decisionRows: number[] = [] - vi.spyOn(database, 'query').mockImplementation(async (sql, params) => { - const rows = await query(sql, params) - if (/^SELECT user_id, relay_host_id FROM relay_region_decisions/.test(sql)) { - decisionRows.push(rows.length) - } - return rows - }) - const first = await store.selectIdleRegionalRehomeCandidates(safety) - const next = await store.selectIdleRegionalRehomeCandidates(safety) - expect(decisionRows.length).toBeGreaterThan(0) - for (const count of decisionRows) { - expect(count).toBeLessThanOrEqual(500) - } - expect(first).toHaveLength(102) - expect(next).toHaveLength(102) - const firstKeys = new Set(first.map((c) => `${c.userId}/${c.targetCellId}`)) - expect(next.every((c) => !firstKeys.has(`${c.userId}/${c.targetCellId}`))).toBe(true) - }) - it("keeps the window's last host when a decision turns eligible between the two reads", async () => { const { store, database, safety, now } = await setup() await seedHost(store, identity) @@ -252,7 +206,6 @@ describe('idle regional rehome candidate window', () => { for (let poll = 0; poll < 20 && !wrapped; poll++) { const page = await store.selectIdleRegionalRehomeCandidates(safety) expect(page.length).toBeGreaterThan(0) - expect(page.length).toBeLessThanOrEqual(102) for (const candidate of page) { const key = `${candidate.userId}/${candidate.targetCellId}` if (seen.has(key)) { diff --git a/cloud/apps/relay/src/assignment/regional-rehome/region-correction-restart.test.ts b/cloud/apps/relay/src/assignment/regional-rehome/region-correction-restart.test.ts index b13e1b9d3a5..beb6a3c58d1 100644 --- a/cloud/apps/relay/src/assignment/regional-rehome/region-correction-restart.test.ts +++ b/cloud/apps/relay/src/assignment/regional-rehome/region-correction-restart.test.ts @@ -80,7 +80,7 @@ describe('persisted region decisions across director restart', () => { expect(row.outcome).toBe('inconclusive') expect(Number(row.expires_at)).toBe(context.window.expiresAt) const successor = (await context.issue(restarted.store)).window! - expect(successor.generation).toBe(context.window.generation + 1) + expect(successor.generation).toBe(2) expect(await restarted.store.exchangeRegionCorrection(identity, context.report, epoch)) .toMatchObject({ reportStatus: 'stale' }) }) @@ -111,7 +111,7 @@ describe('persisted region decisions across director restart', () => { context.setNow(1_000_000_000 - 60_000) const restarted = await context.restart() const successor = (await context.issue(restarted.store)).window! - expect(successor.generation).toBe(context.window.generation + 1) + expect(successor.generation).toBe(2) expect(successor.expiresAt).toBe(context.window.expiresAt - 60_000) expect(await restarted.store.exchangeRegionCorrection(identity, context.report, context.window.assignmentEpoch)) .toMatchObject({ reportStatus: 'stale' }) From 8dd3b710902e1db345cdf60e19a2c81b4fd25db9 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 13 Nov 2026 05:10:41 -0800 Subject: [PATCH 089/178] refactor: organize mobile files and host-screen domain owners (#42019) --- ...obile-localization-coverage-allowlist.json | 2 +- .../app/h/[hostId]/files/[worktreeId].web.tsx | 2 +- .../h/[hostId]/files/preview/[worktreeId].tsx | 2 +- .../files/preview/[worktreeId].web.tsx | 2 +- mobile/rpc-foundation/pilot-scenarios.json | 60 +++--- .../mobile-ai-vault-resume-operations.ts | 2 +- .../mobile-session-browser-operations.ts | 2 +- .../use-new-workspace-repositories.ts | 2 +- mobile/src/files/MobileFilePreviewScreen.tsx | 39 ++-- .../AttachmentTextScrollView.tsx | 6 +- .../MobileChatAttachmentPreview.tsx | 24 +-- .../mobile-chat-attachment-read.ts | 16 +- .../MobileFileExplorerPanel.test.ts | 18 +- .../MobileFileExplorerPanel.tsx | 40 ++-- .../file-explorer-reply-schema.ts | 2 +- .../{ => explorer}/file-list-fallback.ts | 0 .../files/{ => explorer}/file-tree.test.ts | 0 mobile/src/files/{ => explorer}/file-tree.ts | 4 +- .../mobile-file-explorer-operations.ts | 19 +- ...mobile-file-explorer-row-actions-sheet.tsx | 8 +- .../mobile-file-explorer-row.tsx | 14 +- .../mobile-file-explorer-rows.tsx | 0 .../mobile-file-explorer-styles.ts | 4 +- .../native-chat-file-search-operations.ts | 6 +- .../use-mobile-file-explorer-paging.ts | 8 +- .../workspace-file-paths-reply-schema.ts | 2 +- .../MobileFileMarkdownPreview.tsx | 12 +- .../MobileMarkdownPreviewImage.tsx | 18 +- .../MobileMarkdownPreviewModeBar.tsx | 10 +- .../mobile-markdown-file-link.test.ts | 2 +- .../mobile-markdown-file-link.ts | 6 +- .../mobile-markdown-image-source.test.ts | 24 --- .../mobile-markdown-image-source.ts | 12 +- .../{ => media}/MobileFileMediaPreview.tsx | 16 +- .../files/{ => media}/MobileMediaPlayback.tsx | 4 +- .../files/{ => media}/MobilePdfPreview.tsx | 16 +- .../{ => media}/mobile-file-media-download.ts | 8 +- .../files/{ => media}/mobile-file-media.ts | 2 +- .../{ => media}/mobile-media-preview-cache.ts | 0 .../file-exists-error.ts | 2 +- .../file-ownership-reply-schema.ts | 2 +- .../mobile-file-mutation-ownership.ts | 14 +- .../mobile-file-ownership-operations.ts | 6 +- .../{ => preview}/MobileFilePreviewBody.tsx | 20 +- .../MobileFilePreviewDiscardModal.tsx | 4 +- .../MobileFilePreviewEditableSource.tsx | 4 +- .../MobileFilePreviewSourceText.tsx | 9 +- .../file-preview-reply-schema.ts | 2 +- .../{ => preview}/mobile-artifact-kind.ts | 0 .../mobile-file-preview-line-column.ts | 0 .../mobile-file-preview-navigation.ts | 4 +- .../mobile-file-preview-operations.ts | 4 +- .../mobile-file-preview-request.test.ts | 4 +- .../mobile-file-preview-request.ts | 17 +- .../mobile-file-preview-response.ts | 12 +- .../mobile-file-preview-retry.test.tsx | 49 +---- .../mobile-file-preview-route.ts | 2 +- .../mobile-file-preview-source.ts | 0 .../mobile-file-preview-styles.ts | 4 +- .../mobile-file-preview-syntax.ts | 2 +- .../use-mobile-file-preview-back.test.ts | 17 -- .../use-mobile-file-preview-back.ts | 2 +- .../MobileSessionFileReader.tsx | 4 +- .../file-tab-doc-reply-schema.ts | 2 +- .../mobile-diff-image-preview.ts | 2 +- .../mobile-file-tab-doc-operations.ts | 7 +- .../mobile-file-tab-doc.ts | 8 +- .../session-documents/mobile-file-tap-open.ts | 6 +- .../mobile-file-tap-operations.ts | 2 +- .../mobile-markdown-reader-status.ts | 2 +- .../mobile-markdown-tab-operations.ts | 2 +- ...mobile-session-document-read-settlement.ts | 2 +- .../session-document-state.ts | 2 +- .../use-mobile-file-tap-handlers.ts | 2 +- .../use-mobile-session-document-readers.ts | 4 +- .../MobileFilePreviewEditDraft.tsx | 18 +- .../mobile-file-preview-editability.ts | 6 +- .../mobile-terminal-artifact-grant-refresh.ts | 6 +- .../terminal-artifact-grant-error.ts | 0 mobile/src/home/MobileHomeScreen.tsx | 2 +- mobile/src/home/home-query-restore.ts | 2 +- mobile/src/home/use-mobile-home-data.ts | 2 +- .../{ => catalog}/host-list-queries.ts | 24 +-- .../{ => catalog}/host-repo-looks.ts | 6 +- .../host-ssh-target-labels-read.ts | 15 +- .../use-host-project-sections.ts | 22 ++- .../use-host-repo-icon-data-urls.ts | 9 +- .../{ => catalog}/use-host-repo-metadata.ts | 16 +- .../use-host-worktree-catalog.ts | 46 ++--- .../src/host-screen/host-edit-navigation.ts | 35 ---- .../src/host-screen/host-screen-overlays.tsx | 9 +- .../host-screen-reply-schema.test.ts | 176 ------------------ mobile/src/host-screen/host-screen-view.tsx | 2 +- .../host-sidebar-feed-sections.test.ts | 111 ----------- .../host-sidebar-feed.test-fixture.ts | 139 -------------- .../identity/host-edit-navigation.ts | 22 +++ .../{ => identity}/host-stored-identity.ts | 4 +- .../use-host-screen-identity.ts | 22 +-- .../use-open-mobile-host-edit.ts | 2 +- .../host-sidebar-feed-connection.ts | 4 +- .../host-sidebar-feed-inputs.ts | 12 +- .../host-sidebar-feed-operations.ts | 14 +- .../host-sidebar-feed-sections.ts | 33 ++-- .../use-host-sidebar-feed.ts | 12 +- .../host-screen/use-host-screen-controller.ts | 21 ++- .../src/host-screen/use-host-screen-state.ts | 12 +- .../host-list-settings-operations.ts | 8 +- .../{ => view-settings}/host-view-memory.ts | 8 +- .../host-view-pending-changes.ts | 2 +- .../use-host-pending-view-changes.test.ts | 10 +- .../use-host-pending-view-changes.ts | 13 +- .../use-host-view-settings.ts | 20 +- .../use-host-worktree-actions.ts | 63 +++---- .../use-host-worktree-delete.ts | 24 +-- .../workspace-details-sheet.tsx | 24 +-- .../workspace-rename-mutation.ts | 22 +++ .../workspace-rename-sheet.tsx | 14 +- .../worktree-remove-request.ts | 6 +- .../host-pending-creation-row.tsx | 10 +- .../host-workspace-list.tsx | 50 ++--- .../use-host-display-worktrees.ts | 12 +- .../use-host-list-sections.ts | 45 ++--- .../use-slow-pending-keys.test.tsx | 0 .../use-slow-pending-keys.ts | 0 .../phone-card-property-choices.ts | 4 +- .../workspace-option-controls-copy.ts | 10 +- .../workspace-option-controls.tsx | 26 +-- .../workspace-option-rows.tsx | 6 +- .../workspace-option-values.ts | 0 .../workspace-options-sheet.tsx | 32 ++-- .../worktree-remove-request.test.ts | 27 --- .../bridge/bridge-native-verbs.ts | 2 +- .../shell-screen-route.test.ts | 70 ------- .../mobile-web-shell/shell-screen-route.ts | 2 +- .../src/new-workspace/use-host-label-by-id.ts | 2 +- .../new-workspace/use-host-worktree-rows.ts | 2 +- mobile/src/platform/native-pdf-view.ts | 4 +- mobile/src/query/first-paint-query-keys.ts | 6 +- .../src/query/host-query-persistence.test.ts | 51 +---- .../session/MobileSessionActiveContent.tsx | 2 +- .../src/session/MobileSourceControlButton.tsx | 2 +- .../src/session/SessionDockPanelContent.tsx | 2 +- .../mobile-structured-create-operations.ts | 2 +- .../MobileNativeChatComposerAttachments.tsx | 2 +- .../use-mobile-native-chat-file-search.ts | 2 +- .../mobile-native-chat-options-operations.ts | 2 +- .../mobile-native-chat-image-preview.ts | 2 +- ...e-mobile-session-content-create-actions.ts | 4 +- .../use-mobile-folder-attached-worktrees.ts | 2 +- mobile/src/stats/home-stats-summary-read.ts | 2 +- .../client-event-stream-mount-adapters.ts | 4 +- .../file-explorer-screen-mount-adapters.ts | 5 +- .../adapters/file-request-mount-adapters.ts | 19 +- .../adapters/host-screen-mount-adapters.ts | 7 +- .../host-worktree-action-mount-adapters.ts | 7 +- .../adapters/settings-mount-adapters.ts | 8 +- .../src/worktree/home-host-worktree-fetch.ts | 2 +- mobile/src/worktree/host-worktree-refresh.ts | 2 +- mobile/tsconfig.test.mobile-declarations.json | 2 +- tests/e2e/helpers/phone-feed-list.ts | 10 +- 160 files changed, 747 insertions(+), 1361 deletions(-) rename mobile/src/files/{ => attachments}/AttachmentTextScrollView.tsx (95%) rename mobile/src/files/{ => attachments}/MobileChatAttachmentPreview.tsx (87%) rename mobile/src/files/{ => attachments}/mobile-chat-attachment-read.ts (83%) rename mobile/src/files/{ => explorer}/MobileFileExplorerPanel.test.ts (95%) rename mobile/src/files/{ => explorer}/MobileFileExplorerPanel.tsx (90%) rename mobile/src/files/{ => explorer}/file-explorer-reply-schema.ts (92%) rename mobile/src/files/{ => explorer}/file-list-fallback.ts (100%) rename mobile/src/files/{ => explorer}/file-tree.test.ts (100%) rename mobile/src/files/{ => explorer}/file-tree.ts (97%) rename mobile/src/files/{ => explorer}/mobile-file-explorer-operations.ts (90%) rename mobile/src/files/{ => explorer}/mobile-file-explorer-row-actions-sheet.tsx (83%) rename mobile/src/files/{ => explorer}/mobile-file-explorer-row.tsx (95%) rename mobile/src/files/{ => explorer}/mobile-file-explorer-rows.tsx (100%) rename mobile/src/files/{ => explorer}/mobile-file-explorer-styles.ts (94%) rename mobile/src/files/{ => explorer}/native-chat-file-search-operations.ts (84%) rename mobile/src/files/{ => explorer}/use-mobile-file-explorer-paging.ts (93%) rename mobile/src/files/{ => explorer}/workspace-file-paths-reply-schema.ts (84%) rename mobile/src/files/{ => markdown}/MobileFileMarkdownPreview.tsx (79%) rename mobile/src/files/{ => markdown}/MobileMarkdownPreviewImage.tsx (82%) rename mobile/src/files/{ => markdown}/MobileMarkdownPreviewModeBar.tsx (85%) rename mobile/src/files/{ => markdown}/mobile-markdown-file-link.test.ts (96%) rename mobile/src/files/{ => markdown}/mobile-markdown-file-link.ts (86%) rename mobile/src/files/{ => markdown}/mobile-markdown-image-source.test.ts (55%) rename mobile/src/files/{ => markdown}/mobile-markdown-image-source.ts (79%) rename mobile/src/files/{ => media}/MobileFileMediaPreview.tsx (84%) rename mobile/src/files/{ => media}/MobileMediaPlayback.tsx (91%) rename mobile/src/files/{ => media}/MobilePdfPreview.tsx (82%) rename mobile/src/files/{ => media}/mobile-file-media-download.ts (93%) rename mobile/src/files/{ => media}/mobile-file-media.ts (90%) rename mobile/src/files/{ => media}/mobile-media-preview-cache.ts (100%) rename mobile/src/files/{ => mutation-ownership}/file-exists-error.ts (85%) rename mobile/src/files/{ => mutation-ownership}/file-ownership-reply-schema.ts (93%) rename mobile/src/files/{ => mutation-ownership}/mobile-file-mutation-ownership.ts (85%) rename mobile/src/files/{ => mutation-ownership}/mobile-file-ownership-operations.ts (83%) rename mobile/src/files/{ => preview}/MobileFilePreviewBody.tsx (88%) rename mobile/src/files/{ => preview}/MobileFilePreviewDiscardModal.tsx (86%) rename mobile/src/files/{ => preview}/MobileFilePreviewEditableSource.tsx (93%) rename mobile/src/files/{ => preview}/MobileFilePreviewSourceText.tsx (89%) rename mobile/src/files/{ => preview}/file-preview-reply-schema.ts (96%) rename mobile/src/files/{ => preview}/mobile-artifact-kind.ts (100%) rename mobile/src/files/{ => preview}/mobile-file-preview-line-column.ts (100%) rename mobile/src/files/{ => preview}/mobile-file-preview-navigation.ts (89%) rename mobile/src/files/{ => preview}/mobile-file-preview-operations.ts (95%) rename mobile/src/files/{ => preview}/mobile-file-preview-request.test.ts (95%) rename mobile/src/files/{ => preview}/mobile-file-preview-request.ts (94%) rename mobile/src/files/{ => preview}/mobile-file-preview-response.ts (94%) rename mobile/src/files/{ => preview}/mobile-file-preview-retry.test.tsx (72%) rename mobile/src/files/{ => preview}/mobile-file-preview-route.ts (98%) rename mobile/src/files/{ => preview}/mobile-file-preview-source.ts (100%) rename mobile/src/files/{ => preview}/mobile-file-preview-styles.ts (95%) rename mobile/src/files/{ => preview}/mobile-file-preview-syntax.ts (93%) rename mobile/src/files/{ => preview}/use-mobile-file-preview-back.test.ts (85%) rename mobile/src/files/{ => preview}/use-mobile-file-preview-back.ts (96%) rename mobile/src/files/{ => session-documents}/file-tab-doc-reply-schema.ts (96%) rename mobile/src/files/{ => session-documents}/mobile-diff-image-preview.ts (91%) rename mobile/src/files/{ => session-documents}/mobile-file-tab-doc-operations.ts (92%) rename mobile/src/files/{ => session-documents}/mobile-file-tab-doc.ts (91%) rename mobile/src/files/{ => terminal-artifacts}/MobileFilePreviewEditDraft.tsx (71%) rename mobile/src/files/{ => terminal-artifacts}/mobile-file-preview-editability.ts (89%) rename mobile/src/files/{ => terminal-artifacts}/mobile-terminal-artifact-grant-refresh.ts (93%) rename mobile/src/files/{ => terminal-artifacts}/terminal-artifact-grant-error.ts (100%) rename mobile/src/host-screen/{ => catalog}/host-list-queries.ts (93%) rename mobile/src/host-screen/{ => catalog}/host-repo-looks.ts (88%) rename mobile/src/host-screen/{ => catalog}/host-ssh-target-labels-read.ts (74%) rename mobile/src/host-screen/{ => catalog}/use-host-project-sections.ts (81%) rename mobile/src/host-screen/{ => catalog}/use-host-repo-icon-data-urls.ts (87%) rename mobile/src/host-screen/{ => catalog}/use-host-repo-metadata.ts (80%) rename mobile/src/host-screen/{ => catalog}/use-host-worktree-catalog.ts (86%) delete mode 100644 mobile/src/host-screen/host-edit-navigation.ts delete mode 100644 mobile/src/host-screen/host-screen-reply-schema.test.ts delete mode 100644 mobile/src/host-screen/host-sidebar-feed-sections.test.ts delete mode 100644 mobile/src/host-screen/host-sidebar-feed.test-fixture.ts create mode 100644 mobile/src/host-screen/identity/host-edit-navigation.ts rename mobile/src/host-screen/{ => identity}/host-stored-identity.ts (83%) rename mobile/src/host-screen/{ => identity}/use-host-screen-identity.ts (80%) rename mobile/src/host-screen/{ => identity}/use-open-mobile-host-edit.ts (81%) rename mobile/src/host-screen/{ => sidebar-feed}/host-sidebar-feed-connection.ts (82%) rename mobile/src/host-screen/{ => sidebar-feed}/host-sidebar-feed-inputs.ts (75%) rename mobile/src/host-screen/{ => sidebar-feed}/host-sidebar-feed-operations.ts (79%) rename mobile/src/host-screen/{ => sidebar-feed}/host-sidebar-feed-sections.ts (89%) rename mobile/src/host-screen/{ => sidebar-feed}/use-host-sidebar-feed.ts (87%) rename mobile/src/host-screen/{ => view-settings}/host-list-settings-operations.ts (80%) rename mobile/src/host-screen/{ => view-settings}/host-view-memory.ts (86%) rename mobile/src/host-screen/{ => view-settings}/host-view-pending-changes.ts (98%) rename mobile/src/host-screen/{ => view-settings}/use-host-pending-view-changes.test.ts (94%) rename mobile/src/host-screen/{ => view-settings}/use-host-pending-view-changes.ts (93%) rename mobile/src/host-screen/{ => view-settings}/use-host-view-settings.ts (93%) rename mobile/src/host-screen/{ => workspace-actions}/use-host-worktree-actions.ts (83%) rename mobile/src/host-screen/{ => workspace-actions}/use-host-worktree-delete.ts (91%) rename mobile/src/host-screen/{ => workspace-actions}/workspace-details-sheet.tsx (90%) create mode 100644 mobile/src/host-screen/workspace-actions/workspace-rename-mutation.ts rename mobile/src/host-screen/{ => workspace-actions}/workspace-rename-sheet.tsx (78%) rename mobile/src/host-screen/{ => workspace-actions}/worktree-remove-request.ts (78%) rename mobile/src/host-screen/{ => workspace-list}/host-pending-creation-row.tsx (80%) rename mobile/src/host-screen/{ => workspace-list}/host-workspace-list.tsx (88%) rename mobile/src/host-screen/{ => workspace-list}/use-host-display-worktrees.ts (82%) rename mobile/src/host-screen/{ => workspace-list}/use-host-list-sections.ts (78%) rename mobile/src/host-screen/{ => workspace-list}/use-slow-pending-keys.test.tsx (100%) rename mobile/src/host-screen/{ => workspace-list}/use-slow-pending-keys.ts (100%) rename mobile/src/host-screen/{ => workspace-options}/phone-card-property-choices.ts (75%) rename mobile/src/host-screen/{ => workspace-options}/workspace-option-controls-copy.ts (92%) rename mobile/src/host-screen/{ => workspace-options}/workspace-option-controls.tsx (91%) rename mobile/src/host-screen/{ => workspace-options}/workspace-option-rows.tsx (88%) rename mobile/src/host-screen/{ => workspace-options}/workspace-option-values.ts (100%) rename mobile/src/host-screen/{ => workspace-options}/workspace-options-sheet.tsx (90%) delete mode 100644 mobile/src/host-screen/worktree-remove-request.test.ts delete mode 100644 mobile/src/mobile-web-shell/shell-screen-route.test.ts diff --git a/config/mobile-localization-coverage-allowlist.json b/config/mobile-localization-coverage-allowlist.json index d84333750f2..d6e910f1e4f 100644 --- a/config/mobile-localization-coverage-allowlist.json +++ b/config/mobile-localization-coverage-allowlist.json @@ -175,7 +175,7 @@ "count": 1 }, { - "filePath": "mobile/src/files/mobile-file-preview-route.ts", + "filePath": "mobile/src/files/preview/mobile-file-preview-route.ts", "kind": "object-property:message", "text": "Unable to load preview", "dynamic": false, diff --git a/mobile/app/h/[hostId]/files/[worktreeId].web.tsx b/mobile/app/h/[hostId]/files/[worktreeId].web.tsx index 596fbad3260..f27f5804bfb 100644 --- a/mobile/app/h/[hostId]/files/[worktreeId].web.tsx +++ b/mobile/app/h/[hostId]/files/[worktreeId].web.tsx @@ -1,6 +1,6 @@ import { withWorkspaceRoute } from '../../../../src/navigation/workspace-route' import { useLocalSearchParams } from 'expo-router' -import { MobileFileExplorerPanel } from '../../../../src/files/MobileFileExplorerPanel' +import { MobileFileExplorerPanel } from '../../../../src/files/explorer/MobileFileExplorerPanel' import { firstParam } from '../../../../src/navigation/route-param-reader' // Render screen directly: native sibling imports a shell module that throws in browsers, and manifest imports every route. diff --git a/mobile/app/h/[hostId]/files/preview/[worktreeId].tsx b/mobile/app/h/[hostId]/files/preview/[worktreeId].tsx index a70935e4fa6..16a0891a4b6 100644 --- a/mobile/app/h/[hostId]/files/preview/[worktreeId].tsx +++ b/mobile/app/h/[hostId]/files/preview/[worktreeId].tsx @@ -7,7 +7,7 @@ import { useLocalSearchParams } from 'expo-router' import { mobileFilePreviewShellParams, normalizeMobileFilePreviewRouteParams -} from '../../../../../src/files/mobile-file-preview-route' +} from '../../../../../src/files/preview/mobile-file-preview-route' import { shellScreenRoute, shellScreenRouteKey diff --git a/mobile/app/h/[hostId]/files/preview/[worktreeId].web.tsx b/mobile/app/h/[hostId]/files/preview/[worktreeId].web.tsx index 4170e5f8a44..d903f7a3bff 100644 --- a/mobile/app/h/[hostId]/files/preview/[worktreeId].web.tsx +++ b/mobile/app/h/[hostId]/files/preview/[worktreeId].web.tsx @@ -1,7 +1,7 @@ import { withWorkspaceRoute } from '../../../../../src/navigation/workspace-route' import { useLocalSearchParams } from 'expo-router' import { MobileFilePreviewScreen } from '../../../../../src/files/MobileFilePreviewScreen' -import { normalizeMobileFilePreviewRouteParams } from '../../../../../src/files/mobile-file-preview-route' +import { normalizeMobileFilePreviewRouteParams } from '../../../../../src/files/preview/mobile-file-preview-route' // Render screen directly: native sibling imports a shell module that throws in browsers, and manifest imports every route. export default withWorkspaceRoute(function MobileFilePreviewRoute() { diff --git a/mobile/rpc-foundation/pilot-scenarios.json b/mobile/rpc-foundation/pilot-scenarios.json index 46f97142576..0f256d6a21f 100644 --- a/mobile/rpc-foundation/pilot-scenarios.json +++ b/mobile/rpc-foundation/pilot-scenarios.json @@ -930,7 +930,7 @@ "id": "settings-repo-metadata-fulfilled", "operation": "settings.repo-metadata", "family": "settings.repo-metadata", - "sites": ["mobile/src/host-screen/use-host-repo-metadata.ts"], + "sites": ["mobile/src/host-screen/catalog/use-host-repo-metadata.ts"], "schedules": ["settings-first"], "steps": [ { @@ -1023,7 +1023,7 @@ "id": "settings-repo-metadata-icons", "operation": "settings.repo-metadata", "family": "settings.repo-metadata", - "sites": ["mobile/src/host-screen/use-host-repo-metadata.ts"], + "sites": ["mobile/src/host-screen/catalog/use-host-repo-metadata.ts"], "schedules": ["settings-first"], "steps": [ { @@ -1134,7 +1134,7 @@ "id": "settings-repo-metadata-refused", "operation": "settings.repo-metadata", "family": "settings.repo-metadata", - "sites": ["mobile/src/host-screen/use-host-repo-metadata.ts"], + "sites": ["mobile/src/host-screen/catalog/use-host-repo-metadata.ts"], "schedules": ["settings-first"], "steps": [ { @@ -1222,7 +1222,7 @@ "id": "settings-repo-metadata-transport-error", "operation": "settings.repo-metadata", "family": "settings.repo-metadata", - "sites": ["mobile/src/host-screen/use-host-repo-metadata.ts"], + "sites": ["mobile/src/host-screen/catalog/use-host-repo-metadata.ts"], "schedules": ["settings-first"], "steps": [ { @@ -2101,7 +2101,7 @@ "id": "settings-repo-cache-expiry", "operation": "settings.repo-metadata", "family": "settings.repo-metadata", - "sites": ["mobile/src/host-screen/use-host-repo-metadata.ts"], + "sites": ["mobile/src/host-screen/catalog/use-host-repo-metadata.ts"], "schedules": ["follow-up-cache-observation", "timer-expiry"], "steps": [ { @@ -2424,7 +2424,7 @@ "id": "settings-repo-metadata-single-host", "operation": "settings.repo-metadata", "family": "settings.repo-metadata", - "sites": ["mobile/src/host-screen/use-host-repo-metadata.ts"], + "sites": ["mobile/src/host-screen/catalog/use-host-repo-metadata.ts"], "schedules": ["single-ssh-host"], "steps": [ { @@ -2855,7 +2855,7 @@ "id": "settings-repo-metadata-refuse-after-data", "operation": "settings.repo-metadata", "family": "settings.repo-metadata", - "sites": ["mobile/src/host-screen/use-host-repo-metadata.ts"], + "sites": ["mobile/src/host-screen/catalog/use-host-repo-metadata.ts"], "schedules": ["success-then-refused"], "steps": [ { @@ -6809,7 +6809,7 @@ "id": "files-ownership-ssh", "operation": "files.mutation-ownership", "family": "files.mutation-ownership", - "sites": ["mobile/src/files/mobile-file-mutation-ownership.ts"], + "sites": ["mobile/src/files/mutation-ownership/mobile-file-mutation-ownership.ts"], "schedules": [], "steps": [ { @@ -6872,7 +6872,7 @@ "id": "files-ownership-local", "operation": "files.mutation-ownership", "family": "files.mutation-ownership", - "sites": ["mobile/src/files/mobile-file-mutation-ownership.ts"], + "sites": ["mobile/src/files/mutation-ownership/mobile-file-mutation-ownership.ts"], "schedules": [], "steps": [ { @@ -6915,8 +6915,8 @@ "operation": "files.preview-load", "family": "files.preview-load", "sites": [ - "mobile/src/files/mobile-file-preview-request.ts", - "mobile/src/files/mobile-terminal-artifact-grant-refresh.ts" + "mobile/src/files/preview/mobile-file-preview-request.ts", + "mobile/src/files/terminal-artifacts/mobile-terminal-artifact-grant-refresh.ts" ], "schedules": [], "steps": [ @@ -6989,8 +6989,8 @@ "operation": "files.preview-load", "family": "files.preview-load", "sites": [ - "mobile/src/files/mobile-file-preview-request.ts", - "mobile/src/files/mobile-terminal-artifact-grant-refresh.ts" + "mobile/src/files/preview/mobile-file-preview-request.ts", + "mobile/src/files/terminal-artifacts/mobile-terminal-artifact-grant-refresh.ts" ], "schedules": [], "steps": [ @@ -7023,7 +7023,7 @@ "id": "files-preview-worktree", "operation": "files.preview-load", "family": "files.preview-load", - "sites": ["mobile/src/files/mobile-file-preview-request.ts"], + "sites": ["mobile/src/files/preview/mobile-file-preview-request.ts"], "schedules": [], "steps": [ { @@ -7054,7 +7054,7 @@ "id": "files-preview-worktree-image", "operation": "files.preview-load", "family": "files.preview-load", - "sites": ["mobile/src/files/mobile-file-preview-request.ts"], + "sites": ["mobile/src/files/preview/mobile-file-preview-request.ts"], "schedules": [], "steps": [ { @@ -7089,7 +7089,7 @@ "id": "files-preview-artifact-image", "operation": "files.preview-load", "family": "files.preview-load", - "sites": ["mobile/src/files/mobile-file-preview-request.ts"], + "sites": ["mobile/src/files/preview/mobile-file-preview-request.ts"], "schedules": [], "steps": [ { @@ -7126,8 +7126,8 @@ "operation": "files.preview-save", "family": "files.preview-save", "sites": [ - "mobile/src/files/mobile-file-preview-request.ts", - "mobile/src/files/mobile-terminal-artifact-grant-refresh.ts" + "mobile/src/files/preview/mobile-file-preview-request.ts", + "mobile/src/files/terminal-artifacts/mobile-terminal-artifact-grant-refresh.ts" ], "schedules": [], "steps": [ @@ -7179,8 +7179,8 @@ "operation": "files.preview-save", "family": "files.preview-save", "sites": [ - "mobile/src/files/mobile-file-preview-request.ts", - "mobile/src/files/mobile-terminal-artifact-grant-refresh.ts" + "mobile/src/files/preview/mobile-file-preview-request.ts", + "mobile/src/files/terminal-artifacts/mobile-terminal-artifact-grant-refresh.ts" ], "schedules": [], "steps": [ @@ -7212,7 +7212,7 @@ "id": "files-tab-doc-shapes", "operation": "files.tab-doc", "family": "files.tab-doc", - "sites": ["mobile/src/files/mobile-file-tab-doc.ts"], + "sites": ["mobile/src/files/session-documents/mobile-file-tab-doc.ts"], "schedules": [], "steps": [ { @@ -7416,7 +7416,7 @@ "id": "host-view-settings-sync", "operation": "host.view-settings", "family": "host.view-settings", - "sites": ["mobile/src/host-screen/use-host-view-settings.ts"], + "sites": ["mobile/src/host-screen/view-settings/use-host-view-settings.ts"], "schedules": [], "steps": [ { @@ -7640,7 +7640,7 @@ "id": "host-worktree-actions-pin-open-delete", "operation": "host.worktree-actions", "family": "host.worktree-actions", - "sites": ["mobile/src/host-screen/use-host-worktree-actions.ts"], + "sites": ["mobile/src/host-screen/workspace-actions/use-host-worktree-actions.ts"], "schedules": [], "steps": [ { @@ -7731,7 +7731,7 @@ "id": "host-worktree-delete-refused", "operation": "host.worktree-actions", "family": "host.worktree-actions", - "sites": ["mobile/src/host-screen/use-host-worktree-actions.ts"], + "sites": ["mobile/src/host-screen/workspace-actions/use-host-worktree-actions.ts"], "schedules": [], "steps": [ { @@ -7786,7 +7786,7 @@ "id": "host-worktree-delete-archive-hook-failed", "operation": "host.worktree-actions", "family": "host.worktree-actions", - "sites": ["mobile/src/host-screen/use-host-worktree-actions.ts"], + "sites": ["mobile/src/host-screen/workspace-actions/use-host-worktree-actions.ts"], "schedules": [], "steps": [ { @@ -20218,7 +20218,7 @@ "id": "files-explorer-legacy-fallback", "operation": "files.explorer-screen", "family": "files.explorer-screen", - "sites": ["mobile/src/files/MobileFileExplorerPanel.tsx"], + "sites": ["mobile/src/files/explorer/MobileFileExplorerPanel.tsx"], "schedules": [], "steps": [ { @@ -20277,7 +20277,7 @@ "id": "files-explorer-readdir", "operation": "files.explorer-screen", "family": "files.explorer-screen", - "sites": ["mobile/src/files/MobileFileExplorerPanel.tsx"], + "sites": ["mobile/src/files/explorer/MobileFileExplorerPanel.tsx"], "schedules": [], "steps": [ { @@ -23762,7 +23762,7 @@ "id": "files-preview-worktree-text-read", "operation": "files.preview-load", "family": "files.preview-worktree-text", - "sites": ["mobile/src/files/mobile-file-preview-request.ts"], + "sites": ["mobile/src/files/preview/mobile-file-preview-request.ts"], "schedules": [], "steps": [ { @@ -23793,7 +23793,7 @@ "id": "files-preview-worktree-image-read", "operation": "files.preview-load", "family": "files.preview-worktree-image", - "sites": ["mobile/src/files/mobile-file-preview-request.ts"], + "sites": ["mobile/src/files/preview/mobile-file-preview-request.ts"], "schedules": [], "steps": [ { @@ -23828,7 +23828,7 @@ "id": "files-preview-artifact-image-read", "operation": "files.preview-load", "family": "files.preview-artifact-image", - "sites": ["mobile/src/files/mobile-file-preview-request.ts"], + "sites": ["mobile/src/files/preview/mobile-file-preview-request.ts"], "schedules": [], "steps": [ { diff --git a/mobile/src/agent-history/mobile-ai-vault-resume-operations.ts b/mobile/src/agent-history/mobile-ai-vault-resume-operations.ts index 48e9c2ad335..1a0ece8eead 100644 --- a/mobile/src/agent-history/mobile-ai-vault-resume-operations.ts +++ b/mobile/src/agent-history/mobile-ai-vault-resume-operations.ts @@ -1,6 +1,6 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' import { rpcResultVariant } from '../transport/rpc/rpc-operation-result-reader' -import '../files/mobile-file-preview-operations' +import '../files/preview/mobile-file-preview-operations' import { aiVaultResumePreparationSchema } from './mobile-ai-vault-resume-reply-schema' import '../browser/tab-navigation/mobile-session-browser-reply-schema' import '../files/session-documents/mobile-file-tap-reply-schema' diff --git a/mobile/src/browser/tab-navigation/mobile-session-browser-operations.ts b/mobile/src/browser/tab-navigation/mobile-session-browser-operations.ts index 561f607ff2b..956dc42f809 100644 --- a/mobile/src/browser/tab-navigation/mobile-session-browser-operations.ts +++ b/mobile/src/browser/tab-navigation/mobile-session-browser-operations.ts @@ -1,6 +1,6 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' import { rpcResultVariant } from '../../transport/rpc/rpc-operation-result-reader' -import '../../files/mobile-file-preview-operations' +import '../../files/preview/mobile-file-preview-operations' import { browserTabCreatedSchema } from './mobile-session-browser-reply-schema' import '../../agent-history/mobile-ai-vault-resume-reply-schema' import '../../files/session-documents/mobile-file-tap-reply-schema' diff --git a/mobile/src/components/use-new-workspace-repositories.ts b/mobile/src/components/use-new-workspace-repositories.ts index 15a0c14e57e..b34fa4d8cfa 100644 --- a/mobile/src/components/use-new-workspace-repositories.ts +++ b/mobile/src/components/use-new-workspace-repositories.ts @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react' import { useQueryClient } from '@tanstack/react-query' import { getRepoExecutionHostId, type ExecutionHostId } from '../../../src/shared/execution-host' import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import { cachedHostRepos } from '../host-screen/host-list-queries' +import { cachedHostRepos } from '../host-screen/catalog/host-list-queries' import { listHostRepos } from '../worktree/server-repos' import { useServerRepos } from '../worktree/use-server-repos' import { useLastVisitedWorktreeRepoId } from '../worktree/use-last-visited-worktree-repo' diff --git a/mobile/src/files/MobileFilePreviewScreen.tsx b/mobile/src/files/MobileFilePreviewScreen.tsx index 37dab60d168..dbf347ede61 100644 --- a/mobile/src/files/MobileFilePreviewScreen.tsx +++ b/mobile/src/files/MobileFilePreviewScreen.tsx @@ -1,5 +1,9 @@ -import { saveErrorMessageFromPreviewResult } from './mobile-file-preview-response' -import { MobileChatAttachmentPreview } from './MobileChatAttachmentPreview' +import { + saveErrorMessageFromPreviewResult, + previewError, + type MobileFilePreviewResult +} from './preview/mobile-file-preview-response' +import { MobileChatAttachmentPreview } from './attachments/MobileChatAttachmentPreview' import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' import { Alert, View } from 'react-native' import { SafeAreaView } from 'react-native-safe-area-context' @@ -15,33 +19,34 @@ import { useWorkspaceClient } from '../worktree/use-workspace-client' import { connectionRetryAction } from '../transport/connection/connection-retry-action' import { loadMobileFilePreview, - previewError, - saveMobileTerminalArtifactPreview, - type MobileFilePreviewResult -} from './mobile-file-preview-request' -import { MobileFilePreviewDiscardModal } from './MobileFilePreviewDiscardModal' + saveMobileTerminalArtifactPreview +} from './preview/mobile-file-preview-request' +import { MobileFilePreviewDiscardModal } from './preview/MobileFilePreviewDiscardModal' import { NavBar } from '../components/nav-bar/NavBar' import { NavBarButton } from '../components/nav-bar/NavBarButton' -import { MobileFilePreviewBody } from './MobileFilePreviewBody' +import { MobileFilePreviewBody } from './preview/MobileFilePreviewBody' import { MobileMarkdownPreviewModeBar, useMarkdownPreviewMode -} from './MobileMarkdownPreviewModeBar' +} from './markdown/MobileMarkdownPreviewModeBar' import { displayNameFromPreviewPath, type MobileFilePreviewRouteState -} from './mobile-file-preview-route' -import { previewSourceFromRoute, sourceKeyForPreview } from './mobile-file-preview-source' -import { normalizeMobileFilePreviewLineColumn } from './mobile-file-preview-line-column' +} from './preview/mobile-file-preview-route' +import { previewSourceFromRoute, sourceKeyForPreview } from './preview/mobile-file-preview-source' +import { normalizeMobileFilePreviewLineColumn } from './preview/mobile-file-preview-line-column' import { hasUnsavedMobileTerminalArtifactDraft, isEditableMobileTerminalArtifactPreview, shouldKeepDirtyDraftOnPreviewLoadResult -} from './mobile-file-preview-editability' -import { filePreviewStyles as styles } from './mobile-file-preview-styles' -import { mobileMarkdownFileLink } from './mobile-markdown-file-link' -import { useMobileFilePreviewBack } from './use-mobile-file-preview-back' -import { filePreviewEditDraftKey, MobileFilePreviewEditDraft } from './MobileFilePreviewEditDraft' +} from './terminal-artifacts/mobile-file-preview-editability' +import { filePreviewStyles as styles } from './preview/mobile-file-preview-styles' +import { mobileMarkdownFileLink } from './markdown/mobile-markdown-file-link' +import { useMobileFilePreviewBack } from './preview/use-mobile-file-preview-back' +import { + filePreviewEditDraftKey, + MobileFilePreviewEditDraft +} from './terminal-artifacts/MobileFilePreviewEditDraft' import { dropDocumentEditDraft } from '../drafts/document-edit-draft-rows' type Props = { diff --git a/mobile/src/files/AttachmentTextScrollView.tsx b/mobile/src/files/attachments/AttachmentTextScrollView.tsx similarity index 95% rename from mobile/src/files/AttachmentTextScrollView.tsx rename to mobile/src/files/attachments/AttachmentTextScrollView.tsx index 07c84177e19..b9d5255f358 100644 --- a/mobile/src/files/AttachmentTextScrollView.tsx +++ b/mobile/src/files/attachments/AttachmentTextScrollView.tsx @@ -3,9 +3,9 @@ import { Platform, ScrollView, Text, View, type ScrollViewInstance } from 'react import type { AttachmentTextSource, AttachmentTextWindow -} from '../../../src/shared/attachment-text/attachment-text-source' -import { AttachmentTextViewport } from '../../../src/shared/attachment-text/attachment-text-viewport' -import { chatAttachmentPreviewStyles as styles } from './mobile-file-preview-styles' +} from '../../../../src/shared/attachment-text/attachment-text-source' +import { AttachmentTextViewport } from '../../../../src/shared/attachment-text/attachment-text-viewport' +import { chatAttachmentPreviewStyles as styles } from '../preview/mobile-file-preview-styles' function scrollElement(view: ScrollViewInstance | null): HTMLElement | null { if (Platform.OS !== 'web') { diff --git a/mobile/src/files/MobileChatAttachmentPreview.tsx b/mobile/src/files/attachments/MobileChatAttachmentPreview.tsx similarity index 87% rename from mobile/src/files/MobileChatAttachmentPreview.tsx rename to mobile/src/files/attachments/MobileChatAttachmentPreview.tsx index c1ab21efabb..eee059e3f2c 100644 --- a/mobile/src/files/MobileChatAttachmentPreview.tsx +++ b/mobile/src/files/attachments/MobileChatAttachmentPreview.tsx @@ -1,20 +1,20 @@ -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import type { Translate } from '../i18n/mobile-i18n' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' +import type { Translate } from '../../i18n/mobile-i18n' import { createMobileChatAttachmentReader } from './mobile-chat-attachment-read' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { useMemo, useState, useEffect, useCallback } from 'react' import { Pressable, Text, View } from 'react-native' import { AttachmentTextScrollView } from './AttachmentTextScrollView' import { SafeAreaView } from 'react-native-safe-area-context' -import { useWorkspaceRouteHandoff } from '../navigation/workspace-route-handoff' -import { useWorkspaceClient } from '../worktree/use-workspace-client' -import { createAttachmentTextPreviewHook } from '../../../src/shared/attachment-text/use-attachment-text-preview' -import { chatTextAttachmentName } from '../../../src/shared/native-chat-text-attachment-references' -import { MOBILE_BRIDGE_MAX_FRAME_BYTES } from '../../../src/shared/mobile-bridge-frame-bytes' -import { useClipboardWriter } from '../platform/clipboard' -import { getUiIntlLocale } from '../i18n/ui-intl-locale' -import { chatAttachmentPreviewStyles as styles } from './mobile-file-preview-styles' -import type { MobileFilePreviewRouteParams } from './mobile-file-preview-route' +import { useWorkspaceRouteHandoff } from '../../navigation/workspace-route-handoff' +import { useWorkspaceClient } from '../../worktree/use-workspace-client' +import { createAttachmentTextPreviewHook } from '../../../../src/shared/attachment-text/use-attachment-text-preview' +import { chatTextAttachmentName } from '../../../../src/shared/native-chat-text-attachment-references' +import { MOBILE_BRIDGE_MAX_FRAME_BYTES } from '../../../../src/shared/mobile-bridge-frame-bytes' +import { useClipboardWriter } from '../../platform/clipboard' +import { getUiIntlLocale } from '../../i18n/ui-intl-locale' +import { chatAttachmentPreviewStyles as styles } from '../preview/mobile-file-preview-styles' +import type { MobileFilePreviewRouteParams } from '../preview/mobile-file-preview-route' const useAttachmentTextPreview = createAttachmentTextPreviewHook({ useState, diff --git a/mobile/src/files/mobile-chat-attachment-read.ts b/mobile/src/files/attachments/mobile-chat-attachment-read.ts similarity index 83% rename from mobile/src/files/mobile-chat-attachment-read.ts rename to mobile/src/files/attachments/mobile-chat-attachment-read.ts index c3f2953ff98..0a9ae36f7a8 100644 --- a/mobile/src/files/mobile-chat-attachment-read.ts +++ b/mobile/src/files/attachments/mobile-chat-attachment-read.ts @@ -1,14 +1,14 @@ -import { sharedPromiseSettlementWaiters } from '../../../src/shared/promise-settlement-waiters' -import { hostStatusProbe } from '../transport/connection/host-status-probe-operations' -import { readHostStatusReply } from '../query/host-status-query' -import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' -import { rpcResultVariant } from '../transport/rpc/rpc-operation-result-reader' +import { sharedPromiseSettlementWaiters } from '../../../../src/shared/promise-settlement-waiters' +import { hostStatusProbe } from '../../transport/connection/host-status-probe-operations' +import { readHostStatusReply } from '../../query/host-status-query' +import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' +import { rpcResultVariant } from '../../transport/rpc/rpc-operation-result-reader' import { attachmentTextChunkSchema, type AttachmentTextRead -} from '../../../src/shared/attachment-text/attachment-text-source' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import type { UnvalidatedRpcRequestPort } from '../transport/rpc/unvalidated-rpc-request-port' +} from '../../../../src/shared/attachment-text/attachment-text-source' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import type { UnvalidatedRpcRequestPort } from '../../transport/rpc/unvalidated-rpc-request-port' // Match the shared wire identifier without importing the host capability aggregate into this route. const AGENT_SESSION_TEXT_ATTACHMENT_READ_CAPABILITY = 'native-chat.text-attachment-read.v1' diff --git a/mobile/src/files/MobileFileExplorerPanel.test.ts b/mobile/src/files/explorer/MobileFileExplorerPanel.test.ts similarity index 95% rename from mobile/src/files/MobileFileExplorerPanel.test.ts rename to mobile/src/files/explorer/MobileFileExplorerPanel.test.ts index 4d3863a0356..dcf2feb3561 100644 --- a/mobile/src/files/MobileFileExplorerPanel.test.ts +++ b/mobile/src/files/explorer/MobileFileExplorerPanel.test.ts @@ -4,12 +4,12 @@ import { act, create, type ReactTestRenderer } from 'react-test-renderer' import { afterEach, describe, expect, it, vi } from 'vitest' import { MobileFileExplorerPanel } from './MobileFileExplorerPanel' import type { MobileDirEntry } from './file-tree' -import { createOrcaQueryClient } from '../query/orca-query-client' -import { settleQueryNotifications } from '../test-support/rpc-recording/hook-mount' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import { stubRpcClient } from '../transport/rpc/rpc-client-test-stub' -import type { ConnectionState } from '../transport/connection/connection-state' -import type { RpcResponse } from '../transport/rpc/rpc-wire-types' +import { createOrcaQueryClient } from '../../query/orca-query-client' +import { settleQueryNotifications } from '../../test-support/rpc-recording/hook-mount' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import { stubRpcClient } from '../../transport/rpc/rpc-client-test-stub' +import type { ConnectionState } from '../../transport/connection/connection-state' +import type { RpcResponse } from '../../transport/rpc/rpc-wire-types' const mounted: ReactTestRenderer[] = [] @@ -25,7 +25,7 @@ const mockTransport = vi.hoisted( }) ) -vi.mock('../platform/use-reduced-motion', () => ({ useReducedMotionEnabled: () => true })) +vi.mock('../../platform/use-reduced-motion', () => ({ useReducedMotionEnabled: () => true })) vi.mock('react-native-reanimated', () => ({ Easing: { bezier: () => (t: number) => t }, default: { View: 'AnimatedView' }, @@ -110,7 +110,7 @@ vi.mock('lucide-react-native', () => ({ X: 'X' })) -vi.mock('../platform/haptics', () => ({ +vi.mock('../../platform/haptics', () => ({ triggerSelection: vi.fn() })) @@ -118,7 +118,7 @@ vi.mock('./mobile-file-explorer-row-actions-sheet', () => ({ useMobileFileExplorerRowActions: () => ({ openRowActions: () => {}, rowActionsSheet: null }) })) -vi.mock('../transport/connection/host-client-hooks', () => ({ +vi.mock('../../transport/connection/host-client-hooks', () => ({ useForceReconnect: () => mockTransport.forceReconnect, useHostClient: () => ({ client: mockTransport.client, diff --git a/mobile/src/files/MobileFileExplorerPanel.tsx b/mobile/src/files/explorer/MobileFileExplorerPanel.tsx similarity index 90% rename from mobile/src/files/MobileFileExplorerPanel.tsx rename to mobile/src/files/explorer/MobileFileExplorerPanel.tsx index b0bcb0dea2c..d87d3061071 100644 --- a/mobile/src/files/MobileFileExplorerPanel.tsx +++ b/mobile/src/files/explorer/MobileFileExplorerPanel.tsx @@ -17,38 +17,38 @@ import { type QueryKey, type UseQueryResult } from '@tanstack/react-query' -import { useWorkspaceExecutionHost } from '../navigation/workspace-execution-host' -import { useWorkspaceRouteHandoff } from '../navigation/workspace-route-handoff' -import { useHostQueries, useHostQuery } from '../query/host-query' -import { hostQueryKey, hostScopeKey } from '../query/host-query-key' -import { hostScreenState } from '../query/host-query-screen-state' -import { useForceReconnect } from '../transport/connection/host-client-hooks' -import { useWorkspaceClient } from '../worktree/use-workspace-client' -import { connectionRetryAction } from '../transport/connection/connection-retry-action' -import type { ConnectionState } from '../transport/connection/connection-state' -import { getWorktreeLabel } from '../worktree/worktree-label' +import { useWorkspaceExecutionHost } from '../../navigation/workspace-execution-host' +import { useWorkspaceRouteHandoff } from '../../navigation/workspace-route-handoff' +import { useHostQueries, useHostQuery } from '../../query/host-query' +import { hostQueryKey, hostScopeKey } from '../../query/host-query-key' +import { hostScreenState } from '../../query/host-query-screen-state' +import { useForceReconnect } from '../../transport/connection/host-client-hooks' +import { useWorkspaceClient } from '../../worktree/use-workspace-client' +import { connectionRetryAction } from '../../transport/connection/connection-retry-action' +import type { ConnectionState } from '../../transport/connection/connection-state' +import { getWorktreeLabel } from '../../worktree/worktree-label' import type { DirectoryCache, DirectoryState, FileExplorerRow } from './file-tree' -import { spacing } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import { spacing } from '../../theme/mobile-theme' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { fileDirectoryQuery, type DirectoryRead } from './mobile-file-explorer-operations' import { fileExplorerStyles as styles } from './mobile-file-explorer-styles' import { useMobileFileExplorerRows } from './mobile-file-explorer-rows' import { MobileFileExplorerRow } from './mobile-file-explorer-row' import { useMobileFileExplorerRowActions } from './mobile-file-explorer-row-actions-sheet' import { useMobileFileExplorerPaging } from './use-mobile-file-explorer-paging' -import { useRefreshHint } from '../query/refresh-hint' -import { navigateToMobileFilePreview } from './mobile-file-preview-navigation' +import { useRefreshHint } from '../../query/refresh-hint' +import { navigateToMobileFilePreview } from '../preview/mobile-file-preview-navigation' import { MobileLoadErrorState, MobileNoMatchState, MobileOfflineState, MobileStateView -} from '../components/MobileStateView' -import { BottomSearchBar } from '../components/nav-bar/BottomSearchBar' -import { NavBar } from '../components/nav-bar/NavBar' -import { NavBarButton } from '../components/nav-bar/NavBarButton' -import { translate } from '../i18n/mobile-i18n' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +} from '../../components/MobileStateView' +import { BottomSearchBar } from '../../components/nav-bar/BottomSearchBar' +import { NavBar } from '../../components/nav-bar/NavBar' +import { NavBarButton } from '../../components/nav-bar/NavBarButton' +import { translate } from '../../i18n/mobile-i18n' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' const ROOT = { relativePath: '' } const NO_FOLDERS: ReadonlySet = new Set() diff --git a/mobile/src/files/file-explorer-reply-schema.ts b/mobile/src/files/explorer/file-explorer-reply-schema.ts similarity index 92% rename from mobile/src/files/file-explorer-reply-schema.ts rename to mobile/src/files/explorer/file-explorer-reply-schema.ts index f02c6492e4f..0d91d11e337 100644 --- a/mobile/src/files/file-explorer-reply-schema.ts +++ b/mobile/src/files/explorer/file-explorer-reply-schema.ts @@ -1,5 +1,5 @@ import * as z from 'zod' -import { salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' +import { salvagedOptional, salvagingArray } from '../../../../src/shared/zod-salvage' // Require an array because the cache sorts/walks it unguarded. Drop rows without name or isDirectory; salvage decorative isSymlink. export const fileDirectoryEntriesSchema = salvagingArray( diff --git a/mobile/src/files/file-list-fallback.ts b/mobile/src/files/explorer/file-list-fallback.ts similarity index 100% rename from mobile/src/files/file-list-fallback.ts rename to mobile/src/files/explorer/file-list-fallback.ts diff --git a/mobile/src/files/file-tree.test.ts b/mobile/src/files/explorer/file-tree.test.ts similarity index 100% rename from mobile/src/files/file-tree.test.ts rename to mobile/src/files/explorer/file-tree.test.ts diff --git a/mobile/src/files/file-tree.ts b/mobile/src/files/explorer/file-tree.ts similarity index 97% rename from mobile/src/files/file-tree.ts rename to mobile/src/files/explorer/file-tree.ts index eeecd29128e..9d283aecfd1 100644 --- a/mobile/src/files/file-tree.ts +++ b/mobile/src/files/explorer/file-tree.ts @@ -1,5 +1,5 @@ -import { MEDIA_FILE_MIME_TYPES } from '../../../src/shared/file-preview/media-mime-types' -import { compareFileNames } from '../../../src/shared/file-listing/file-name-sort' +import { MEDIA_FILE_MIME_TYPES } from '../../../../src/shared/file-preview/media-mime-types' +import { compareFileNames } from '../../../../src/shared/file-listing/file-name-sort' export type MobileDirEntry = { name: string diff --git a/mobile/src/files/mobile-file-explorer-operations.ts b/mobile/src/files/explorer/mobile-file-explorer-operations.ts similarity index 90% rename from mobile/src/files/mobile-file-explorer-operations.ts rename to mobile/src/files/explorer/mobile-file-explorer-operations.ts index 5e3e7f35ea4..d3858727544 100644 --- a/mobile/src/files/mobile-file-explorer-operations.ts +++ b/mobile/src/files/explorer/mobile-file-explorer-operations.ts @@ -1,11 +1,14 @@ -import { translate } from '../i18n/mobile-i18n' -import { defineHostQuery } from '../query/host-query' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import { isMobileMethodUnavailableError } from '../transport/rpc/mobile-method-unavailable' -import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' -import { rpcResultVariant, rpcResultVariants } from '../transport/rpc/rpc-operation-result-reader' -import { FILE_READ_DIR_MAX_PAGE_ENTRIES } from '../../../src/shared/file-listing/file-dir-page' -import { compareDirEntries } from '../../../src/shared/file-listing/file-name-sort' +import { translate } from '../../i18n/mobile-i18n' +import { defineHostQuery } from '../../query/host-query' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import { isMobileMethodUnavailableError } from '../../transport/rpc/mobile-method-unavailable' +import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' +import { + rpcResultVariant, + rpcResultVariants +} from '../../transport/rpc/rpc-operation-result-reader' +import { FILE_READ_DIR_MAX_PAGE_ENTRIES } from '../../../../src/shared/file-listing/file-dir-page' +import { compareDirEntries } from '../../../../src/shared/file-listing/file-name-sort' import { fileDirectoryEntriesSchema, fileDirectoryPageSchema, diff --git a/mobile/src/files/mobile-file-explorer-row-actions-sheet.tsx b/mobile/src/files/explorer/mobile-file-explorer-row-actions-sheet.tsx similarity index 83% rename from mobile/src/files/mobile-file-explorer-row-actions-sheet.tsx rename to mobile/src/files/explorer/mobile-file-explorer-row-actions-sheet.tsx index 5a1f0db5025..1fb90aa511b 100644 --- a/mobile/src/files/mobile-file-explorer-row-actions-sheet.tsx +++ b/mobile/src/files/explorer/mobile-file-explorer-row-actions-sheet.tsx @@ -1,9 +1,9 @@ import { useState } from 'react' import { Copy } from 'lucide-react-native' -import { ActionSheetModal, type ActionSheetAction } from '../components/ActionSheetModal' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { useClipboardWriter } from '../platform/clipboard' -import { triggerError, triggerSuccess } from '../platform/haptics' +import { ActionSheetModal, type ActionSheetAction } from '../../components/ActionSheetModal' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' +import { useClipboardWriter } from '../../platform/clipboard' +import { triggerError, triggerSuccess } from '../../platform/haptics' import type { TreeNode } from './file-tree' /** Which row's actions are open, and the sheet that shows them. */ diff --git a/mobile/src/files/mobile-file-explorer-row.tsx b/mobile/src/files/explorer/mobile-file-explorer-row.tsx similarity index 95% rename from mobile/src/files/mobile-file-explorer-row.tsx rename to mobile/src/files/explorer/mobile-file-explorer-row.tsx index c6923495607..c1da0c60af6 100644 --- a/mobile/src/files/mobile-file-explorer-row.tsx +++ b/mobile/src/files/explorer/mobile-file-explorer-row.tsx @@ -1,4 +1,4 @@ -import { classifyMobileArtifact } from './mobile-artifact-kind' +import { classifyMobileArtifact } from '../preview/mobile-artifact-kind' import { ActivityIndicator, Pressable, Text, View } from 'react-native' import { ChevronDown, @@ -8,10 +8,10 @@ import { Folder, Image as ImageIcon } from 'lucide-react-native' -import { triggerMediumImpact, triggerSelection } from '../platform/haptics' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { colors, spacing } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import { triggerMediumImpact, triggerSelection } from '../../platform/haptics' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' +import { colors, spacing } from '../../theme/mobile-theme' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { DIRECTORY_PAGE_SIZE, type FileExplorerRow, @@ -20,9 +20,9 @@ import { type TreeNode } from './file-tree' import type { FolderPagingStatus } from './use-mobile-file-explorer-paging' -import { getUiIntlLocale } from '../i18n/ui-intl-locale' +import { getUiIntlLocale } from '../../i18n/ui-intl-locale' import { fileExplorerStyles as styles } from './mobile-file-explorer-styles' -import { canPreviewMobileFileRow } from './mobile-file-preview-navigation' +import { canPreviewMobileFileRow } from '../preview/mobile-file-preview-navigation' type Props = { item: FileExplorerRow diff --git a/mobile/src/files/mobile-file-explorer-rows.tsx b/mobile/src/files/explorer/mobile-file-explorer-rows.tsx similarity index 100% rename from mobile/src/files/mobile-file-explorer-rows.tsx rename to mobile/src/files/explorer/mobile-file-explorer-rows.tsx diff --git a/mobile/src/files/mobile-file-explorer-styles.ts b/mobile/src/files/explorer/mobile-file-explorer-styles.ts similarity index 94% rename from mobile/src/files/mobile-file-explorer-styles.ts rename to mobile/src/files/explorer/mobile-file-explorer-styles.ts index 4233138fe49..88b1e8589f1 100644 --- a/mobile/src/files/mobile-file-explorer-styles.ts +++ b/mobile/src/files/explorer/mobile-file-explorer-styles.ts @@ -1,6 +1,6 @@ import { StyleSheet } from 'react-native' -import { colors, radii, spacing, typography } from '../theme/mobile-theme' -import { themedStyleSheet } from '../theme/themed-styles' +import { colors, radii, spacing, typography } from '../../theme/mobile-theme' +import { themedStyleSheet } from '../../theme/themed-styles' export const fileExplorerStyles = themedStyleSheet(() => ({ container: { diff --git a/mobile/src/files/native-chat-file-search-operations.ts b/mobile/src/files/explorer/native-chat-file-search-operations.ts similarity index 84% rename from mobile/src/files/native-chat-file-search-operations.ts rename to mobile/src/files/explorer/native-chat-file-search-operations.ts index 8f39e7d5873..f23e7279c76 100644 --- a/mobile/src/files/native-chat-file-search-operations.ts +++ b/mobile/src/files/explorer/native-chat-file-search-operations.ts @@ -1,6 +1,6 @@ -import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' -import { rpcResultVariant } from '../transport/rpc/rpc-operation-result-reader' -import '../terminal/terminal-reply-schema' +import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' +import { rpcResultVariant } from '../../transport/rpc/rpc-operation-result-reader' +import '../../terminal/terminal-reply-schema' import { workspaceFilePathsSchema } from './workspace-file-paths-reply-schema' // Skip refusals to retain suggestions; inspect method_not_found first to fall back from search to inventory. diff --git a/mobile/src/files/use-mobile-file-explorer-paging.ts b/mobile/src/files/explorer/use-mobile-file-explorer-paging.ts similarity index 93% rename from mobile/src/files/use-mobile-file-explorer-paging.ts rename to mobile/src/files/explorer/use-mobile-file-explorer-paging.ts index 767e4294d5b..2b004108626 100644 --- a/mobile/src/files/use-mobile-file-explorer-paging.ts +++ b/mobile/src/files/explorer/use-mobile-file-explorer-paging.ts @@ -1,9 +1,9 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useQueryClient } from '@tanstack/react-query' -import { FILE_READ_DIR_MAX_PAGE_ENTRIES } from '../../../src/shared/file-listing/file-dir-page' -import { hostQueryKey, type HostQueryTarget } from '../query/host-query-key' -import { hostReadClient } from '../query/host-read-client' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' +import { FILE_READ_DIR_MAX_PAGE_ENTRIES } from '../../../../src/shared/file-listing/file-dir-page' +import { hostQueryKey, type HostQueryTarget } from '../../query/host-query-key' +import { hostReadClient } from '../../query/host-read-client' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import { DIRECTORY_PAGE_SIZE } from './file-tree' import { fileDirectoryQuery, diff --git a/mobile/src/files/workspace-file-paths-reply-schema.ts b/mobile/src/files/explorer/workspace-file-paths-reply-schema.ts similarity index 84% rename from mobile/src/files/workspace-file-paths-reply-schema.ts rename to mobile/src/files/explorer/workspace-file-paths-reply-schema.ts index 743c7eb409f..6f86037daea 100644 --- a/mobile/src/files/workspace-file-paths-reply-schema.ts +++ b/mobile/src/files/explorer/workspace-file-paths-reply-schema.ts @@ -1,5 +1,5 @@ import * as z from 'zod' -import { salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' +import { salvagedOptional, salvagingArray } from '../../../../src/shared/zod-salvage' // Keep legacy missing/unreadable file lists empty; validate the container before projecting nonempty relative paths. export const workspaceFilePathsSchema = z diff --git a/mobile/src/files/MobileFileMarkdownPreview.tsx b/mobile/src/files/markdown/MobileFileMarkdownPreview.tsx similarity index 79% rename from mobile/src/files/MobileFileMarkdownPreview.tsx rename to mobile/src/files/markdown/MobileFileMarkdownPreview.tsx index 6ee17d248e5..1a2b08723f2 100644 --- a/mobile/src/files/MobileFileMarkdownPreview.tsx +++ b/mobile/src/files/markdown/MobileFileMarkdownPreview.tsx @@ -1,15 +1,15 @@ import { MobileMarkdownPreviewImage } from './MobileMarkdownPreviewImage' -import type { MobileFilePreviewRpcSender } from './mobile-file-preview-operations' +import type { MobileFilePreviewRpcSender } from '../preview/mobile-file-preview-operations' import { ScrollView } from 'react-native' -import { MobileMarkdown } from '../components/MobileMarkdown' -import { MOBILE_MARKDOWN_DOCUMENT_SCALE } from '../components/mobile-markdown-styles' +import { MobileMarkdown } from '../../components/MobileMarkdown' +import { MOBILE_MARKDOWN_DOCUMENT_SCALE } from '../../components/mobile-markdown-styles' import { MobileFilePreviewSourceText, MobileFilePreviewTruncatedNote -} from './MobileFilePreviewSourceText' +} from '../preview/MobileFilePreviewSourceText' import type { MarkdownPreviewMode } from './MobileMarkdownPreviewModeBar' -import { filePreviewStyles as styles } from './mobile-file-preview-styles' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import { filePreviewStyles as styles } from '../preview/mobile-file-preview-styles' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' type Props = { mode: MarkdownPreviewMode diff --git a/mobile/src/files/MobileMarkdownPreviewImage.tsx b/mobile/src/files/markdown/MobileMarkdownPreviewImage.tsx similarity index 82% rename from mobile/src/files/MobileMarkdownPreviewImage.tsx rename to mobile/src/files/markdown/MobileMarkdownPreviewImage.tsx index d3c61a10a26..07dd6e735eb 100644 --- a/mobile/src/files/MobileMarkdownPreviewImage.tsx +++ b/mobile/src/files/markdown/MobileMarkdownPreviewImage.tsx @@ -1,15 +1,15 @@ import { useEffect, useState } from 'react' import { ActivityIndicator, Image, Pressable, Text, View } from 'react-native' -import { colors, spacing, typography, pressedStyle } from '../theme/mobile-theme' -import { themedStyleSheet } from '../theme/themed-styles' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { MobileLoadErrorState } from '../components/MobileStateView' -import { MobileNativeChatImageViewer } from '../session/native-chat/visual/MobileNativeChatImageViewer' -import { MobileImageViewerActions } from '../session/native-chat/visual/MobileImageViewerActions' -import type { MobileFilePreviewRpcSender } from './mobile-file-preview-operations' +import { colors, spacing, typography, pressedStyle } from '../../theme/mobile-theme' +import { themedStyleSheet } from '../../theme/themed-styles' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import { MobileLoadErrorState } from '../../components/MobileStateView' +import { MobileNativeChatImageViewer } from '../../session/native-chat/visual/MobileNativeChatImageViewer' +import { MobileImageViewerActions } from '../../session/native-chat/visual/MobileImageViewerActions' +import type { MobileFilePreviewRpcSender } from '../preview/mobile-file-preview-operations' import { loadMarkdownPreviewImage } from './mobile-markdown-image-source' -import { translate } from '../i18n/mobile-i18n' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +import { translate } from '../../i18n/mobile-i18n' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' type Props = { client: MobileFilePreviewRpcSender | null diff --git a/mobile/src/files/MobileMarkdownPreviewModeBar.tsx b/mobile/src/files/markdown/MobileMarkdownPreviewModeBar.tsx similarity index 85% rename from mobile/src/files/MobileMarkdownPreviewModeBar.tsx rename to mobile/src/files/markdown/MobileMarkdownPreviewModeBar.tsx index 5a85333f346..7a8371c1bb9 100644 --- a/mobile/src/files/MobileMarkdownPreviewModeBar.tsx +++ b/mobile/src/files/markdown/MobileMarkdownPreviewModeBar.tsx @@ -1,10 +1,10 @@ import { useState } from 'react' import { View } from 'react-native' -import { MobileSegmentedControl } from '../components/MobileSegmentedControl' -import { MobileSourceCopyButton } from '../components/MobileSourceCopyButton' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { filePreviewStyles as styles } from './mobile-file-preview-styles' +import { MobileSegmentedControl } from '../../components/MobileSegmentedControl' +import { MobileSourceCopyButton } from '../../components/MobileSourceCopyButton' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import { filePreviewStyles as styles } from '../preview/mobile-file-preview-styles' export type MarkdownPreviewMode = 'preview' | 'source' diff --git a/mobile/src/files/mobile-markdown-file-link.test.ts b/mobile/src/files/markdown/mobile-markdown-file-link.test.ts similarity index 96% rename from mobile/src/files/mobile-markdown-file-link.test.ts rename to mobile/src/files/markdown/mobile-markdown-file-link.test.ts index 34d926ef441..412dc80519f 100644 --- a/mobile/src/files/mobile-markdown-file-link.test.ts +++ b/mobile/src/files/markdown/mobile-markdown-file-link.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { routeMarkdownHref } from '../components/markdown-href-routing' +import { routeMarkdownHref } from '../../components/markdown-href-routing' import { mobileMarkdownFileLink } from './mobile-markdown-file-link' const params = { diff --git a/mobile/src/files/mobile-markdown-file-link.ts b/mobile/src/files/markdown/mobile-markdown-file-link.ts similarity index 86% rename from mobile/src/files/mobile-markdown-file-link.ts rename to mobile/src/files/markdown/mobile-markdown-file-link.ts index c5f8ea0552b..9d8a615d059 100644 --- a/mobile/src/files/mobile-markdown-file-link.ts +++ b/mobile/src/files/markdown/mobile-markdown-file-link.ts @@ -2,12 +2,12 @@ import { isWindowsAbsolutePathLike, relativePathInsideRoot, resolveRuntimePath -} from '../../../src/shared/cross-platform-path' -import { splitFilePathLineSuffix } from '../components/markdown-file-path-detection' +} from '../../../../src/shared/cross-platform-path' +import { splitFilePathLineSuffix } from '../../components/markdown-file-path-detection' import { createMobileFilePreviewHref, type MobileFilePreviewRouteParams -} from './mobile-file-preview-route' +} from '../preview/mobile-file-preview-route' export function mobileMarkdownFileLink(params: MobileFilePreviewRouteParams, pathText: string) { if (params.source === 'terminalArtifact' || !params.relativePath) { diff --git a/mobile/src/files/mobile-markdown-image-source.test.ts b/mobile/src/files/markdown/mobile-markdown-image-source.test.ts similarity index 55% rename from mobile/src/files/mobile-markdown-image-source.test.ts rename to mobile/src/files/markdown/mobile-markdown-image-source.test.ts index 547969f1852..0aa0fb60404 100644 --- a/mobile/src/files/mobile-markdown-image-source.test.ts +++ b/mobile/src/files/markdown/mobile-markdown-image-source.test.ts @@ -30,28 +30,4 @@ describe('Markdown preview images', () => { }) } ) - - it.each([ - '../../private.png', - '/private.png', - 'C:\\private.png', - 'file:///tmp/private.png', - 'javascript:alert(1)', - 'notes.txt' - ])('does not read unauthorized or unsupported image locations: %s', async (href) => { - const client = host() - await expect(loadMarkdownPreviewImage(client, 'wt', 'docs/readme.md', href)).rejects.toThrow() - expect(client.sendRequest).not.toHaveBeenCalled() - }) - - it.each(['/Users/me/notes/readme.md', 'C:\\notes\\readme.md'])( - 'does not read workspace images for a document outside the workspace: %s', - async (documentPath) => { - const client = host() - await expect( - loadMarkdownPreviewImage(client, 'wt', documentPath, 'images/d.png') - ).rejects.toThrow() - expect(client.sendRequest).not.toHaveBeenCalled() - } - ) }) diff --git a/mobile/src/files/mobile-markdown-image-source.ts b/mobile/src/files/markdown/mobile-markdown-image-source.ts similarity index 79% rename from mobile/src/files/mobile-markdown-image-source.ts rename to mobile/src/files/markdown/mobile-markdown-image-source.ts index de157ae1db5..209467b11d1 100644 --- a/mobile/src/files/mobile-markdown-image-source.ts +++ b/mobile/src/files/markdown/mobile-markdown-image-source.ts @@ -2,12 +2,12 @@ import { isWindowsAbsolutePathLike, relativePathInsideRoot, resolveRuntimePath -} from '../../../src/shared/cross-platform-path' -import { routeMarkdownHref } from '../components/markdown-href-routing' -import type { MobileFilePreviewRpcSender } from './mobile-file-preview-operations' -import { loadMobileFilePreview } from './mobile-file-preview-request' -import { classifyMobileArtifact } from './mobile-artifact-kind' -import { svgMarkupDataUri } from '../../../src/shared/file-preview/svg-document' +} from '../../../../src/shared/cross-platform-path' +import { routeMarkdownHref } from '../../components/markdown-href-routing' +import type { MobileFilePreviewRpcSender } from '../preview/mobile-file-preview-operations' +import { loadMobileFilePreview } from '../preview/mobile-file-preview-request' +import { classifyMobileArtifact } from '../preview/mobile-artifact-kind' +import { svgMarkupDataUri } from '../../../../src/shared/file-preview/svg-document' // Absolute paths leave the workspace; an external-file tab's document has no workspace images. function isOutsideWorkspace(path: string): boolean { diff --git a/mobile/src/files/MobileFileMediaPreview.tsx b/mobile/src/files/media/MobileFileMediaPreview.tsx similarity index 84% rename from mobile/src/files/MobileFileMediaPreview.tsx rename to mobile/src/files/media/MobileFileMediaPreview.tsx index e62bdaddff2..d9251974d7c 100644 --- a/mobile/src/files/MobileFileMediaPreview.tsx +++ b/mobile/src/files/media/MobileFileMediaPreview.tsx @@ -1,18 +1,18 @@ import { useEffect, useState } from 'react' import { ActivityIndicator, Text, View } from 'react-native' -import { colors } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import type { MobileFilePreviewRpcSender } from './mobile-file-preview-operations' +import { colors } from '../../theme/mobile-theme' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import type { MobileFilePreviewRpcSender } from '../preview/mobile-file-preview-operations' import { PDF_MIME_TYPE, type MobileFileMedia } from './mobile-file-media' import { downloadMobileFileMedia, type MobileMediaSink } from './mobile-file-media-download' import { createMobileMediaSink } from './mobile-media-preview-cache' import { MobileMediaPlayback } from './MobileMediaPlayback' import { MobilePdfPreview } from './MobilePdfPreview' -import { filePreviewStyles as styles } from './mobile-file-preview-styles' -import { formatPreviewByteLength } from './mobile-file-preview-response' -import { MobileLoadErrorState } from '../components/MobileStateView' -import { translate } from '../i18n/mobile-i18n' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +import { filePreviewStyles as styles } from '../preview/mobile-file-preview-styles' +import { formatPreviewByteLength } from '../preview/mobile-file-preview-response' +import { MobileLoadErrorState } from '../../components/MobileStateView' +import { translate } from '../../i18n/mobile-i18n' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' type State = { uri: string } | { error: string } | { bytes: number; total: number } type Props = { media: MobileFileMedia; client: MobileFilePreviewRpcSender | null; title: string } diff --git a/mobile/src/files/MobileMediaPlayback.tsx b/mobile/src/files/media/MobileMediaPlayback.tsx similarity index 91% rename from mobile/src/files/MobileMediaPlayback.tsx rename to mobile/src/files/media/MobileMediaPlayback.tsx index 855532848b4..86acdc9afc4 100644 --- a/mobile/src/files/MobileMediaPlayback.tsx +++ b/mobile/src/files/media/MobileMediaPlayback.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react' import { Text, View } from 'react-native' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { filePreviewStyles as styles } from './mobile-file-preview-styles' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import { filePreviewStyles as styles } from '../preview/mobile-file-preview-styles' export type MobileMediaPlaybackProps = { uri: string; mimeType: string; title: string } diff --git a/mobile/src/files/MobilePdfPreview.tsx b/mobile/src/files/media/MobilePdfPreview.tsx similarity index 82% rename from mobile/src/files/MobilePdfPreview.tsx rename to mobile/src/files/media/MobilePdfPreview.tsx index 2fb92e4db68..d76848660ee 100644 --- a/mobile/src/files/MobilePdfPreview.tsx +++ b/mobile/src/files/media/MobilePdfPreview.tsx @@ -1,15 +1,15 @@ import { useCallback, useRef, useState } from 'react' import { useFocusEffect } from 'expo-router' import { ActivityIndicator, Text, View } from 'react-native' -import { colors } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { usePdfOpener } from '../platform/pdf-opener' -import { PdfOpenRefusedError } from '../platform/pdf-opener-contract' +import { colors } from '../../theme/mobile-theme' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import { usePdfOpener } from '../../platform/pdf-opener' +import { PdfOpenRefusedError } from '../../platform/pdf-opener-contract' import type { MobileFileMedia } from './mobile-file-media' -import { filePreviewStyles as styles } from './mobile-file-preview-styles' -import { MobileLoadErrorState, MobileStateButton } from '../components/MobileStateView' -import { translate } from '../i18n/mobile-i18n' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +import { filePreviewStyles as styles } from '../preview/mobile-file-preview-styles' +import { MobileLoadErrorState, MobileStateButton } from '../../components/MobileStateView' +import { translate } from '../../i18n/mobile-i18n' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' type State = | { status: 'opening' } diff --git a/mobile/src/files/mobile-file-media-download.ts b/mobile/src/files/media/mobile-file-media-download.ts similarity index 93% rename from mobile/src/files/mobile-file-media-download.ts rename to mobile/src/files/media/mobile-file-media-download.ts index 71ae4eaa484..29ffe879839 100644 --- a/mobile/src/files/mobile-file-media-download.ts +++ b/mobile/src/files/media/mobile-file-media-download.ts @@ -1,10 +1,10 @@ import { Buffer } from 'buffer/index.js' import * as z from 'zod' -import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' -import { rpcResultVariant } from '../transport/rpc/rpc-operation-result-reader' -import type { MobileFilePreviewRpcSender } from './mobile-file-preview-operations' +import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' +import { rpcResultVariant } from '../../transport/rpc/rpc-operation-result-reader' +import type { MobileFilePreviewRpcSender } from '../preview/mobile-file-preview-operations' import type { MobileFileMedia } from './mobile-file-media' -import { isMobileMethodUnavailableError } from '../transport/rpc/mobile-method-unavailable' +import { isMobileMethodUnavailableError } from '../../transport/rpc/mobile-method-unavailable' export const MOBILE_MEDIA_CHUNK_BYTES = 384 * 1024 export const MOBILE_MEDIA_MAX_BYTES = 256 * 1024 * 1024 diff --git a/mobile/src/files/mobile-file-media.ts b/mobile/src/files/media/mobile-file-media.ts similarity index 90% rename from mobile/src/files/mobile-file-media.ts rename to mobile/src/files/media/mobile-file-media.ts index c6fbeeddb18..3a745d654e2 100644 --- a/mobile/src/files/mobile-file-media.ts +++ b/mobile/src/files/media/mobile-file-media.ts @@ -1,4 +1,4 @@ -import { MEDIA_FILE_MIME_TYPES } from '../../../src/shared/file-preview/media-mime-types' +import { MEDIA_FILE_MIME_TYPES } from '../../../../src/shared/file-preview/media-mime-types' export const PDF_MIME_TYPE = 'application/pdf' diff --git a/mobile/src/files/mobile-media-preview-cache.ts b/mobile/src/files/media/mobile-media-preview-cache.ts similarity index 100% rename from mobile/src/files/mobile-media-preview-cache.ts rename to mobile/src/files/media/mobile-media-preview-cache.ts diff --git a/mobile/src/files/file-exists-error.ts b/mobile/src/files/mutation-ownership/file-exists-error.ts similarity index 85% rename from mobile/src/files/file-exists-error.ts rename to mobile/src/files/mutation-ownership/file-exists-error.ts index 929774fb201..f00b9e913cd 100644 --- a/mobile/src/files/file-exists-error.ts +++ b/mobile/src/files/mutation-ownership/file-exists-error.ts @@ -1,4 +1,4 @@ -import '../i18n/mobile-i18n' +import '../../i18n/mobile-i18n' export function isFileExistsErrorMessage(message: string): boolean { const normalized = message.toLowerCase() diff --git a/mobile/src/files/file-ownership-reply-schema.ts b/mobile/src/files/mutation-ownership/file-ownership-reply-schema.ts similarity index 93% rename from mobile/src/files/file-ownership-reply-schema.ts rename to mobile/src/files/mutation-ownership/file-ownership-reply-schema.ts index 916fcff091f..4e8f1c07202 100644 --- a/mobile/src/files/file-ownership-reply-schema.ts +++ b/mobile/src/files/mutation-ownership/file-ownership-reply-schema.ts @@ -1,5 +1,5 @@ import * as z from 'zod' -import { salvagedOptional } from '../../../src/shared/zod-salvage' +import { salvagedOptional } from '../../../../src/shared/zod-salvage' // Ownership routes writes: unreadable routing members must fail, since salvaging them to absent could send a remote mutation locally. diff --git a/mobile/src/files/mobile-file-mutation-ownership.ts b/mobile/src/files/mutation-ownership/mobile-file-mutation-ownership.ts similarity index 85% rename from mobile/src/files/mobile-file-mutation-ownership.ts rename to mobile/src/files/mutation-ownership/mobile-file-mutation-ownership.ts index 31fd49806ec..e1bc71cb0db 100644 --- a/mobile/src/files/mobile-file-mutation-ownership.ts +++ b/mobile/src/files/mutation-ownership/mobile-file-mutation-ownership.ts @@ -1,13 +1,13 @@ -import { parseExecutionHostId } from '../../../src/shared/execution-host' -import { assertFileMutationOwnershipCapability } from '../../../src/shared/file-write/file-mutation-ownership' -import type { SshConnectionState, SshMutationExpectation } from '../../../src/shared/ssh-types' -import { translate } from '../i18n/mobile-i18n' +import { parseExecutionHostId } from '../../../../src/shared/execution-host' +import { assertFileMutationOwnershipCapability } from '../../../../src/shared/file-write/file-mutation-ownership' +import type { SshConnectionState, SshMutationExpectation } from '../../../../src/shared/ssh-types' +import { translate } from '../../i18n/mobile-i18n' import { - fileOwnershipRuntimeStatusRead, fileOwnershipSshStateRead, fileOwnershipWorktreeRead, type MobileFileOwnershipRpcSender } from './mobile-file-ownership-operations' +import { taskRuntimeStatusRead } from '../../tasks/screen-state/mobile-task-runtime-operations' const FILE_MUTATION_TIMEOUT_MS = 15_000 @@ -53,10 +53,10 @@ export async function captureMobileFileMutationOwnership( client: MobileFileOwnershipRpcSender, worktree: string ): Promise { - const statusReply = await fileOwnershipRuntimeStatusRead.request(client, undefined, { + const statusReply = await taskRuntimeStatusRead.request(client, undefined, { timeoutMs: FILE_MUTATION_TIMEOUT_MS }) - const status = fileOwnershipRuntimeStatusRead.interpret(statusReply) + const status = taskRuntimeStatusRead.interpret(statusReply) assertFileMutationOwnershipCapability(status) const worktreeReply = await fileOwnershipWorktreeRead.request( diff --git a/mobile/src/files/mobile-file-ownership-operations.ts b/mobile/src/files/mutation-ownership/mobile-file-ownership-operations.ts similarity index 83% rename from mobile/src/files/mobile-file-ownership-operations.ts rename to mobile/src/files/mutation-ownership/mobile-file-ownership-operations.ts index 15943df7f30..7db0e0cc98a 100644 --- a/mobile/src/files/mobile-file-ownership-operations.ts +++ b/mobile/src/files/mutation-ownership/mobile-file-ownership-operations.ts @@ -1,5 +1,5 @@ -import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' -import { rpcResultVariant } from '../transport/rpc/rpc-operation-result-reader' +import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' +import { rpcResultVariant } from '../../transport/rpc/rpc-operation-result-reader' import { fileOwnershipSshStateSchema, fileOwnershipWorktreeSchema @@ -8,7 +8,7 @@ import { // All ownership reads must succeed before a write: refusal or malformed replies abort with a named error rather than risking the wrong host. // Reuse the Tasks runtime-status operation; this gate needs the same fields. -export { taskRuntimeStatusRead as fileOwnershipRuntimeStatusRead } from '../tasks/screen-state/mobile-task-runtime-operations' +import '../../tasks/screen-state/mobile-task-runtime-operations' export const fileOwnershipWorktreeRead = bindDeferredRpcOperation( defineRpcOperation({ diff --git a/mobile/src/files/MobileFilePreviewBody.tsx b/mobile/src/files/preview/MobileFilePreviewBody.tsx similarity index 88% rename from mobile/src/files/MobileFilePreviewBody.tsx rename to mobile/src/files/preview/MobileFilePreviewBody.tsx index 811223a0b20..7f8d5fddb3b 100644 --- a/mobile/src/files/MobileFilePreviewBody.tsx +++ b/mobile/src/files/preview/MobileFilePreviewBody.tsx @@ -1,24 +1,24 @@ -import { MobileFileMediaPreview } from './MobileFileMediaPreview' +import { MobileFileMediaPreview } from '../media/MobileFileMediaPreview' import type { MobileFilePreviewRpcSender } from './mobile-file-preview-operations' import { Text, View } from 'react-native' import { FileText } from 'lucide-react-native' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import type { MobileFilePreviewResult } from './mobile-file-preview-request' -import { MobileFileMarkdownPreview } from './MobileFileMarkdownPreview' -import type { MarkdownPreviewMode } from './MobileMarkdownPreviewModeBar' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import type { MobileFilePreviewResult } from './mobile-file-preview-response' +import { MobileFileMarkdownPreview } from '../markdown/MobileFileMarkdownPreview' +import type { MarkdownPreviewMode } from '../markdown/MobileMarkdownPreviewModeBar' import { MobileFilePreviewEditableSource } from './MobileFilePreviewEditableSource' import { MobileFilePreviewSourceText } from './MobileFilePreviewSourceText' import type { MobileFilePreviewLineColumn } from './mobile-file-preview-line-column' import { filePreviewStyles as styles } from './mobile-file-preview-styles' -import { MobilePanZoomImage } from '../components/MobilePanZoomImage' -import { MobileHtmlPreview } from '../components/MobileHtmlPreview' -import { MobileSvgPreview } from '../components/MobileSvgPreview' +import { MobilePanZoomImage } from '../../components/MobilePanZoomImage' +import { MobileHtmlPreview } from '../../components/MobileHtmlPreview' +import { MobileSvgPreview } from '../../components/MobileSvgPreview' import { MobileLoadErrorState, MobileOfflineState, MobileStateView -} from '../components/MobileStateView' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +} from '../../components/MobileStateView' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' type Props = { preview: MobileFilePreviewResult diff --git a/mobile/src/files/MobileFilePreviewDiscardModal.tsx b/mobile/src/files/preview/MobileFilePreviewDiscardModal.tsx similarity index 86% rename from mobile/src/files/MobileFilePreviewDiscardModal.tsx rename to mobile/src/files/preview/MobileFilePreviewDiscardModal.tsx index ddf40459f1b..50d8047746f 100644 --- a/mobile/src/files/MobileFilePreviewDiscardModal.tsx +++ b/mobile/src/files/preview/MobileFilePreviewDiscardModal.tsx @@ -1,5 +1,5 @@ -import { ConfirmModal } from '../components/ConfirmModal' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +import { ConfirmModal } from '../../components/ConfirmModal' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' export function MobileFilePreviewDiscardModal(props: { visible: boolean diff --git a/mobile/src/files/MobileFilePreviewEditableSource.tsx b/mobile/src/files/preview/MobileFilePreviewEditableSource.tsx similarity index 93% rename from mobile/src/files/MobileFilePreviewEditableSource.tsx rename to mobile/src/files/preview/MobileFilePreviewEditableSource.tsx index 4d41b14d780..d10c509c6d9 100644 --- a/mobile/src/files/MobileFilePreviewEditableSource.tsx +++ b/mobile/src/files/preview/MobileFilePreviewEditableSource.tsx @@ -2,9 +2,9 @@ import { useEffect, useState } from 'react' import { Text, TextInput, View } from 'react-native' import type { MobileFilePreviewLineColumn } from './mobile-file-preview-line-column' import { textOffsetForLineColumn } from './mobile-file-preview-line-column' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { filePreviewStyles as styles } from './mobile-file-preview-styles' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' type Props = { title: string diff --git a/mobile/src/files/MobileFilePreviewSourceText.tsx b/mobile/src/files/preview/MobileFilePreviewSourceText.tsx similarity index 89% rename from mobile/src/files/MobileFilePreviewSourceText.tsx rename to mobile/src/files/preview/MobileFilePreviewSourceText.tsx index 1b694e801cc..d98dcbfa287 100644 --- a/mobile/src/files/MobileFilePreviewSourceText.tsx +++ b/mobile/src/files/preview/MobileFilePreviewSourceText.tsx @@ -1,12 +1,13 @@ import { useEffect, useMemo, useRef } from 'react' import { ScrollView, Text, type ScrollViewInstance } from 'react-native' -import { MobileSyntaxSegments } from '../components/MobileSyntaxSegments' -import { formatPreviewByteLength } from './mobile-file-preview-request' +import { MobileSyntaxSegments } from '../../components/MobileSyntaxSegments' +import './mobile-file-preview-request' +import { formatPreviewByteLength } from './mobile-file-preview-response' import { scrollOffsetForPreviewLine } from './mobile-file-preview-line-column' import { buildMobileFilePreviewSyntax } from './mobile-file-preview-syntax' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { filePreviewStyles as styles } from './mobile-file-preview-styles' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' export function MobileFilePreviewSourceText({ relativePath, diff --git a/mobile/src/files/file-preview-reply-schema.ts b/mobile/src/files/preview/file-preview-reply-schema.ts similarity index 96% rename from mobile/src/files/file-preview-reply-schema.ts rename to mobile/src/files/preview/file-preview-reply-schema.ts index 5b84f0268b7..7aa7d223a76 100644 --- a/mobile/src/files/file-preview-reply-schema.ts +++ b/mobile/src/files/preview/file-preview-reply-schema.ts @@ -1,5 +1,5 @@ import * as z from 'zod' -import { salvagedOptional } from '../../../src/shared/zod-salvage' +import { salvagedOptional } from '../../../../src/shared/zod-salvage' // Require unguarded members, salvage typed guards to their existing fallbacks, and retain unknown for truthiness-only reads. diff --git a/mobile/src/files/mobile-artifact-kind.ts b/mobile/src/files/preview/mobile-artifact-kind.ts similarity index 100% rename from mobile/src/files/mobile-artifact-kind.ts rename to mobile/src/files/preview/mobile-artifact-kind.ts diff --git a/mobile/src/files/mobile-file-preview-line-column.ts b/mobile/src/files/preview/mobile-file-preview-line-column.ts similarity index 100% rename from mobile/src/files/mobile-file-preview-line-column.ts rename to mobile/src/files/preview/mobile-file-preview-line-column.ts diff --git a/mobile/src/files/mobile-file-preview-navigation.ts b/mobile/src/files/preview/mobile-file-preview-navigation.ts similarity index 89% rename from mobile/src/files/mobile-file-preview-navigation.ts rename to mobile/src/files/preview/mobile-file-preview-navigation.ts index e9e3fbcc5bc..f050fac9961 100644 --- a/mobile/src/files/mobile-file-preview-navigation.ts +++ b/mobile/src/files/preview/mobile-file-preview-navigation.ts @@ -1,6 +1,6 @@ -import { mobileFileMediaMime } from './mobile-file-media' +import { mobileFileMediaMime } from '../media/mobile-file-media' import { classifyMobileArtifact } from './mobile-artifact-kind' -import { defaultScheduleTimer } from '../platform/timer-scheduler' +import { defaultScheduleTimer } from '../../platform/timer-scheduler' import { createMobileFilePreviewHref, type MobileFilePreviewHref, diff --git a/mobile/src/files/mobile-file-preview-operations.ts b/mobile/src/files/preview/mobile-file-preview-operations.ts similarity index 95% rename from mobile/src/files/mobile-file-preview-operations.ts rename to mobile/src/files/preview/mobile-file-preview-operations.ts index a75f58e44df..b88b8b86a0c 100644 --- a/mobile/src/files/mobile-file-preview-operations.ts +++ b/mobile/src/files/preview/mobile-file-preview-operations.ts @@ -1,5 +1,5 @@ -import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' -import { rpcResultVariant } from '../transport/rpc/rpc-operation-result-reader' +import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' +import { rpcResultVariant } from '../../transport/rpc/rpc-operation-result-reader' import { filePreviewImageSchema, filePreviewTextSchema, diff --git a/mobile/src/files/mobile-file-preview-request.test.ts b/mobile/src/files/preview/mobile-file-preview-request.test.ts similarity index 95% rename from mobile/src/files/mobile-file-preview-request.test.ts rename to mobile/src/files/preview/mobile-file-preview-request.test.ts index 89692886a59..51150105f98 100644 --- a/mobile/src/files/mobile-file-preview-request.test.ts +++ b/mobile/src/files/preview/mobile-file-preview-request.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest' -import type { RpcFailure, RpcResponse, RpcSuccess } from '../transport/rpc/rpc-wire-types' +import type { RpcFailure, RpcResponse, RpcSuccess } from '../../transport/rpc/rpc-wire-types' import { saveMobileTerminalArtifactPreview } from './mobile-file-preview-request' -import '../transport/rpc/rpc-incompatible-reply-error' +import '../../transport/rpc/rpc-incompatible-reply-error' function ok(result: unknown): RpcSuccess { return { id: '1', ok: true, result, _meta: { runtimeId: 'runtime-1' } } diff --git a/mobile/src/files/mobile-file-preview-request.ts b/mobile/src/files/preview/mobile-file-preview-request.ts similarity index 94% rename from mobile/src/files/mobile-file-preview-request.ts rename to mobile/src/files/preview/mobile-file-preview-request.ts index 52288a8b17b..546dba823ab 100644 --- a/mobile/src/files/mobile-file-preview-request.ts +++ b/mobile/src/files/preview/mobile-file-preview-request.ts @@ -1,8 +1,8 @@ -import { terminalArtifactPreviewMatchesBase } from './mobile-file-preview-editability' -import { mobileFileMedia } from './mobile-file-media' +import { terminalArtifactPreviewMatchesBase } from '../terminal-artifacts/mobile-file-preview-editability' +import { mobileFileMedia } from '../media/mobile-file-media' import { classifyMobileArtifact } from './mobile-artifact-kind' -import type { RpcAcceptedResult } from '../transport/rpc/rpc-accepted-result' -import type { RpcFailure, RpcResponse } from '../transport/rpc/rpc-wire-types' +import type { RpcAcceptedResult } from '../../transport/rpc/rpc-accepted-result' +import type { RpcFailure, RpcResponse } from '../../transport/rpc/rpc-wire-types' import { filePreviewImageRead, filePreviewTextRead, @@ -22,14 +22,7 @@ import { refreshTerminalArtifactSourceAfterGrantFailure, type MobileTerminalArtifactPreviewSource, type TerminalArtifactRetryOptions -} from './mobile-terminal-artifact-grant-refresh' - -export { formatPreviewByteLength, previewError } from './mobile-file-preview-response' - -export type { - MobileFilePreviewResult, - MobileFilePreviewTextKind -} from './mobile-file-preview-response' +} from '../terminal-artifacts/mobile-terminal-artifact-grant-refresh' export type MobileFilePreviewReadMethod = 'files.read' | 'files.readPreview' export type MobileTerminalArtifactPreviewReadMethod = diff --git a/mobile/src/files/mobile-file-preview-response.ts b/mobile/src/files/preview/mobile-file-preview-response.ts similarity index 94% rename from mobile/src/files/mobile-file-preview-response.ts rename to mobile/src/files/preview/mobile-file-preview-response.ts index ef2e7324d8b..3f7501613de 100644 --- a/mobile/src/files/mobile-file-preview-response.ts +++ b/mobile/src/files/preview/mobile-file-preview-response.ts @@ -1,10 +1,10 @@ -import type { MobileFileMedia } from './mobile-file-media' +import type { MobileFileMedia } from '../media/mobile-file-media' import { classifyMobileArtifact } from './mobile-artifact-kind' -import { translate } from '../i18n/mobile-i18n' -import { getUiIntlLocale } from '../i18n/ui-intl-locale' -import type { RpcFailure } from '../transport/rpc/rpc-wire-types' -import { isMarkdownPath } from './file-tree' -import { isTerminalArtifactGrantError } from './terminal-artifact-grant-error' +import { translate } from '../../i18n/mobile-i18n' +import { getUiIntlLocale } from '../../i18n/ui-intl-locale' +import type { RpcFailure } from '../../transport/rpc/rpc-wire-types' +import { isMarkdownPath } from '../explorer/file-tree' +import { isTerminalArtifactGrantError } from '../terminal-artifacts/terminal-artifact-grant-error' export type MobileFilePreviewTextKind = 'html' | 'markdown' | 'svg' | 'text' diff --git a/mobile/src/files/mobile-file-preview-retry.test.tsx b/mobile/src/files/preview/mobile-file-preview-retry.test.tsx similarity index 72% rename from mobile/src/files/mobile-file-preview-retry.test.tsx rename to mobile/src/files/preview/mobile-file-preview-retry.test.tsx index c81519c9f02..5241f3f5bc4 100644 --- a/mobile/src/files/mobile-file-preview-retry.test.tsx +++ b/mobile/src/files/preview/mobile-file-preview-retry.test.tsx @@ -1,4 +1,4 @@ -vi.mock('./MobileFileMediaPreview', () => ({ MobileFileMediaPreview: () => null })) +vi.mock('../media/MobileFileMediaPreview', () => ({ MobileFileMediaPreview: () => null })) import { createElement } from 'react' import { act, create, type ReactTestRenderer } from 'react-test-renderer' import { afterEach, expect, it, vi } from 'vitest' @@ -39,25 +39,24 @@ vi.mock('lucide-react-native', () => ({ Save: 'Icon', WifiOff: 'Icon' })) -vi.mock('../navigation/route-handoff', () => ({ +vi.mock('../../navigation/route-handoff', () => ({ useRouteHandoff: () => ({ back: () => {}, canGoBack: () => false, push }) })) -vi.mock('../components/ConfirmModal', () => ({ ConfirmModal: () => null })) -vi.mock('./MobileFileMarkdownPreview', () => ({ +vi.mock('../../components/ConfirmModal', () => ({ ConfirmModal: () => null })) +vi.mock('../markdown/MobileFileMarkdownPreview', () => ({ MobileFileMarkdownPreview: 'MobileFileMarkdownPreview' })) -vi.mock('../components/MobileSourceCopyButton', () => ({ MobileSourceCopyButton: () => null })) +vi.mock('../../components/MobileSourceCopyButton', () => ({ MobileSourceCopyButton: () => null })) vi.mock('./MobileFilePreviewSourceText', () => ({ MobileFilePreviewSourceText: () => null })) // No shell client initially: the screen must settle on waiting. -vi.mock('../transport/connection/host-client-hooks', () => ({ +vi.mock('../../transport/connection/host-client-hooks', () => ({ useForceReconnect: () => transport.forceReconnect, useHostClient: () => ({ ...transport.connection, clientId: null }) })) -import { MobileFilePreviewScreen } from './MobileFilePreviewScreen' -import { WorkspaceExecutionHostContext } from '../navigation/workspace-execution-host' -import { resetDocumentEditDraftsForTests } from '../drafts/document-edit-draft-rows' -import type { ExecutionHostId } from '../../../src/shared/execution-host' +import { MobileFilePreviewScreen } from '../MobileFilePreviewScreen' + +import { resetDocumentEditDraftsForTests } from '../../drafts/document-edit-draft-rows' let tree: ReactTestRenderer | null = null @@ -160,33 +159,3 @@ it('keeps an editable dirty draft when a reconnect read returns a truncated file expect(editor().props.value).toBe('My unsaved changes') expect(save().props.disabled).toBe(false) }) - -it.each(['host', 'executionHost'] as const)( - 'does not carry a draft across %s authority with the same workspace and path', - async (authority) => { - serveReads(async () => previewReply('Original')) - const withExecutionHost = - (value: ExecutionHostId | undefined) => (screen: ReturnType) => - createElement(WorkspaceExecutionHostContext.Provider, { value }, screen) - const { rendered, editor, save } = await mountArtifact(withExecutionHost(undefined)) - await act(async () => editor().props.onChangeText('My unsaved changes')) - await act(async () => { - rendered.update( - withExecutionHost(authority === 'executionHost' ? 'runtime:other-server' : undefined)( - createElement(MobileFilePreviewScreen, { - route: { - ...ARTIFACT_ROUTE, - params: { - ...ARTIFACT_ROUTE.params, - hostId: authority === 'host' ? 'host-b' : 'host-a', - grantId: 'grant-b' - } - } - }) - ) - ) - }) - expect(editor().props.value).toBe('Original') - expect(save().props.disabled).toBe(true) - } -) diff --git a/mobile/src/files/mobile-file-preview-route.ts b/mobile/src/files/preview/mobile-file-preview-route.ts similarity index 98% rename from mobile/src/files/mobile-file-preview-route.ts rename to mobile/src/files/preview/mobile-file-preview-route.ts index 7c1b2c401f4..bec6960059d 100644 --- a/mobile/src/files/mobile-file-preview-route.ts +++ b/mobile/src/files/preview/mobile-file-preview-route.ts @@ -1,4 +1,4 @@ -import { translate } from '../i18n/mobile-i18n' +import { translate } from '../../i18n/mobile-i18n' export type MobileFilePreviewParamValue = string | string[] | undefined diff --git a/mobile/src/files/mobile-file-preview-source.ts b/mobile/src/files/preview/mobile-file-preview-source.ts similarity index 100% rename from mobile/src/files/mobile-file-preview-source.ts rename to mobile/src/files/preview/mobile-file-preview-source.ts diff --git a/mobile/src/files/mobile-file-preview-styles.ts b/mobile/src/files/preview/mobile-file-preview-styles.ts similarity index 95% rename from mobile/src/files/mobile-file-preview-styles.ts rename to mobile/src/files/preview/mobile-file-preview-styles.ts index f0d91330251..ac11a88eb0d 100644 --- a/mobile/src/files/mobile-file-preview-styles.ts +++ b/mobile/src/files/preview/mobile-file-preview-styles.ts @@ -1,6 +1,6 @@ import { StyleSheet } from 'react-native' -import { colors, spacing, typography } from '../theme/mobile-theme' -import { themedStyleSheet } from '../theme/themed-styles' +import { colors, spacing, typography } from '../../theme/mobile-theme' +import { themedStyleSheet } from '../../theme/themed-styles' export const filePreviewStyles = themedStyleSheet(() => ({ container: { diff --git a/mobile/src/files/mobile-file-preview-syntax.ts b/mobile/src/files/preview/mobile-file-preview-syntax.ts similarity index 93% rename from mobile/src/files/mobile-file-preview-syntax.ts rename to mobile/src/files/preview/mobile-file-preview-syntax.ts index 5a67f6d7d90..c3f4de4d689 100644 --- a/mobile/src/files/mobile-file-preview-syntax.ts +++ b/mobile/src/files/preview/mobile-file-preview-syntax.ts @@ -2,7 +2,7 @@ import { highlightMobileCode, resolveMobileSyntaxLanguage, type MobileSyntaxSegment -} from './session-documents/mobile-file-syntax' +} from '../session-documents/mobile-file-syntax' export type MobileFilePreviewSyntax = { language: string diff --git a/mobile/src/files/use-mobile-file-preview-back.test.ts b/mobile/src/files/preview/use-mobile-file-preview-back.test.ts similarity index 85% rename from mobile/src/files/use-mobile-file-preview-back.test.ts rename to mobile/src/files/preview/use-mobile-file-preview-back.test.ts index a0ff5d5ad08..f29ff872f40 100644 --- a/mobile/src/files/use-mobile-file-preview-back.test.ts +++ b/mobile/src/files/preview/use-mobile-file-preview-back.test.ts @@ -135,21 +135,4 @@ describe('leaving the file preview', () => { }) // The bundler selects use-back-claim.web.ts for the shell’s Back key; the hook uses one seam on both platforms. - it('registers once across a rebuilt handler, rather than per dependency change', () => { - const leave = vi.fn() - const renderer = render(false, leave) - expect(native.addEventListener).toHaveBeenCalledTimes(1) - expect(native.addEventListener.mock.calls[0]?.[0]).toBe('hardwareBackPress') - act(() => { - renderer.update(createElement(Screen, { hasUnsavedDraft: true, leave })) - }) - expect(native.addEventListener).toHaveBeenCalledTimes(1) - let handled = false - act(() => { - handled = native.addEventListener.mock.calls[0]?.[1]?.() ?? false - }) - expect(handled).toBe(true) - expect(leave).not.toHaveBeenCalled() - expect(held.back?.confirmingDiscard).toBe(true) - }) }) diff --git a/mobile/src/files/use-mobile-file-preview-back.ts b/mobile/src/files/preview/use-mobile-file-preview-back.ts similarity index 96% rename from mobile/src/files/use-mobile-file-preview-back.ts rename to mobile/src/files/preview/use-mobile-file-preview-back.ts index a92d10747c1..ac2f4bed6a8 100644 --- a/mobile/src/files/use-mobile-file-preview-back.ts +++ b/mobile/src/files/preview/use-mobile-file-preview-back.ts @@ -1,5 +1,5 @@ import { useCallback, useState } from 'react' -import { useBackClaim } from '../navigation/use-back-claim' +import { useBackClaim } from '../../navigation/use-back-claim' export type MobileFilePreviewBack = { confirmingDiscard: boolean diff --git a/mobile/src/files/session-documents/MobileSessionFileReader.tsx b/mobile/src/files/session-documents/MobileSessionFileReader.tsx index d87903a4fe1..957963c2f15 100644 --- a/mobile/src/files/session-documents/MobileSessionFileReader.tsx +++ b/mobile/src/files/session-documents/MobileSessionFileReader.tsx @@ -1,5 +1,5 @@ -import { MobileFileMediaPreview } from '../MobileFileMediaPreview' -import type { MobileFilePreviewRpcSender } from '../mobile-file-preview-operations' +import { MobileFileMediaPreview } from '../media/MobileFileMediaPreview' +import type { MobileFilePreviewRpcSender } from '../preview/mobile-file-preview-operations' import { useState, useEffect, useCallback, useMemo } from 'react' import { FlatList, diff --git a/mobile/src/files/file-tab-doc-reply-schema.ts b/mobile/src/files/session-documents/file-tab-doc-reply-schema.ts similarity index 96% rename from mobile/src/files/file-tab-doc-reply-schema.ts rename to mobile/src/files/session-documents/file-tab-doc-reply-schema.ts index 8a9ad61b634..8dcdbdde1dc 100644 --- a/mobile/src/files/file-tab-doc-reply-schema.ts +++ b/mobile/src/files/session-documents/file-tab-doc-reply-schema.ts @@ -1,5 +1,5 @@ import * as z from 'zod' -import { salvagedOptional } from '../../../src/shared/zod-salvage' +import { salvagedOptional } from '../../../../src/shared/zod-salvage' // Tabs publish members directly into ready documents; their readers must be stricter than the preview screen’s guarded normalizers. diff --git a/mobile/src/files/mobile-diff-image-preview.ts b/mobile/src/files/session-documents/mobile-diff-image-preview.ts similarity index 91% rename from mobile/src/files/mobile-diff-image-preview.ts rename to mobile/src/files/session-documents/mobile-diff-image-preview.ts index 41599515a67..477a0fd3eaa 100644 --- a/mobile/src/files/mobile-diff-image-preview.ts +++ b/mobile/src/files/session-documents/mobile-diff-image-preview.ts @@ -1,4 +1,4 @@ -import { buildImageDataUri } from '../../../src/shared/file-preview/image-data-uri' +import { buildImageDataUri } from '../../../../src/shared/file-preview/image-data-uri' // Only modifiedDeleted proves deletion; read failures can also return empty modifiedContent with modifiedIsBinary false. // Omit kind: the reply reader admits every non-text arm, including unknown host arms. diff --git a/mobile/src/files/mobile-file-tab-doc-operations.ts b/mobile/src/files/session-documents/mobile-file-tab-doc-operations.ts similarity index 92% rename from mobile/src/files/mobile-file-tab-doc-operations.ts rename to mobile/src/files/session-documents/mobile-file-tab-doc-operations.ts index aef0cb50328..c25926fe980 100644 --- a/mobile/src/files/mobile-file-tab-doc-operations.ts +++ b/mobile/src/files/session-documents/mobile-file-tab-doc-operations.ts @@ -1,5 +1,8 @@ -import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' -import { rpcResultVariant, rpcResultVariants } from '../transport/rpc/rpc-operation-result-reader' +import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' +import { + rpcResultVariant, + rpcResultVariants +} from '../../transport/rpc/rpc-operation-result-reader' import { fileTabBinaryDiffSchema, fileTabImageSchema, diff --git a/mobile/src/files/mobile-file-tab-doc.ts b/mobile/src/files/session-documents/mobile-file-tab-doc.ts similarity index 91% rename from mobile/src/files/mobile-file-tab-doc.ts rename to mobile/src/files/session-documents/mobile-file-tab-doc.ts index 1efca7607b1..70f82ab3db2 100644 --- a/mobile/src/files/mobile-file-tab-doc.ts +++ b/mobile/src/files/session-documents/mobile-file-tab-doc.ts @@ -1,10 +1,10 @@ -import { mobileFileMedia, type MobileFileMedia } from './mobile-file-media' -import { buildImageDataUri } from '../../../src/shared/file-preview/image-data-uri' -import { classifyMobileArtifact } from './mobile-artifact-kind' +import { mobileFileMedia, type MobileFileMedia } from '../media/mobile-file-media' +import { buildImageDataUri } from '../../../../src/shared/file-preview/image-data-uri' +import { classifyMobileArtifact } from '../preview/mobile-artifact-kind' import { buildMobileDiffLines, type MobileDiffLine -} from '../source-control/review/mobile-diff-lines' +} from '../../source-control/review/mobile-diff-lines' import { mobileDiffImageDataUri } from './mobile-diff-image-preview' import { fileTabDiffRead, diff --git a/mobile/src/files/session-documents/mobile-file-tap-open.ts b/mobile/src/files/session-documents/mobile-file-tap-open.ts index f08eed88cad..52a8dc84a37 100644 --- a/mobile/src/files/session-documents/mobile-file-tap-open.ts +++ b/mobile/src/files/session-documents/mobile-file-tap-open.ts @@ -2,10 +2,10 @@ import type { ParsedFileLinkLocation } from '../../../../src/shared/file-links/f import type { RuntimeNativeChatFileContext } from '../../../../src/shared/native-chat/native-chat-file-context' import type { RuntimeTerminalPathResolution } from '../../../../src/shared/terminal/runtime-terminal-file-path' import { filesystemPathToFileUri } from '../../../../src/shared/file-links/file-uri-path' -import { createMobileFilePreviewHref } from '../mobile-file-preview-route' -import { classifyMobileArtifact } from '../mobile-artifact-kind' +import { createMobileFilePreviewHref } from '../preview/mobile-file-preview-route' +import { classifyMobileArtifact } from '../preview/mobile-artifact-kind' import { fileTapOpenRun } from './mobile-file-tap-operations' -import { terminalArtifactPathResolve as fileTapPathResolve } from '../mobile-file-preview-operations' +import { terminalArtifactPathResolve as fileTapPathResolve } from '../preview/mobile-file-preview-operations' import { mapWithConcurrency } from '../../../../src/shared/map-with-concurrency' import { shouldActivateOpenedMobileSessionTab, diff --git a/mobile/src/files/session-documents/mobile-file-tap-operations.ts b/mobile/src/files/session-documents/mobile-file-tap-operations.ts index b150abc6d3e..36b4b26c4da 100644 --- a/mobile/src/files/session-documents/mobile-file-tap-operations.ts +++ b/mobile/src/files/session-documents/mobile-file-tap-operations.ts @@ -1,6 +1,6 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' import { rpcResultVariant } from '../../transport/rpc/rpc-operation-result-reader' -import '../mobile-file-preview-operations' +import '../preview/mobile-file-preview-operations' import { fileTapOpenedSchema } from './mobile-file-tap-reply-schema' import '../../agent-history/mobile-ai-vault-resume-reply-schema' import '../../browser/tab-navigation/mobile-session-browser-reply-schema' diff --git a/mobile/src/files/session-documents/mobile-markdown-reader-status.ts b/mobile/src/files/session-documents/mobile-markdown-reader-status.ts index e238cda5922..0b5958e531e 100644 --- a/mobile/src/files/session-documents/mobile-markdown-reader-status.ts +++ b/mobile/src/files/session-documents/mobile-markdown-reader-status.ts @@ -1,5 +1,5 @@ import { translate } from '../../i18n/mobile-i18n' -import { formatPreviewByteLength } from '../mobile-file-preview-response' +import { formatPreviewByteLength } from '../preview/mobile-file-preview-response' import type { MarkdownDocState } from './session-document-state' export function markdownReaderStatusText( diff --git a/mobile/src/files/session-documents/mobile-markdown-tab-operations.ts b/mobile/src/files/session-documents/mobile-markdown-tab-operations.ts index cb863e4277a..7683a7d25d4 100644 --- a/mobile/src/files/session-documents/mobile-markdown-tab-operations.ts +++ b/mobile/src/files/session-documents/mobile-markdown-tab-operations.ts @@ -1,6 +1,6 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' import { rpcResultVariant } from '../../transport/rpc/rpc-operation-result-reader' -import '../mobile-file-preview-operations' +import '../preview/mobile-file-preview-operations' import { sessionLaunchUnreadReplySchema } from '../../transport/rpc/rpc-uninterpreted-reply-schemas' import '../../agent-history/mobile-ai-vault-resume-reply-schema' import '../../browser/tab-navigation/mobile-session-browser-reply-schema' diff --git a/mobile/src/files/session-documents/mobile-session-document-read-settlement.ts b/mobile/src/files/session-documents/mobile-session-document-read-settlement.ts index 39d21b702e7..af20065d729 100644 --- a/mobile/src/files/session-documents/mobile-session-document-read-settlement.ts +++ b/mobile/src/files/session-documents/mobile-session-document-read-settlement.ts @@ -1,4 +1,4 @@ -import type { MobileFileMedia } from '../mobile-file-media' +import type { MobileFileMedia } from '../media/mobile-file-media' import type { MobileDiffLine } from '../../source-control/review/mobile-diff-lines' import type { FileDocState, MarkdownDocState, ReadyFileDocState } from './session-document-state' diff --git a/mobile/src/files/session-documents/session-document-state.ts b/mobile/src/files/session-documents/session-document-state.ts index 84574cdc51a..feb12e4cec3 100644 --- a/mobile/src/files/session-documents/session-document-state.ts +++ b/mobile/src/files/session-documents/session-document-state.ts @@ -1,4 +1,4 @@ -import type { MobileFileMedia } from '../mobile-file-media' +import type { MobileFileMedia } from '../media/mobile-file-media' import type { MobileDiffLine } from '../../source-control/review/mobile-diff-lines' import type { MobileSyntaxSegment } from './mobile-file-syntax' diff --git a/mobile/src/files/session-documents/use-mobile-file-tap-handlers.ts b/mobile/src/files/session-documents/use-mobile-file-tap-handlers.ts index 6e455fdbb86..5718a80e123 100644 --- a/mobile/src/files/session-documents/use-mobile-file-tap-handlers.ts +++ b/mobile/src/files/session-documents/use-mobile-file-tap-handlers.ts @@ -1,5 +1,5 @@ import { isCommittedChatAttachmentCandidate } from '../../../../src/shared/native-chat-text-attachment-references' -import { createMobileFilePreviewHref } from '../mobile-file-preview-route' +import { createMobileFilePreviewHref } from '../preview/mobile-file-preview-route' import { useCallback, useLayoutEffect, useRef, type MutableRefObject } from 'react' import type { ParsedFileLinkLocation } from '../../../../src/shared/file-links/file-link-location' import { hostStackRouteHref } from '../../navigation/host-stack-navigation' diff --git a/mobile/src/files/session-documents/use-mobile-session-document-readers.ts b/mobile/src/files/session-documents/use-mobile-session-document-readers.ts index e89617b35d5..4aaf0fe8e44 100644 --- a/mobile/src/files/session-documents/use-mobile-session-document-readers.ts +++ b/mobile/src/files/session-documents/use-mobile-session-document-readers.ts @@ -1,10 +1,10 @@ import { useCallback, useEffect, useRef } from 'react' import type { RpcFailure } from '../../transport/rpc/rpc-wire-types' import { translate } from '../../i18n/mobile-i18n' -import { resolveMobileFileTabDoc } from '../mobile-file-tab-doc' +import { resolveMobileFileTabDoc } from './mobile-file-tab-doc' import { shouldIgnoreTrimWhitespace } from '../../../../src/shared/diff-whitespace' import { useMobileSourceControlSettings } from '../../source-control/mobile-source-control-settings' -import { filePreviewTextRead } from '../mobile-file-preview-operations' +import { filePreviewTextRead } from '../preview/mobile-file-preview-operations' import { markdownTabRead } from './mobile-markdown-tab-operations' import { buildMarkdownDiskFallbackDoc, diff --git a/mobile/src/files/MobileFilePreviewEditDraft.tsx b/mobile/src/files/terminal-artifacts/MobileFilePreviewEditDraft.tsx similarity index 71% rename from mobile/src/files/MobileFilePreviewEditDraft.tsx rename to mobile/src/files/terminal-artifacts/MobileFilePreviewEditDraft.tsx index bea1c2ab81b..6e16aa7f29d 100644 --- a/mobile/src/files/MobileFilePreviewEditDraft.tsx +++ b/mobile/src/files/terminal-artifacts/MobileFilePreviewEditDraft.tsx @@ -1,16 +1,16 @@ -import { fileEditDraftKey } from '../drafts/document-edit-draft-rows' -import { DocumentEditDraftOfferBar } from '../drafts/DocumentEditDraftOfferBar' +import { fileEditDraftKey } from '../../drafts/document-edit-draft-rows' +import { DocumentEditDraftOfferBar } from '../../drafts/DocumentEditDraftOfferBar' import { useDocumentEditDraft, type DocumentEditDraftOffer -} from '../drafts/use-document-edit-draft' -import { useClipboardWriter } from '../platform/clipboard' -import { triggerError, triggerSuccess } from '../platform/haptics' -import { useKeyboardOcclusion } from '../platform/keyboard-occlusion' -import { spacing } from '../theme/mobile-theme' +} from '../../drafts/use-document-edit-draft' +import { useClipboardWriter } from '../../platform/clipboard' +import { triggerError, triggerSuccess } from '../../platform/haptics' +import { useKeyboardOcclusion } from '../../platform/keyboard-occlusion' +import { spacing } from '../../theme/mobile-theme' import { useSafeAreaInsets } from 'react-native-safe-area-context' -import type { MobileFilePreviewSource } from './mobile-file-preview-request' -import { resolveMarkdownFloatingActionsBottom } from './session-documents/markdown-floating-actions-layout' +import type { MobileFilePreviewSource } from '../preview/mobile-file-preview-request' +import { resolveMarkdownFloatingActionsBottom } from '../session-documents/markdown-floating-actions-layout' /** Only terminal artifacts are editable in the file preview. */ export function filePreviewEditDraftKey( diff --git a/mobile/src/files/mobile-file-preview-editability.ts b/mobile/src/files/terminal-artifacts/mobile-file-preview-editability.ts similarity index 89% rename from mobile/src/files/mobile-file-preview-editability.ts rename to mobile/src/files/terminal-artifacts/mobile-file-preview-editability.ts index 14313e813df..50c1ba5bca7 100644 --- a/mobile/src/files/mobile-file-preview-editability.ts +++ b/mobile/src/files/terminal-artifacts/mobile-file-preview-editability.ts @@ -1,7 +1,5 @@ -import type { - MobileFilePreviewResult, - MobileFilePreviewSource -} from './mobile-file-preview-request' +import type { MobileFilePreviewSource } from '../preview/mobile-file-preview-request' +import type { MobileFilePreviewResult } from '../preview/mobile-file-preview-response' export function isEditableMobileTerminalArtifactPreview( preview: MobileFilePreviewResult, diff --git a/mobile/src/files/mobile-terminal-artifact-grant-refresh.ts b/mobile/src/files/terminal-artifacts/mobile-terminal-artifact-grant-refresh.ts similarity index 93% rename from mobile/src/files/mobile-terminal-artifact-grant-refresh.ts rename to mobile/src/files/terminal-artifacts/mobile-terminal-artifact-grant-refresh.ts index c3ac115c907..ea3de1e2c37 100644 --- a/mobile/src/files/mobile-terminal-artifact-grant-refresh.ts +++ b/mobile/src/files/terminal-artifacts/mobile-terminal-artifact-grant-refresh.ts @@ -1,9 +1,9 @@ -import type { RuntimeNativeChatFileContext } from '../../../src/shared/native-chat/native-chat-file-context' -import type { RpcFailure } from '../transport/rpc/rpc-wire-types' +import type { RuntimeNativeChatFileContext } from '../../../../src/shared/native-chat/native-chat-file-context' +import type { RpcFailure } from '../../transport/rpc/rpc-wire-types' import { terminalArtifactPathResolve, type MobileFilePreviewRpcSender -} from './mobile-file-preview-operations' +} from '../preview/mobile-file-preview-operations' import { isTerminalArtifactGrantError } from './terminal-artifact-grant-error' export type MobileTerminalArtifactPreviewSource = { diff --git a/mobile/src/files/terminal-artifact-grant-error.ts b/mobile/src/files/terminal-artifacts/terminal-artifact-grant-error.ts similarity index 100% rename from mobile/src/files/terminal-artifact-grant-error.ts rename to mobile/src/files/terminal-artifacts/terminal-artifact-grant-error.ts diff --git a/mobile/src/home/MobileHomeScreen.tsx b/mobile/src/home/MobileHomeScreen.tsx index 048b43c3ee9..f860dd48ea2 100644 --- a/mobile/src/home/MobileHomeScreen.tsx +++ b/mobile/src/home/MobileHomeScreen.tsx @@ -26,7 +26,7 @@ import { resolveHomeHostConnectionState } from '../transport/hosts/home-host-aut import { removeHostAndCloseClient } from '../host-catalog/host-removal-lifecycle' import { loadHostCatalog } from '../host-catalog/host-store' import type { HostCatalogEntry, HostProfile } from '../host-catalog/host-profile' -import { useOpenMobileHostEdit } from '../host-screen/use-open-mobile-host-edit' +import { useOpenMobileHostEdit } from '../host-screen/identity/use-open-mobile-host-edit' import { mobileConnectionPathLabel } from '../transport/connection/mobile-connection-path-label' import { newWorkspaceOverlayHref } from '../new-workspace/new-workspace-overlay-route' import { useMobileHomeData } from './use-mobile-home-data' diff --git a/mobile/src/home/home-query-restore.ts b/mobile/src/home/home-query-restore.ts index 67f3cda09b4..9c6760408dc 100644 --- a/mobile/src/home/home-query-restore.ts +++ b/mobile/src/home/home-query-restore.ts @@ -5,7 +5,7 @@ import { HOST_LIST_TARGET, homeWorktreeCountsKey, NO_HOST_LIST_INPUT -} from '../host-screen/host-list-queries' +} from '../host-screen/catalog/host-list-queries' import { hostObserverOptions } from '../query/host-query' import { accountsSnapshotFamily, accountsSnapshotKey } from '../accounts/accounts-snapshot-stream' import { persistedHostQueryKey } from '../query/host-query-persistence' diff --git a/mobile/src/home/use-mobile-home-data.ts b/mobile/src/home/use-mobile-home-data.ts index 86db717735c..c5cbdb23736 100644 --- a/mobile/src/home/use-mobile-home-data.ts +++ b/mobile/src/home/use-mobile-home-data.ts @@ -21,7 +21,7 @@ import { HOST_LIST_TARGET, homeWorktreeCountsKey, NO_HOST_LIST_INPUT -} from '../host-screen/host-list-queries' +} from '../host-screen/catalog/host-list-queries' import { useNow } from '../hooks/use-now' import { loadMobileOnboardingSteps, diff --git a/mobile/src/host-screen/host-list-queries.ts b/mobile/src/host-screen/catalog/host-list-queries.ts similarity index 93% rename from mobile/src/host-screen/host-list-queries.ts rename to mobile/src/host-screen/catalog/host-list-queries.ts index 08c966625e2..940935375bb 100644 --- a/mobile/src/host-screen/host-list-queries.ts +++ b/mobile/src/host-screen/catalog/host-list-queries.ts @@ -3,32 +3,32 @@ import { getRepoExecutionHostId, parseExecutionHostId, type ExecutionHostId -} from '../../../src/shared/execution-host' -import { MOBILE_RELAY_HOST_WORKTREE_DELTAS_RUNTIME_CAPABILITY } from '../../../src/shared/mobile-desktop-relay-contract' -import type { MobileRelayHost } from '../../../src/shared/remote-runtime/relay/mobile-relay-hosts-contract' +} from '../../../../src/shared/execution-host' +import { MOBILE_RELAY_HOST_WORKTREE_DELTAS_RUNTIME_CAPABILITY } from '../../../../src/shared/mobile-desktop-relay-contract' +import type { MobileRelayHost } from '../../../../src/shared/remote-runtime/relay/mobile-relay-hosts-contract' import type { Query, QueryClient } from '@tanstack/react-query' -import { defineHostQuery } from '../query/host-query' -import { hostQueryKey, type HostQueryTarget } from '../query/host-query-key' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' +import { defineHostQuery } from '../../query/host-query' +import { hostQueryKey, type HostQueryTarget } from '../../query/host-query-key' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import { applyServerWorkspaces, fetchServerWorkspaces, NO_SERVER_WORKSPACES, ServerRowSnapshots, type ServerWorkspaces -} from '../worktree/server-workspaces' +} from '../../worktree/server-workspaces' import { readWorktreeCatalogSnapshot, type WorktreeCatalogSnapshot -} from '../worktree/worktree-catalog-snapshot-client' -import type { Worktree } from '../worktree/workspace-list-types' +} from '../../worktree/worktree-catalog-snapshot-client' +import type { Worktree } from '../../worktree/workspace-list-types' import { hostRepoCatalogRepos, hostSshTargetSummarySchema, type HostRepoCatalogRow -} from './host-screen-reply-schema' -import { hostPlatformRead, hostRepoCatalogSnapshotRead } from './host-screen-operations' -import { NODE_PLATFORM_NAMES } from '../transport/connection/mobile-runtime-host-platform' +} from '../host-screen-reply-schema' +import { hostPlatformRead, hostRepoCatalogSnapshotRead } from '../host-screen-operations' +import { NODE_PLATFORM_NAMES } from '../../transport/connection/mobile-runtime-host-platform' import { acceptedMetadata, readHostSshTargetLabels, diff --git a/mobile/src/host-screen/host-repo-looks.ts b/mobile/src/host-screen/catalog/host-repo-looks.ts similarity index 88% rename from mobile/src/host-screen/host-repo-looks.ts rename to mobile/src/host-screen/catalog/host-repo-looks.ts index be34ac661e9..b096d1a88a6 100644 --- a/mobile/src/host-screen/host-repo-looks.ts +++ b/mobile/src/host-screen/catalog/host-repo-looks.ts @@ -1,7 +1,7 @@ import { useState } from 'react' -import type { SectionRepo } from '../worktree/workspace-list-types' -import { sectionRepoColor } from '../worktree/workspace-filter-repos' -import { readMobileRepoIcon, type MobileHostRepoIcon } from './host-screen-reply-schema' +import type { SectionRepo } from '../../worktree/workspace-list-types' +import { sectionRepoColor } from '../../worktree/workspace-filter-repos' +import { readMobileRepoIcon, type MobileHostRepoIcon } from '../host-screen-reply-schema' export type RepoLook = { color: string; icon: MobileHostRepoIcon | null } diff --git a/mobile/src/host-screen/host-ssh-target-labels-read.ts b/mobile/src/host-screen/catalog/host-ssh-target-labels-read.ts similarity index 74% rename from mobile/src/host-screen/host-ssh-target-labels-read.ts rename to mobile/src/host-screen/catalog/host-ssh-target-labels-read.ts index a1efa773ecb..93e391da0b1 100644 --- a/mobile/src/host-screen/host-ssh-target-labels-read.ts +++ b/mobile/src/host-screen/catalog/host-ssh-target-labels-read.ts @@ -1,10 +1,11 @@ -import { isHostNotConnectedError } from '../query/host-query-errors' -import { optionalSettingsRead } from '../settings/mobile-settings-read-operations' -import type { RpcAcceptedResult } from '../transport/rpc/rpc-accepted-result' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import type { RpcResponse } from '../transport/rpc/rpc-wire-types' -import { buildHostLabelById } from '../worktree/worktree-host-context-labels' -import { hostSshTargetSummariesRead } from './host-screen-operations' +import { isHostNotConnectedError } from '../../query/host-query-errors' +import { optionalSettingsRead } from '../../settings/mobile-settings-read-operations' +import type { RpcAcceptedResult } from '../../transport/rpc/rpc-accepted-result' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import type { RpcResponse } from '../../transport/rpc/rpc-wire-types' +import '../../worktree/worktree-host-context-labels' +import { buildHostLabelById } from '../../../../src/shared/worktree/host-context-labels' +import { hostSshTargetSummariesRead } from '../host-screen-operations' export async function settledMetadataReply( send: () => Promise diff --git a/mobile/src/host-screen/use-host-project-sections.ts b/mobile/src/host-screen/catalog/use-host-project-sections.ts similarity index 81% rename from mobile/src/host-screen/use-host-project-sections.ts rename to mobile/src/host-screen/catalog/use-host-project-sections.ts index 37337359f61..1b21e70269e 100644 --- a/mobile/src/host-screen/use-host-project-sections.ts +++ b/mobile/src/host-screen/catalog/use-host-project-sections.ts @@ -1,19 +1,21 @@ import { useEffect } from 'react' import * as z from 'zod' -import type { ProjectGroup } from '../../../src/shared/project-group-types' -import { ProjectSectionCatalogSchema } from '../../../src/shared/project-section-catalog-schema' -import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../src/shared/sidebar-inputs-switch' -import { defineHostQuery, useHostQuery } from '../query/host-query' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' +import type { ProjectGroup } from '../../../../src/shared/project-group-types' +import { + ProjectSectionCatalogSchema, + type ServedProjectSections +} from '../../../../src/shared/project-section-catalog-schema' +import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../../src/shared/sidebar-inputs-switch' +import { defineHostQuery, useHostQuery } from '../../query/host-query' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import { hostProjectGroupsRead, projectSectionsRead, - readServedProjectSections, - type ServedProjectSections -} from '../worktree/project-sections-operations' -import { readHostMethodReply } from './host-sidebar-feed-operations' + readServedProjectSections +} from '../../worktree/project-sections-operations' +import { readHostMethodReply } from '../sidebar-feed/host-sidebar-feed-operations' import { HOST_LIST_TARGET, NO_HOST_LIST_INPUT } from './host-list-queries' -import type { HostScreenState } from './use-host-screen-state' +import type { HostScreenState } from '../use-host-screen-state' type HostProjectSections = { sections: ServedProjectSections | null diff --git a/mobile/src/host-screen/use-host-repo-icon-data-urls.ts b/mobile/src/host-screen/catalog/use-host-repo-icon-data-urls.ts similarity index 87% rename from mobile/src/host-screen/use-host-repo-icon-data-urls.ts rename to mobile/src/host-screen/catalog/use-host-repo-icon-data-urls.ts index d6552f1ec61..933a4a2efd5 100644 --- a/mobile/src/host-screen/use-host-repo-icon-data-urls.ts +++ b/mobile/src/host-screen/catalog/use-host-repo-icon-data-urls.ts @@ -1,7 +1,10 @@ import { useEffect, useRef, useState } from 'react' -import { MAX_REPO_ICON_IDS_PER_READ } from '../../../src/shared/sidebar-list/repo-icon-data-urls-contract' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import { readHostMethodReply, sidebarRepoIconsRead } from './host-sidebar-feed-operations' +import { MAX_REPO_ICON_IDS_PER_READ } from '../../../../src/shared/sidebar-list/repo-icon-data-urls-contract' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import { + readHostMethodReply, + sidebarRepoIconsRead +} from '../sidebar-feed/host-sidebar-feed-operations' const NO_DATA_URLS: ReadonlyMap = new Map() diff --git a/mobile/src/host-screen/use-host-repo-metadata.ts b/mobile/src/host-screen/catalog/use-host-repo-metadata.ts similarity index 80% rename from mobile/src/host-screen/use-host-repo-metadata.ts rename to mobile/src/host-screen/catalog/use-host-repo-metadata.ts index 2d306455441..ec77527a46d 100644 --- a/mobile/src/host-screen/use-host-repo-metadata.ts +++ b/mobile/src/host-screen/catalog/use-host-repo-metadata.ts @@ -1,10 +1,10 @@ import { useEffect } from 'react' -import { getRepoExecutionHostId } from '../../../src/shared/execution-host' -import { useHostQuery } from '../query/host-query' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import { buildRepoHostIdByRepoId } from '../worktree/worktree-host-context-labels' -import { buildSshHostHealthById } from '../worktree/worktree-host-health' -import type { SectionRepo } from '../worktree/workspace-list-types' +import { getRepoExecutionHostId } from '../../../../src/shared/execution-host' +import { useHostQuery } from '../../query/host-query' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import { buildRepoHostIdByRepoId } from '../../worktree/worktree-host-context-labels' +import { buildSshHostHealthById } from '../../worktree/worktree-host-health' +import type { SectionRepo } from '../../worktree/workspace-list-types' import { catalogNeedsHostLabels, HOST_LIST_TARGET, @@ -12,8 +12,8 @@ import { hostRepoLabelsQuery, NO_HOST_LIST_INPUT } from './host-list-queries' -import type { HostRepoCatalogRow } from './host-screen-reply-schema' -import type { HostScreenState } from './use-host-screen-state' +import type { HostRepoCatalogRow } from '../host-screen-reply-schema' +import type { HostScreenState } from '../use-host-screen-state' function desktopSectionRepo(repo: HostRepoCatalogRow): SectionRepo { return { diff --git a/mobile/src/host-screen/use-host-worktree-catalog.ts b/mobile/src/host-screen/catalog/use-host-worktree-catalog.ts similarity index 86% rename from mobile/src/host-screen/use-host-worktree-catalog.ts rename to mobile/src/host-screen/catalog/use-host-worktree-catalog.ts index edbad9c48fc..6a1664ebec5 100644 --- a/mobile/src/host-screen/use-host-worktree-catalog.ts +++ b/mobile/src/host-screen/catalog/use-host-worktree-catalog.ts @@ -1,33 +1,33 @@ -import { MOBILE_RELAY_HOSTS_CHANGED_EVENT_RUNTIME_CAPABILITY } from '../../../src/shared/mobile-desktop-relay-contract' -import { WORKTREE_ROWS_CHANGED_EVENT_RUNTIME_CAPABILITY } from '../../../src/shared/worktree-runtime-capabilities' -import { readWorktreeFirstPage } from '../worktree/worktree-catalog-first-page' -import type { Worktree } from '../worktree/workspace-list-types' +import { MOBILE_RELAY_HOSTS_CHANGED_EVENT_RUNTIME_CAPABILITY } from '../../../../src/shared/mobile-desktop-relay-contract' +import { WORKTREE_ROWS_CHANGED_EVENT_RUNTIME_CAPABILITY } from '../../../../src/shared/worktree-runtime-capabilities' +import { readWorktreeFirstPage } from '../../worktree/worktree-catalog-first-page' +import type { Worktree } from '../../worktree/workspace-list-types' import { useCallback, useEffect, useRef, useState } from 'react' import { AppState } from 'react-native' -import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../src/shared/sidebar-inputs-switch' +import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../../src/shared/sidebar-inputs-switch' import { useFocusEffect } from 'expo-router' import { useQueryClient } from '@tanstack/react-query' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import type { ConnectionState } from '../transport/connection/connection-state' -import { RpcIncompatibleReplyError } from '../transport/rpc/rpc-incompatible-reply-error' -import { startHostWorktreeRefresh } from '../worktree/host-worktree-refresh' -import type { HostSidebarFeedTriggers } from './host-sidebar-feed-connection' -import { areWorktreeListsEqual } from '../worktree/worktree-list-snapshot' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import type { ConnectionState } from '../../transport/connection/connection-state' +import { RpcIncompatibleReplyError } from '../../transport/rpc/rpc-incompatible-reply-error' +import { startHostWorktreeRefresh } from '../../worktree/host-worktree-refresh' +import type { HostSidebarFeedTriggers } from '../sidebar-feed/host-sidebar-feed-connection' +import { areWorktreeListsEqual } from '../../worktree/worktree-list-snapshot' import { clearConfirmedActiveWorktreeIdentity, retainLiveSleptWorktreeIdentities -} from '../worktree/worktree-host-row-identity' -import { savePinnedIds } from '../storage/preferences' -import type { HostScreenState } from './use-host-screen-state' -import { relaysToServers } from '../worktree/execution-host-scoped-rpc-client' -import { NO_SERVER_WORKSPACES } from '../worktree/server-workspaces' -import { useRelayHosts } from '../worktree/relay-hosts-feed' -import { WorktreeCatalogReadError } from '../worktree/worktree-catalog-snapshot-client' -import { useHostQuery } from '../query/host-query' -import { hostReadClient } from '../query/host-read-client' -import { hostScopeKey } from '../query/host-query-key' -import { isHostNotConnectedError } from '../query/host-query-errors' -import { refetchHostQueryAfterInFlight } from '../query/host-event-router' +} from '../../worktree/worktree-host-row-identity' +import { savePinnedIds } from '../../storage/preferences' +import type { HostScreenState } from '../use-host-screen-state' +import { relaysToServers } from '../../worktree/execution-host-scoped-rpc-client' +import { NO_SERVER_WORKSPACES } from '../../worktree/server-workspaces' +import { useRelayHosts } from '../../worktree/relay-hosts-feed' +import { WorktreeCatalogReadError } from '../../worktree/worktree-catalog-snapshot-client' +import { useHostQuery } from '../../query/host-query' +import { hostReadClient } from '../../query/host-read-client' +import { hostScopeKey } from '../../query/host-query-key' +import { isHostNotConnectedError } from '../../query/host-query-errors' +import { refetchHostQueryAfterInFlight } from '../../query/host-event-router' import { isHostListQuery, HOST_LIST_BACKSTOP_POLL_MS, diff --git a/mobile/src/host-screen/host-edit-navigation.ts b/mobile/src/host-screen/host-edit-navigation.ts deleted file mode 100644 index c1e667c5ca2..00000000000 --- a/mobile/src/host-screen/host-edit-navigation.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - hostStackHostRoute, - navigateToHostStackRoute, - type HostStackHostRoute, - type HostStackNavigationController, - type HostStackNavigationState, - type HostStackRootNavigation, - type HostStackRouteTarget, - type HostStackRouter -} from '../navigation/host-stack-navigation' - -export type MobileHostEditHostRoute = HostStackHostRoute -export type MobileHostEditNavigationState = HostStackNavigationState -export type MobileHostEditRootNavigation = HostStackRootNavigation -export type MobileHostEditRouter = HostStackRouter -export type MobileHostEditNavigationController = HostStackNavigationController - -export function mobileHostEditHostRoute(hostId: string): MobileHostEditHostRoute { - return hostStackHostRoute(hostId) -} - -export function mobileHostEditRouteTarget(hostId: string): HostStackRouteTarget { - return { - name: '[hostId]/edit', - params: { hostId } - } -} - -export function navigateToMobileHostEdit( - navigation: MobileHostEditRootNavigation, - router: MobileHostEditRouter, - hostId: string -): MobileHostEditNavigationController { - return navigateToHostStackRoute(navigation, router, hostId, mobileHostEditRouteTarget(hostId)) -} diff --git a/mobile/src/host-screen/host-screen-overlays.tsx b/mobile/src/host-screen/host-screen-overlays.tsx index ba7f8977bc7..0526700d08f 100644 --- a/mobile/src/host-screen/host-screen-overlays.tsx +++ b/mobile/src/host-screen/host-screen-overlays.tsx @@ -12,12 +12,13 @@ import { ConfirmModal } from '../components/ConfirmModal' import { OrcaHookTrustDrawer } from '../components/OrcaHookTrustDrawer' import { NewWorktreeModalController } from '../components/NewWorktreeModalController' import { hostNewWorktreeSessionRoute } from '../host-new-worktree-session-route' -import { isWorktreePinned, type Worktree } from '../worktree/workspace-list-sections' +import { isWorktreePinned } from '../worktree/workspace-list-sections' +import type { Worktree } from '../worktree/workspace-list-types' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' import { hostScreenStyles as styles } from './host-screen-styles' -import { WorkspaceOptionsSheet } from './workspace-options-sheet' -import { WorkspaceDetailsSheet } from './workspace-details-sheet' -import { WorkspaceRenameSheet } from './workspace-rename-sheet' +import { WorkspaceOptionsSheet } from './workspace-options/workspace-options-sheet' +import { WorkspaceDetailsSheet } from './workspace-actions/workspace-details-sheet' +import { WorkspaceRenameSheet } from './workspace-actions/workspace-rename-sheet' import type { HostScreenController } from './use-host-screen-controller' import { useMobileTranslation } from '../i18n/use-mobile-translation' diff --git a/mobile/src/host-screen/host-screen-reply-schema.test.ts b/mobile/src/host-screen/host-screen-reply-schema.test.ts deleted file mode 100644 index e6aba400135..00000000000 --- a/mobile/src/host-screen/host-screen-reply-schema.test.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - hostPlatformSchema, - hostRepoCatalogSchema, - hostSshTargetSummariesSchema, - hostViewSettingsSchema -} from './host-screen-reply-schema' - -describe('host screen reply schemas', () => { - it('requires the repo list and drops a row no map could key', () => { - expect(hostRepoCatalogSchema.safeParse({}).success).toBe(false) - const parsed = hostRepoCatalogSchema.parse({ - repos: [{ id: 'r1', displayName: 'orca' }, { id: 'r2' }, { displayName: 'ghost' }] - }) - expect(parsed.map((repo) => repo.id)).toEqual(['r1']) - }) - - it('salvages badgeColor onto the generated swatch main fell back to', () => { - expect( - hostRepoCatalogSchema.parse({ repos: [{ id: 'r', displayName: 'o', badgeColor: 3 }] })[0] - ?.badgeColor - ).toBeUndefined() - }) - - it('keeps a known repo icon arm and degrades an unknown one to absent', () => { - const known = hostRepoCatalogSchema.parse({ - repos: [{ id: 'r', displayName: 'o', repoIcon: { type: 'emoji', emoji: 'x' } }] - }) - expect(known[0]?.repoIcon).toEqual({ type: 'emoji', emoji: 'x' }) - const unknown = hostRepoCatalogSchema.parse({ - repos: [{ id: 'r', displayName: 'o', repoIcon: { type: 'svg', markup: '' } }] - }) - // Unknown icon arms retain the row and use MobileRepoIcon’s Folder default. - expect(unknown[0]?.id).toBe('r') - expect(unknown[0]?.repoIcon).toBeUndefined() - }) - - it('keeps an image icon whose source this build has never heard of', () => { - const [repo] = hostRepoCatalogSchema.parse({ - repos: [ - { - id: 'r', - displayName: 'o', - repoIcon: { - type: 'image', - src: 'https://example.invalid/a.png', - source: 'gitlab', - label: 'acme/orca' - } - } - ] - }) - // MobileRepoIcon ignores source; constraining it would drop images from newer hosts for a member the renderer never reads. - expect(repo?.repoIcon).toEqual({ - type: 'image', - src: 'https://example.invalid/a.png', - source: 'gitlab', - label: 'acme/orca' - }) - }) - - it('passes a host-id spelling through for getRepoExecutionHostId to judge', () => { - const [repo] = hostRepoCatalogSchema.parse({ - repos: [{ id: 'r', displayName: 'o', executionHostId: 'cloud:zone-a', connectionId: null }] - }) - expect(repo?.executionHostId).toBe('cloud:zone-a') - expect(repo?.connectionId).toBeNull() - }) - - it('degrades an unreadable ssh target list to the empty one that falls back to host ids', () => { - expect(hostSshTargetSummariesSchema.parse({})).toEqual([]) - expect(hostSshTargetSummariesSchema.parse({ targets: 'none' })).toEqual([]) - expect( - hostSshTargetSummariesSchema.parse({ - targets: [{ id: 't', label: 'T' }, { id: 'u' }, { label: 'V' }] - }) - ).toEqual([{ id: 't', label: 'T' }]) - }) - - it('keeps SSH lifecycle fields raw and drops a malformed one without losing the target', () => { - expect( - hostSshTargetSummariesSchema.parse({ - targets: [ - { id: 't', label: 'T', connected: false, connectionStatus: 'hibernating' }, - { id: 'u', label: 'U', connected: 'no', connectionStatus: 7 } - ] - }) - ).toEqual([ - { id: 't', label: 'T', connected: false, connectionStatus: 'hibernating' }, - { id: 'u', label: 'U' } - ]) - }) - - it('answers the empty target list for a reply that is no object at all', () => { - // Label writes precede platform writes; malformed targets must degrade to [] rather than skip both. - for (const payload of [null, undefined, 'nope', 42, []]) { - expect(hostSshTargetSummariesSchema.parse(payload)).toEqual([]) - } - }) - - it('reads only a platform Node could have reported', () => { - expect(hostPlatformSchema.parse({ platform: 'win32' })).toBe('win32') - expect(hostPlatformSchema.parse({ platform: 'plan9' })).toBeNull() - expect(hostPlatformSchema.parse({ platform: '' })).toBeNull() - expect(hostPlatformSchema.parse({})).toBeNull() - for (const payload of [null, undefined, 'nope', 42]) { - expect(hostPlatformSchema.parse(payload)).toBeNull() - } - }) - - it('requires the ui member main read with a bare property access', () => { - expect(hostViewSettingsSchema.safeParse({}).success).toBe(false) - expect(hostViewSettingsSchema.safeParse(null).success).toBe(false) - expect(hostViewSettingsSchema.parse({ ui: {} })).toEqual({}) - }) - - it('degrades an unknown grouping or sort arm to absent so the local mode is kept', () => { - const parsed = hostViewSettingsSchema.parse({ - ui: { groupBy: 'agent', sortBy: 'stars', hideSleepingWorkspaces: 'yes' } - }) - expect(parsed.groupBy).toBeUndefined() - expect(parsed.sortBy).toBeUndefined() - expect(parsed.hideSleepingWorkspaces).toBeUndefined() - }) - - it('keeps the known view arms and the lists the screen adopts', () => { - const parsed = hostViewSettingsSchema.parse({ - ui: { - groupBy: 'workspace-status', - sortBy: 'recent', - filterRepoIds: ['r1'], - collapsedGroups: [], - workspaceStatuses: [{ id: 'active', label: 'Active' }] - } - }) - expect(parsed.groupBy).toBe('workspace-status') - expect(parsed.sortBy).toBe('recent') - expect(parsed.filterRepoIds).toEqual(['r1']) - expect(parsed.workspaceStatuses).toEqual([{ id: 'active', label: 'Active' }]) - }) - - it('reads every Workspace options value the desktop mirrors to its phone', () => { - const ui = { - hideAutomationGeneratedWorkspaces: true, - hideCliCreatedWorkspaces: false, - hideDetachedHeadWorkspaces: true, - hideWorkspacesFromOtherDevices: true, - projectOrderBy: 'recent', - worktreeCardProperties: ['status', 'a-property-from-a-newer-desktop'], - agentActivityDisplayMode: 'full' - } - expect(hostViewSettingsSchema.parse({ ui })).toEqual(ui) - }) - - it('reads a desktop that predates them as absent, which keeps the local value', () => { - const parsed = hostViewSettingsSchema.parse({ - ui: { groupBy: 'repo', projectOrderBy: 'alphabetical', agentActivityDisplayMode: 7 } - }) - expect(parsed).toEqual({ groupBy: 'repo' }) - }) - - it('salvages a non-array status catalog rather than handing a string to the group lookups', () => { - expect( - hostViewSettingsSchema.parse({ ui: { workspaceStatuses: 'active' } }).workspaceStatuses - ).toBeUndefined() - }) -}) - -describe('repo icon payloads', () => { - it('preserves the lucide icon name the row renders', () => { - const [row] = hostRepoCatalogSchema.parse({ - repos: [{ id: 'r', displayName: 'r', repoIcon: { type: 'lucide', name: 'Terminal' } }] - }) - expect(row?.repoIcon).toEqual({ type: 'lucide', name: 'Terminal' }) - }) -}) diff --git a/mobile/src/host-screen/host-screen-view.tsx b/mobile/src/host-screen/host-screen-view.tsx index 0aa860410de..c566c1e3d9b 100644 --- a/mobile/src/host-screen/host-screen-view.tsx +++ b/mobile/src/host-screen/host-screen-view.tsx @@ -7,7 +7,7 @@ import { hostScreenStyles as styles } from './host-screen-styles' import { HostBottomBar } from './host-bottom-bar' import { HostConnectionStatus, HostScreenHeader } from './host-screen-header' import { HostScreenOverlays } from './host-screen-overlays' -import { HostWorkspaceList } from './host-workspace-list' +import { HostWorkspaceList } from './workspace-list/host-workspace-list' import type { HostScreenController } from './use-host-screen-controller' export function HostScreenView({ controller }: { controller: HostScreenController }) { diff --git a/mobile/src/host-screen/host-sidebar-feed-sections.test.ts b/mobile/src/host-screen/host-sidebar-feed-sections.test.ts deleted file mode 100644 index 5d0cf70ce29..00000000000 --- a/mobile/src/host-screen/host-sidebar-feed-sections.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - buildSidebarFeedRows, - type SidebarFeedView -} from '../../../src/shared/sidebar-list/sidebar-feed-rows' - -import { buildSidebarFeedCatalog } from '../../../src/shared/sidebar-list/sidebar-feed-catalog' -import { buildHostFeedView } from './host-sidebar-feed-inputs' -import { buildHostFeedSections } from './host-sidebar-feed-sections' -import { - SERVED_SECTIONS, - feedDomains, - feedKey, - feedRow, - phoneView -} from './host-sidebar-feed.test-fixture' -import type { MobileViewState } from '../worktree/workspace-view-settings' -import type { ProjectGroup } from '../../../src/shared/project-group-types' -import { readServedProjectSections } from '../worktree/project-sections-operations' -import { NO_WORKTREE_HOST_CONTEXT } from '../worktree/worktree-host-context-labels' - -function phoneFeedView( - view: MobileViewState, - showPinnedInGroups = false, - hostPlatform: NodeJS.Platform | null = 'darwin' -): SidebarFeedView { - return buildHostFeedView({ - view, - projectOrderBy: 'manual', - desktop: { ...feedDomains().desktop!, showPinnedInGroups }, - hostPlatform - }) -} - -describe('the phone list from the sidebar feed', () => { - it('files a grouped project under its group header, nested and in the group’s order', () => { - const group = (id: string, name: string, extra: Partial = {}): ProjectGroup => ({ - id, - name, - parentPath: null, - parentGroupId: null, - createdFrom: 'manual', - tabOrder: 0, - isCollapsed: false, - color: null, - createdAt: 0, - updatedAt: 0, - ...extra - }) - const served = readServedProjectSections({ - id: 'sections', - ok: true, - result: { - catalog: { - ...SERVED_SECTIONS, - repos: [ - // Use reversed fixture order so only projectGroupOrder can produce the expected order. - { ...SERVED_SECTIONS.repos[0]!, projectGroupId: 'pg-inner', projectGroupOrder: 1 }, - { ...SERVED_SECTIONS.repos[1]!, projectGroupId: 'pg-inner', projectGroupOrder: 0 } - ], - projectGroups: [ - group('pg-school', 'school'), - group('pg-inner', 'inner', { parentGroupId: 'pg-school', createdFrom: 'folder-scan' }) - ] - } - } - }) - if (served.kind !== 'answered' || !served.value) { - throw new Error('expected a served catalog') - } - const grouped = buildSidebarFeedCatalog(served.value, [], []) - const base = feedDomains() - const domains = feedDomains({ - workspaces: new Map([ - ...base.workspaces, - [feedKey('|orca::/w/solo'), feedRow({ id: 'orca::/w/solo', manualOrder: 4 })] - ]) - }) - const built = buildSidebarFeedRows(domains, grouped, phoneFeedView(phoneView())) - const sections = buildHostFeedSections({ - rows: built.sectionRows, - lastVisited: null, - liveRows: [], - hostContextOf: NO_WORKTREE_HOST_CONTEXT, - repoHostIdByRepoId: new Map([['lab', 'ssh:gpu']]), - search: '' - }) - const headers = sections.map( - (section) => - `${section.title}${section.isProjectGroup ? ' group' : ''} depth=${String(section.projectGroupDepth ?? 0)}` - ) - expect(headers).toEqual([ - 'Pinned depth=0', - 'school group depth=0', - 'inner group depth=1', - 'lab depth=2', - 'orca depth=2' - ]) - expect( - sections - .slice(-2) - .map((section) => - section.data.map((row) => [ - row.view.projectGroupDepth, - row.view.inFolderScanGroup === true - ]) - ) - ).toEqual([[[2, true]], [[2, true]]]) - expect(sections.at(-2)?.data.map((row) => row.row.worktreeId)).toEqual(['lab::/w/gpu']) - }) -}) diff --git a/mobile/src/host-screen/host-sidebar-feed.test-fixture.ts b/mobile/src/host-screen/host-sidebar-feed.test-fixture.ts deleted file mode 100644 index 85afd5b4998..00000000000 --- a/mobile/src/host-screen/host-sidebar-feed.test-fixture.ts +++ /dev/null @@ -1,139 +0,0 @@ -import type { SidebarFeedDomains } from '../../../src/shared/sidebar-list/sidebar-feed-domains' -import type { WorkspaceRow } from '../../../src/shared/sidebar-list/workspace-rows' -import { - parseWorktreeHostIdentity, - type HostScopedWorktreeKey -} from '../../../src/shared/worktree/host-qualified-identity' -import type { ServedProjectSections } from '../worktree/project-sections-operations' -import { INITIAL_MOBILE_VIEW, type MobileViewState } from '../worktree/workspace-view-settings' -import type { Worktree } from '../worktree/workspace-list-types' - -export function feedRow(overrides: Partial & Pick): WorkspaceRow { - return { - hostId: 'local', - kind: 'git', - repoId: 'orca', - path: `/w/${overrides.id}`, - branch: overrides.id, - displayName: overrides.id, - isMainWorktree: false, - lineage: null, - isPinned: false, - isArchived: false, - isUnread: false, - workspaceStatus: 'in-progress', - comment: '', - linkedIssue: null, - linkedPR: null, - linkedLinearIssue: null, - linkedGitLabMR: null, - linkedGitLabIssue: null, - linkedItems: [], - defaultBranch: false, - ...overrides - } -} - -export const FEED_ROWS = { - parent: feedRow({ - id: 'orca::/w/parent', - isPinned: true, - instanceId: 'i-parent', - manualOrder: 1 - }), - child: feedRow({ - id: 'orca::/w/child', - instanceId: 'i-child', - manualOrder: 2, - lineage: { - parentWorktreeId: 'orca::/w/parent', - worktreeInstanceId: 'i-child', - parentWorktreeInstanceId: 'i-parent' - } - }), - remote: feedRow({ id: 'lab::/w/gpu', hostId: 'ssh:gpu', repoId: 'lab', manualOrder: 3 }) -} - -export function feedKey(identity: string): HostScopedWorktreeKey { - const key = parseWorktreeHostIdentity(identity) - if (!key) { - throw new Error(`not a row identity: ${identity}`) - } - return key -} - -export function feedDomains(overrides: Partial = {}): SidebarFeedDomains { - return { - workspaces: new Map([ - [feedKey('|orca::/w/parent'), FEED_ROWS.parent], - [feedKey('|orca::/w/child'), FEED_ROWS.child], - [feedKey('ssh:gpu|lab::/w/gpu'), FEED_ROWS.remote] - ]), - activity: new Map(), - review: new Map(), - creations: new Map(), - pairedServerHosts: new Map(), - pairedServerRows: new Map(), - repoIcons: new Map(), - desktop: { - desktopActive: { hostId: null, worktreeId: 'orca::/w/parent' }, - pinnedWorkspaceIds: ['orca::/w/parent'], - repoOrder: [ - { hostId: 'local', repoId: 'orca' }, - { hostId: 'ssh:gpu', repoId: 'lab' } - ], - workspaceHostOrder: [], - sshHosts: { 'ssh:gpu': { label: 'GPU box', status: 'connected' } }, - wslHosts: {} - }, - projectCatalog: null, - retention: null, - ...overrides - } -} - -type ServedRepo = ServedProjectSections['repos'][number] - -function servedRepo(fields: Pick & Partial): ServedRepo { - return { - displayName: fields.id, - badgeColor: undefined, - addedAt: undefined, - connectionId: undefined, - executionHostId: undefined, - projectGroupId: undefined, - projectGroupOrder: undefined, - ...fields - } -} - -export const SERVED_SECTIONS: ServedProjectSections = { - repos: [ - servedRepo({ id: 'orca', path: '/r/orca', badgeColor: '#f00' }), - servedRepo({ id: 'lab', path: '/r/lab', connectionId: 'gpu' }) - ], - projects: [], - setups: [], - projectOrderBy: 'manual', - projectGroups: undefined -} - -export function phoneView(overrides: Partial = {}): MobileViewState { - return { ...INITIAL_MOBILE_VIEW, groupBy: 'repo', sortBy: 'manual', ...overrides } -} - -export function liveRow(overrides: Partial & Pick): Worktree { - return { - repoId: 'orca', - repo: 'orca', - branch: overrides.worktreeId, - displayName: overrides.worktreeId, - path: `/w/${overrides.worktreeId}`, - liveTerminalCount: 0, - hasAttachedPty: false, - unread: false, - isPinned: false, - linkedPR: null, - ...overrides - } -} diff --git a/mobile/src/host-screen/identity/host-edit-navigation.ts b/mobile/src/host-screen/identity/host-edit-navigation.ts new file mode 100644 index 00000000000..7360a528e1a --- /dev/null +++ b/mobile/src/host-screen/identity/host-edit-navigation.ts @@ -0,0 +1,22 @@ +import { + navigateToHostStackRoute, + type HostStackNavigationController, + type HostStackRootNavigation, + type HostStackRouteTarget, + type HostStackRouter +} from '../../navigation/host-stack-navigation' + +export function mobileHostEditRouteTarget(hostId: string): HostStackRouteTarget { + return { + name: '[hostId]/edit', + params: { hostId } + } +} + +export function navigateToMobileHostEdit( + navigation: HostStackRootNavigation, + router: HostStackRouter, + hostId: string +): HostStackNavigationController { + return navigateToHostStackRoute(navigation, router, hostId, mobileHostEditRouteTarget(hostId)) +} diff --git a/mobile/src/host-screen/host-stored-identity.ts b/mobile/src/host-screen/identity/host-stored-identity.ts similarity index 83% rename from mobile/src/host-screen/host-stored-identity.ts rename to mobile/src/host-screen/identity/host-stored-identity.ts index dd02444eb75..45cef67e4fb 100644 --- a/mobile/src/host-screen/host-stored-identity.ts +++ b/mobile/src/host-screen/identity/host-stored-identity.ts @@ -1,5 +1,5 @@ -import type { HostProfile } from '../host-catalog/host-profile' -import type { HostMachine } from '../../../src/shared/host-machine-line' +import type { HostProfile } from '../../host-catalog/host-profile' +import type { HostMachine } from '../../../../src/shared/host-machine-line' export type HostStoredDescriptor = { personalName?: string diff --git a/mobile/src/host-screen/use-host-screen-identity.ts b/mobile/src/host-screen/identity/use-host-screen-identity.ts similarity index 80% rename from mobile/src/host-screen/use-host-screen-identity.ts rename to mobile/src/host-screen/identity/use-host-screen-identity.ts index 057a23b8718..59ad8a8229e 100644 --- a/mobile/src/host-screen/use-host-screen-identity.ts +++ b/mobile/src/host-screen/identity/use-host-screen-identity.ts @@ -1,17 +1,17 @@ import { useEffect } from 'react' import { useQueryClient } from '@tanstack/react-query' -import { translate } from '../i18n/mobile-i18n' -import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../src/shared/sidebar-inputs-switch' -import { loadPinnedIds } from '../storage/preferences' -import { loadHosts, peekHosts } from '../host-catalog/host-store' -import { updateLastConnected } from '../host-catalog/host-connection-history' -import type { HostProfile } from '../host-catalog/host-profile' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import type { HostScreenState } from './use-host-screen-state' +import { translate } from '../../i18n/mobile-i18n' +import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../../src/shared/sidebar-inputs-switch' +import { loadPinnedIds } from '../../storage/preferences' +import { loadHosts, peekHosts } from '../../host-catalog/host-store' +import { updateLastConnected } from '../../host-catalog/host-connection-history' +import type { HostProfile } from '../../host-catalog/host-profile' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import type { HostScreenState } from '../use-host-screen-state' import { storedHostDescriptor } from './host-stored-identity' -import { INITIAL_MOBILE_VIEW } from '../worktree/workspace-view-settings' -import { cachedHostView } from './host-view-memory' -import { cachedHostWorktrees } from './host-list-queries' +import { INITIAL_MOBILE_VIEW } from '../../worktree/workspace-view-settings' +import { cachedHostView } from '../view-settings/host-view-memory' +import { cachedHostWorktrees } from '../catalog/host-list-queries' // The host as last read this launch, so the title's first frame shows its name, not "Host". export function peekStoredHost(hostId: string | undefined): HostProfile | null { diff --git a/mobile/src/host-screen/use-open-mobile-host-edit.ts b/mobile/src/host-screen/identity/use-open-mobile-host-edit.ts similarity index 81% rename from mobile/src/host-screen/use-open-mobile-host-edit.ts rename to mobile/src/host-screen/identity/use-open-mobile-host-edit.ts index a6517e3a3d9..0bf5e726969 100644 --- a/mobile/src/host-screen/use-open-mobile-host-edit.ts +++ b/mobile/src/host-screen/identity/use-open-mobile-host-edit.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react' -import { useOpenHostStackRoute } from '../navigation/use-open-host-stack-route' +import { useOpenHostStackRoute } from '../../navigation/use-open-host-stack-route' import { mobileHostEditRouteTarget } from './host-edit-navigation' export function useOpenMobileHostEdit(): (hostId: string) => void { diff --git a/mobile/src/host-screen/host-sidebar-feed-connection.ts b/mobile/src/host-screen/sidebar-feed/host-sidebar-feed-connection.ts similarity index 82% rename from mobile/src/host-screen/host-sidebar-feed-connection.ts rename to mobile/src/host-screen/sidebar-feed/host-sidebar-feed-connection.ts index d48e56d65a2..bf1ed86d98a 100644 --- a/mobile/src/host-screen/host-sidebar-feed-connection.ts +++ b/mobile/src/host-screen/sidebar-feed/host-sidebar-feed-connection.ts @@ -2,8 +2,8 @@ import { SidebarFeedReader, type SidebarFeedNotice, type SidebarFeedReaderRead -} from '../../../src/shared/sidebar-feed-client/sidebar-feed-reader' -import type { UnvalidatedRpcRequestPort } from '../transport/rpc/unvalidated-rpc-request-port' +} from '../../../../src/shared/sidebar-feed-client/sidebar-feed-reader' +import type { UnvalidatedRpcRequestPort } from '../../transport/rpc/unvalidated-rpc-request-port' import { hostSidebarFeedTransport } from './host-sidebar-feed-operations' export type HostSidebarFeedTriggers = { diff --git a/mobile/src/host-screen/host-sidebar-feed-inputs.ts b/mobile/src/host-screen/sidebar-feed/host-sidebar-feed-inputs.ts similarity index 75% rename from mobile/src/host-screen/host-sidebar-feed-inputs.ts rename to mobile/src/host-screen/sidebar-feed/host-sidebar-feed-inputs.ts index bba09257ec2..936a32aa55b 100644 --- a/mobile/src/host-screen/host-sidebar-feed-inputs.ts +++ b/mobile/src/host-screen/sidebar-feed/host-sidebar-feed-inputs.ts @@ -3,12 +3,12 @@ import { LOCAL_EXECUTION_HOST_ID, normalizeExecutionHostId, type ExecutionHostId -} from '../../../src/shared/execution-host' -import { getHostContextLabel } from '../../../src/shared/worktree/host-context-labels' -import type { DesktopSidebarFacts } from '../../../src/shared/sidebar-list/desktop-sidebar-facts' -import type { SidebarFeedView } from '../../../src/shared/sidebar-list/sidebar-feed-rows' -import type { ProjectOrderBy } from '../../../src/shared/ui-chrome-types' -import type { MobileViewState } from '../worktree/workspace-view-settings' +} from '../../../../src/shared/execution-host' +import { getHostContextLabel } from '../../../../src/shared/worktree/host-context-labels' +import type { DesktopSidebarFacts } from '../../../../src/shared/sidebar-list/desktop-sidebar-facts' +import type { SidebarFeedView } from '../../../../src/shared/sidebar-list/sidebar-feed-rows' +import type { ProjectOrderBy } from '../../../../src/shared/ui-chrome-types' +import type { MobileViewState } from '../../worktree/workspace-view-settings' // Mirror desktop UI/settings into the shared sidebar builder; resolved all-host scope matches desktop legacy scope. export function buildHostFeedView(args: { diff --git a/mobile/src/host-screen/host-sidebar-feed-operations.ts b/mobile/src/host-screen/sidebar-feed/host-sidebar-feed-operations.ts similarity index 79% rename from mobile/src/host-screen/host-sidebar-feed-operations.ts rename to mobile/src/host-screen/sidebar-feed/host-sidebar-feed-operations.ts index 3f69e0ce52e..0aa0bf7e949 100644 --- a/mobile/src/host-screen/host-sidebar-feed-operations.ts +++ b/mobile/src/host-screen/sidebar-feed/host-sidebar-feed-operations.ts @@ -1,11 +1,11 @@ import * as z from 'zod' -import { SidebarInputsReadResultSchema } from '../../../src/shared/sidebar-list/sidebar-inputs-feed-contract' -import type { SidebarFeedTransport } from '../../../src/shared/sidebar-feed-client/sidebar-feed-reader' -import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' -import { rpcResultVariant } from '../transport/rpc/rpc-operation-result-reader' -import { isMobileMethodUnavailableError } from '../transport/rpc/mobile-method-unavailable' -import type { RpcResponse } from '../transport/rpc/rpc-wire-types' -import type { UnvalidatedRpcRequestPort } from '../transport/rpc/unvalidated-rpc-request-port' +import { SidebarInputsReadResultSchema } from '../../../../src/shared/sidebar-list/sidebar-inputs-feed-contract' +import type { SidebarFeedTransport } from '../../../../src/shared/sidebar-feed-client/sidebar-feed-reader' +import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' +import { rpcResultVariant } from '../../transport/rpc/rpc-operation-result-reader' +import { isMobileMethodUnavailableError } from '../../transport/rpc/mobile-method-unavailable' +import type { RpcResponse } from '../../transport/rpc/rpc-wire-types' +import type { UnvalidatedRpcRequestPort } from '../../transport/rpc/unvalidated-rpc-request-port' // Only ORCA_SIDEBAR_INPUTS=main pages read this feed; method_not_found or an older allowlist’s forbidden means the page is newer than the host. diff --git a/mobile/src/host-screen/host-sidebar-feed-sections.ts b/mobile/src/host-screen/sidebar-feed/host-sidebar-feed-sections.ts similarity index 89% rename from mobile/src/host-screen/host-sidebar-feed-sections.ts rename to mobile/src/host-screen/sidebar-feed/host-sidebar-feed-sections.ts index fcdf2861ab7..f26ea3c39f3 100644 --- a/mobile/src/host-screen/host-sidebar-feed-sections.ts +++ b/mobile/src/host-screen/sidebar-feed/host-sidebar-feed-sections.ts @@ -2,26 +2,31 @@ import { LOCAL_EXECUTION_HOST_ID, getWorktreeExecutionHostId, type ExecutionHostId -} from '../../../src/shared/execution-host' -import { folderWorkspaceToWorktree } from '../../../src/shared/folder-workspaces/folder-workspace-worktree' -import { getFolderWorkspaceHostId } from '../../../src/shared/sidebar-list/folder-workspace-host-id' -import type { HostSectionRow } from '../../../src/shared/sidebar-list/host-section-rows' -import type { GroupHeaderRow } from '../../../src/shared/sidebar-list/row-types' -import { PR_GROUP_LABELS } from '../../../src/shared/workspace-view/workspace-pr-groups' -import type { Worktree as HostWorktree } from '../../../src/shared/worktree/types' -import { composeWorktreeHostIdentity } from '../../../src/shared/worktree/host-qualified-identity' -import type { Section, Worktree, WorktreeHostContextView } from '../worktree/workspace-list-types' +} from '../../../../src/shared/execution-host' +import { folderWorkspaceToWorktree } from '../../../../src/shared/folder-workspaces/folder-workspace-worktree' +import { getFolderWorkspaceHostId } from '../../../../src/shared/sidebar-list/folder-workspace-host-id' +import type { HostSectionRow } from '../../../../src/shared/sidebar-list/host-section-rows' +import type { GroupHeaderRow } from '../../../../src/shared/sidebar-list/row-types' +import { PR_GROUP_LABELS } from '../../../../src/shared/workspace-view/workspace-pr-groups' +import type { Worktree as HostWorktree } from '../../../../src/shared/worktree/types' +import { composeWorktreeHostIdentity } from '../../../../src/shared/worktree/host-qualified-identity' +import type { + Section, + Worktree, + WorktreeHostContextView +} from '../../worktree/workspace-list-types' import { isLastVisitedWorktreeRow, type LastVisitedWorktreeOnHost -} from '../worktree/host-list-highlight' +} from '../../worktree/host-list-highlight' import { resolveWorktreeHostId, type WorktreeHostContextOf -} from '../worktree/worktree-host-context-labels' -import { filterWorktrees } from '../worktree/workspace-list-sections' -import { rowFiltersForView, type MobileViewState } from '../worktree/workspace-view-settings' -import type { HiddenProjects } from '../../../src/shared/hidden-projects' +} from '../../worktree/worktree-host-context-labels' +import '../../worktree/workspace-list-sections' +import { filterWorktrees } from '../../worktree/workspace-row-filtering' +import { rowFiltersForView, type MobileViewState } from '../../worktree/workspace-view-settings' +import type { HiddenProjects } from '../../../../src/shared/hidden-projects' export type HostFeedListSources = { rows: readonly HostSectionRow[] diff --git a/mobile/src/host-screen/use-host-sidebar-feed.ts b/mobile/src/host-screen/sidebar-feed/use-host-sidebar-feed.ts similarity index 87% rename from mobile/src/host-screen/use-host-sidebar-feed.ts rename to mobile/src/host-screen/sidebar-feed/use-host-sidebar-feed.ts index 380b4e1cc7a..e66829b05a6 100644 --- a/mobile/src/host-screen/use-host-sidebar-feed.ts +++ b/mobile/src/host-screen/sidebar-feed/use-host-sidebar-feed.ts @@ -1,16 +1,16 @@ import { useEffect, useMemo, useRef, useState } from 'react' -import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../src/shared/sidebar-inputs-switch' -import type { SidebarFeedDomains } from '../../../src/shared/sidebar-list/sidebar-feed-domains' +import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../../src/shared/sidebar-inputs-switch' +import type { SidebarFeedDomains } from '../../../../src/shared/sidebar-list/sidebar-feed-domains' import type { SidebarFeedNotice, SidebarFeedReader -} from '../../../src/shared/sidebar-feed-client/sidebar-feed-reader' +} from '../../../../src/shared/sidebar-feed-client/sidebar-feed-reader' import { isReadableSidebarFeed, type SidebarFeedSnapshot -} from '../../../src/shared/sidebar-feed-client/sidebar-feed-snapshot' -import { RememberedSidebarFeeds } from '../../../src/shared/sidebar-feed-client/remembered-sidebar-feeds' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' +} from '../../../../src/shared/sidebar-feed-client/sidebar-feed-snapshot' +import { RememberedSidebarFeeds } from '../../../../src/shared/sidebar-feed-client/remembered-sidebar-feeds' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import { createHostSidebarFeedReader, type HostSidebarFeedTriggers diff --git a/mobile/src/host-screen/use-host-screen-controller.ts b/mobile/src/host-screen/use-host-screen-controller.ts index e5ea6277d43..0bc3da7bf96 100644 --- a/mobile/src/host-screen/use-host-screen-controller.ts +++ b/mobile/src/host-screen/use-host-screen-controller.ts @@ -17,17 +17,20 @@ import { useReconnectAttempt, useRelayRecoveryStatus } from '../transport/connection/client-context-connection-metrics' -import { useHostDisplayWorktrees, useResolvedHostPlatform } from './use-host-display-worktrees' -import { useHostListSections } from './use-host-list-sections' -import { useSwitchedHostSidebarFeed } from './use-host-sidebar-feed' -import { useHostProjectSections } from './use-host-project-sections' -import { useHostRepoMetadata } from './use-host-repo-metadata' -import { peekStoredHost, useHostScreenIdentity } from './use-host-screen-identity' +import { + useHostDisplayWorktrees, + useResolvedHostPlatform +} from './workspace-list/use-host-display-worktrees' +import { useHostListSections } from './workspace-list/use-host-list-sections' +import { useSwitchedHostSidebarFeed } from './sidebar-feed/use-host-sidebar-feed' +import { useHostProjectSections } from './catalog/use-host-project-sections' +import { useHostRepoMetadata } from './catalog/use-host-repo-metadata' +import { peekStoredHost, useHostScreenIdentity } from './identity/use-host-screen-identity' import { useHostScreenState } from './use-host-screen-state' -import { useHostViewSettings } from './use-host-view-settings' +import { useHostViewSettings } from './view-settings/use-host-view-settings' import { countActiveWorkspaceFilters } from '../../../src/shared/workspace-view/workspace-row-filters' -import { useHostWorktreeActions } from './use-host-worktree-actions' -import { useHostWorktreeCatalog } from './use-host-worktree-catalog' +import { useHostWorktreeActions } from './workspace-actions/use-host-worktree-actions' +import { useHostWorktreeCatalog } from './catalog/use-host-worktree-catalog' import { useHostDisplay } from '../host-descriptors/use-host-display' import { classifyConnection } from '../transport/connection/connection-health' diff --git a/mobile/src/host-screen/use-host-screen-state.ts b/mobile/src/host-screen/use-host-screen-state.ts index 4f40a1a95bb..3b965964dfb 100644 --- a/mobile/src/host-screen/use-host-screen-state.ts +++ b/mobile/src/host-screen/use-host-screen-state.ts @@ -2,17 +2,17 @@ import { useCallback, useRef, useState } from 'react' import { useQueryClient } from '@tanstack/react-query' import type { ExecutionHostId } from '../../../src/shared/execution-host' import type { ExecutionHostHealth } from '../../../src/shared/execution-host/execution-host-health' -import { storedHostDescriptor, type HostStoredDescriptor } from './host-stored-identity' +import { storedHostDescriptor, type HostStoredDescriptor } from './identity/host-stored-identity' import type { RpcClient } from '../transport/rpc/rpc-client-contract' import type { HostProfile } from '../host-catalog/host-profile' import { INITIAL_MOBILE_VIEW, type MobileViewState } from '../worktree/workspace-view-settings' -import type { Worktree } from '../worktree/workspace-list-sections' -import type { ServedProjectSections } from '../worktree/project-sections-operations' -import type { SectionRepo } from '../worktree/workspace-list-types' + +import type { ServedProjectSections } from '../../../src/shared/project-section-catalog-schema' +import type { SectionRepo, Worktree } from '../worktree/workspace-list-types' import { NO_SERVER_WORKSPACES, type ServerWorkspaces } from '../worktree/server-workspaces' import type { ProjectGroup } from '../../../src/shared/project-group-types' -import { cachedHostWorktrees } from './host-list-queries' -import { cachedHostView, useHostViewMemory } from './host-view-memory' +import { cachedHostWorktrees } from './catalog/host-list-queries' +import { cachedHostView, useHostViewMemory } from './view-settings/host-view-memory' export function useHostScreenState( hostId: string | undefined, diff --git a/mobile/src/host-screen/host-list-settings-operations.ts b/mobile/src/host-screen/view-settings/host-list-settings-operations.ts similarity index 80% rename from mobile/src/host-screen/host-list-settings-operations.ts rename to mobile/src/host-screen/view-settings/host-list-settings-operations.ts index 4af11d70e45..ed977bb0d1c 100644 --- a/mobile/src/host-screen/host-list-settings-operations.ts +++ b/mobile/src/host-screen/view-settings/host-list-settings-operations.ts @@ -1,7 +1,7 @@ -import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' -import type { RpcCompatibleReader } from '../transport/rpc/rpc-operation-contract' -import '../../../src/shared/source-control/listing/source-control-group-order' -import { settingsMember, settingsField } from '../settings/mobile-settings-projection' +import { bindDeferredRpcOperation, defineRpcOperation } from '../../transport/rpc/rpc-operation' +import type { RpcCompatibleReader } from '../../transport/rpc/rpc-operation-contract' +import '../../../../src/shared/source-control/listing/source-control-group-order' +import { settingsMember, settingsField } from '../../settings/mobile-settings-projection' type HostListSettings = { showPinnedWorktreesInGroups: boolean diff --git a/mobile/src/host-screen/host-view-memory.ts b/mobile/src/host-screen/view-settings/host-view-memory.ts similarity index 86% rename from mobile/src/host-screen/host-view-memory.ts rename to mobile/src/host-screen/view-settings/host-view-memory.ts index c04f436c9f7..0701f08b7e8 100644 --- a/mobile/src/host-screen/host-view-memory.ts +++ b/mobile/src/host-screen/view-settings/host-view-memory.ts @@ -1,10 +1,10 @@ import { useCallback } from 'react' import { skipToken, useQuery, useQueryClient, type QueryClient } from '@tanstack/react-query' import * as z from 'zod' -import { hostQueryKey } from '../query/host-query-key' -import type { PersistedHostFamily } from '../query/host-query-persistence' -import type { MobileViewState } from '../worktree/workspace-view-settings' -import { HOST_LIST_TARGET, NO_HOST_LIST_INPUT } from './host-list-queries' +import { hostQueryKey } from '../../query/host-query-key' +import type { PersistedHostFamily } from '../../query/host-query-persistence' +import type { MobileViewState } from '../../worktree/workspace-view-settings' +import { HOST_LIST_TARGET, NO_HOST_LIST_INPUT } from '../catalog/host-list-queries' const storedViewSchema = z.looseObject({ groupBy: z.string(), diff --git a/mobile/src/host-screen/host-view-pending-changes.ts b/mobile/src/host-screen/view-settings/host-view-pending-changes.ts similarity index 98% rename from mobile/src/host-screen/host-view-pending-changes.ts rename to mobile/src/host-screen/view-settings/host-view-pending-changes.ts index 76f2d48924b..6f932beb9f1 100644 --- a/mobile/src/host-screen/host-view-pending-changes.ts +++ b/mobile/src/host-screen/view-settings/host-view-pending-changes.ts @@ -2,7 +2,7 @@ import type { MobileViewPatch, MobileViewState, WorkspaceViewSettings -} from '../worktree/workspace-view-settings' +} from '../../worktree/workspace-view-settings' // Show the desired value until the desktop answers; a slow response is not a refusal. export type PendingViewChange = { opId: number; sentAt: number } & ( diff --git a/mobile/src/host-screen/use-host-pending-view-changes.test.ts b/mobile/src/host-screen/view-settings/use-host-pending-view-changes.test.ts similarity index 94% rename from mobile/src/host-screen/use-host-pending-view-changes.test.ts rename to mobile/src/host-screen/view-settings/use-host-pending-view-changes.test.ts index 29a393415cb..2812a4012e3 100644 --- a/mobile/src/host-screen/use-host-pending-view-changes.test.ts +++ b/mobile/src/host-screen/view-settings/use-host-pending-view-changes.test.ts @@ -3,11 +3,13 @@ import { act, create as createRenderer } from 'react-test-renderer' import { afterEach, describe, expect, it, vi } from 'vitest' -vi.mock('../../../src/shared/sidebar-inputs-switch', () => ({ ORCA_SIDEBAR_INPUTS_IS_MAIN: true })) +vi.mock('../../../../src/shared/sidebar-inputs-switch', () => ({ + ORCA_SIDEBAR_INPUTS_IS_MAIN: true +})) -import '../../../src/shared/rpc-contract/workspace-view-collapse-params' -import { FakeSession } from '../transport/connection/mobile-endpoint-supervisor-test-fakes' -import type { RpcResponse } from '../transport/rpc/rpc-wire-types' +import '../../../../src/shared/rpc-contract/workspace-view-collapse-params' +import { FakeSession } from '../../transport/connection/mobile-endpoint-supervisor-test-fakes' +import type { RpcResponse } from '../../transport/rpc/rpc-wire-types' import { addPendingViewChange, reconcilePendingViewChanges, diff --git a/mobile/src/host-screen/use-host-pending-view-changes.ts b/mobile/src/host-screen/view-settings/use-host-pending-view-changes.ts similarity index 93% rename from mobile/src/host-screen/use-host-pending-view-changes.ts rename to mobile/src/host-screen/view-settings/use-host-pending-view-changes.ts index fd5aeca638b..6df67901a6b 100644 --- a/mobile/src/host-screen/use-host-pending-view-changes.ts +++ b/mobile/src/host-screen/view-settings/use-host-pending-view-changes.ts @@ -1,9 +1,12 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../src/shared/sidebar-inputs-switch' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import type { MobileViewPatch, WorkspaceViewSettings } from '../worktree/workspace-view-settings' -import { hostViewSettingsWrite } from './host-screen-operations' -import { readHostMethodReply, setCollapsedWrite } from './host-sidebar-feed-operations' +import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../../src/shared/sidebar-inputs-switch' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import type { MobileViewPatch, WorkspaceViewSettings } from '../../worktree/workspace-view-settings' +import { hostViewSettingsWrite } from '../host-screen-operations' +import { + readHostMethodReply, + setCollapsedWrite +} from '../sidebar-feed/host-sidebar-feed-operations' import { PENDING_VIEW_CHANGE_STALE_MS, addPendingViewChange, diff --git a/mobile/src/host-screen/use-host-view-settings.ts b/mobile/src/host-screen/view-settings/use-host-view-settings.ts similarity index 93% rename from mobile/src/host-screen/use-host-view-settings.ts rename to mobile/src/host-screen/view-settings/use-host-view-settings.ts index 208d262439f..a2c83d1a58a 100644 --- a/mobile/src/host-screen/use-host-view-settings.ts +++ b/mobile/src/host-screen/view-settings/use-host-view-settings.ts @@ -1,37 +1,37 @@ import { useCallback, useRef } from 'react' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import type { ConnectionState } from '../transport/connection/connection-state' -import { getMobileWorkspaceLineageGroupKey } from '../worktree/mobile-workspace-lineage' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import type { ConnectionState } from '../../transport/connection/connection-state' +import { getMobileWorkspaceLineageGroupKey } from '../../worktree/mobile-workspace-lineage' import { applyDesktopViewSettings, buildWorkspaceViewSettingsUpdate, type MobileViewPatch, type MobileViewState, type WorkspaceViewSettings -} from '../worktree/workspace-view-settings' +} from '../../worktree/workspace-view-settings' import { changedWorkspaceRowFilters, NO_WORKSPACE_ROW_FILTERS -} from '../../../src/shared/workspace-view/workspace-row-filters' +} from '../../../../src/shared/workspace-view/workspace-row-filters' import { hostCardLayoutWrite, hostViewSettingsRead, hostViewSettingsWrite -} from './host-screen-operations' +} from '../host-screen-operations' import { hostListSettingsRead } from './host-list-settings-operations' import type { AgentActivityDisplayMode, ProjectOrderBy, WorktreeCardProperty -} from '../../../src/shared/ui-chrome-types' -import { getWorktreeCardModeUpdates } from '../../../src/shared/worktree/card-properties' -import type { HostScreenState } from './use-host-screen-state' +} from '../../../../src/shared/ui-chrome-types' +import { getWorktreeCardModeUpdates } from '../../../../src/shared/worktree/card-properties' +import type { HostScreenState } from '../use-host-screen-state' import { applyCollapse, withPendingViewChanges } from './host-view-pending-changes' import { useSwitchedHostPendingViewChanges, type PendingViewStatus } from './use-host-pending-view-changes' -import type { FilterState, WorkspaceListItem } from '../worktree/workspace-list-types' +import type { FilterState, WorkspaceListItem } from '../../worktree/workspace-list-types' const REFUSED_NOTE = 'The desktop did not take that change.' const NOTHING_PENDING: PendingViewStatus = { pending: [], stale: false } diff --git a/mobile/src/host-screen/use-host-worktree-actions.ts b/mobile/src/host-screen/workspace-actions/use-host-worktree-actions.ts similarity index 83% rename from mobile/src/host-screen/use-host-worktree-actions.ts rename to mobile/src/host-screen/workspace-actions/use-host-worktree-actions.ts index d96d2e8c9ab..a64de9fe88b 100644 --- a/mobile/src/host-screen/use-host-worktree-actions.ts +++ b/mobile/src/host-screen/workspace-actions/use-host-worktree-actions.ts @@ -1,52 +1,33 @@ import { useCallback } from 'react' import { useQueryClient } from '@tanstack/react-query' -import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../src/shared/sidebar-inputs-switch' +import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../../src/shared/sidebar-inputs-switch' import type { useRouter } from 'expo-router' -import { floatingWorkspaceSessionPath } from '../session/floating-workspace' -import { holdSessionLeave } from '../session/session-leave-guard' -import { savePinnedIds } from '../storage/preferences' -import type { useForgetHostClient } from '../transport/connection/host-client-hooks' -import { removeHostAndCloseClient } from '../host-catalog/host-removal-lifecycle' -import { isPageHostRemovalUnavailable } from '../host-catalog/page-host-removal-refusal' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' +import { floatingWorkspaceSessionPath } from '../../session/floating-workspace' +import { holdSessionLeave } from '../../session/session-leave-guard' +import { savePinnedIds } from '../../storage/preferences' +import type { useForgetHostClient } from '../../transport/connection/host-client-hooks' +import { removeHostAndCloseClient } from '../../host-catalog/host-removal-lifecycle' +import { isPageHostRemovalUnavailable } from '../../host-catalog/page-host-removal-refusal' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import { rpcClientForExecutionHost, workspaceRouteExecutionHost -} from '../worktree/execution-host-scoped-rpc-client' -import { serverUpdateNeededNotice } from '../host-route-notice' -import type { ConnectionState } from '../transport/connection/connection-state' -import { leaveHostRoute } from '../host-route-exit' -import { getWorktreeRowIdentity, isSameWorktreeRow } from '../worktree/worktree-host-row-identity' -import { noteLastVisitedWorktree } from '../worktree/last-visited-worktree-by-host' -import { isWorktreePinned, type Worktree } from '../worktree/workspace-list-sections' +} from '../../worktree/execution-host-scoped-rpc-client' +import { serverUpdateNeededNotice } from '../../host-route-notice' +import type { ConnectionState } from '../../transport/connection/connection-state' +import { leaveHostRoute } from '../../host-route-exit' import { - worktreeActivate, - worktreeDisplayNameWrite, - worktreePinWrite, - worktreeSleep -} from './host-screen-operations' -import type { HostScreenState } from './use-host-screen-state' + getWorktreeRowIdentity, + isSameWorktreeRow +} from '../../worktree/worktree-host-row-identity' +import { noteLastVisitedWorktree } from '../../worktree/last-visited-worktree-by-host' +import { isWorktreePinned } from '../../worktree/workspace-list-sections' +import type { Worktree } from '../../worktree/workspace-list-types' +import { worktreeActivate, worktreePinWrite, worktreeSleep } from '../host-screen-operations' +import type { HostScreenState } from '../use-host-screen-state' import { useHostWorktreeDelete } from './use-host-worktree-delete' -import { defineHostMutation, useHostMutation, type HostMutationPhase } from '../query/host-mutation' - -async function sendWorkspaceRename( - client: RpcClient, - vars: { worktreeId: string; displayName: string } -): Promise { - worktreeDisplayNameWrite.interpret( - await worktreeDisplayNameWrite.request(client, { - worktree: `id:${vars.worktreeId}`, - displayName: vars.displayName - }) - ) -} - -const workspaceRename = defineHostMutation({ - offline: 'draft', - send: sendWorkspaceRename, - // Renaming is idempotent, so a lost reply is settled by sending the same name again. - confirm: sendWorkspaceRename -}) +import { useHostMutation, type HostMutationPhase } from '../../query/host-mutation' +import { workspaceRename } from './workspace-rename-mutation' export function useHostWorktreeActions(args: { client: RpcClient | null diff --git a/mobile/src/host-screen/use-host-worktree-delete.ts b/mobile/src/host-screen/workspace-actions/use-host-worktree-delete.ts similarity index 91% rename from mobile/src/host-screen/use-host-worktree-delete.ts rename to mobile/src/host-screen/workspace-actions/use-host-worktree-delete.ts index eb4b90885c1..33dae59848e 100644 --- a/mobile/src/host-screen/use-host-worktree-delete.ts +++ b/mobile/src/host-screen/workspace-actions/use-host-worktree-delete.ts @@ -1,20 +1,20 @@ import { useCallback, useRef, useState } from 'react' -import type { PersistedTrustedOrcaHooks } from '../../../src/shared/orca-yaml-hook-types' -import { ARCHIVE_HOOK_FAILED_REMOVAL_CODE } from '../../../src/shared/worktree/archive-hook-removal-gate' -import type { OrcaHookTrustPrompt } from '../components/OrcaHookTrustDrawer' -import { newWorkspaceUiStateRead } from '../components/new-workspace-operations' -import { canUseRepoWideTrust } from '../../../src/shared/orca-hook-trust-entries' +import type { PersistedTrustedOrcaHooks } from '../../../../src/shared/orca-yaml-hook-types' +import { ARCHIVE_HOOK_FAILED_REMOVAL_CODE } from '../../../../src/shared/worktree/archive-hook-removal-gate' +import type { OrcaHookTrustPrompt } from '../../components/OrcaHookTrustDrawer' +import { newWorkspaceUiStateRead } from '../../components/new-workspace-operations' +import { canUseRepoWideTrust } from '../../../../src/shared/orca-hook-trust-entries' import { persistOrcaHookTrustApproval, wasOrcaHookPreviouslyApproved -} from '../new-workspace/creation/setup-hook-trust' -import { triggerError, triggerSuccess } from '../platform/haptics' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' +} from '../../new-workspace/creation/setup-hook-trust' +import { triggerError, triggerSuccess } from '../../platform/haptics' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import { buildWorktreeRemoveRequest } from './worktree-remove-request' -import { removeWorktreeRow } from '../worktree/worktree-host-row-identity' -import type { Worktree } from '../worktree/workspace-list-sections' -import { worktreeArchiveHookTrustRead, worktreeRemove } from './host-screen-operations' -import type { HostScreenState } from './use-host-screen-state' +import { removeWorktreeRow } from '../../worktree/worktree-host-row-identity' +import type { Worktree } from '../../worktree/workspace-list-types' +import { worktreeArchiveHookTrustRead, worktreeRemove } from '../host-screen-operations' +import type { HostScreenState } from '../use-host-screen-state' type ArchiveHookDecision = | { kind: 'run' | 'skip' } diff --git a/mobile/src/host-screen/workspace-details-sheet.tsx b/mobile/src/host-screen/workspace-actions/workspace-details-sheet.tsx similarity index 90% rename from mobile/src/host-screen/workspace-details-sheet.tsx rename to mobile/src/host-screen/workspace-actions/workspace-details-sheet.tsx index e285c5b6480..1bf6ac07cee 100644 --- a/mobile/src/host-screen/workspace-details-sheet.tsx +++ b/mobile/src/host-screen/workspace-actions/workspace-details-sheet.tsx @@ -8,30 +8,30 @@ import { SquareTerminal, StickyNote } from 'lucide-react-native' -import { colors, spacing, typography } from '../theme/mobile-theme' -import { themedStyleSheet } from '../theme/themed-styles' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { prStateColor } from '../components/WorktreeMetaGlyphs' -import { PrStateIcon } from '../components/PrStateIcon' +import { colors, spacing, typography } from '../../theme/mobile-theme' +import { themedStyleSheet } from '../../theme/themed-styles' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import { prStateColor } from '../../components/WorktreeMetaGlyphs' +import { PrStateIcon } from '../../components/PrStateIcon' import { LIST_LEADING_INSET, LIST_LEADING_SLOT, LIST_ROW_PADDING, ListGroup, ListRow -} from '../components/list/ListGroup' -import { TaskProviderLogo } from '../components/TaskProviderLogo' -import { getWorktreeGitIdentityDisplay } from '../../../src/shared/worktree/git-identity-display' +} from '../../components/list/ListGroup' +import { TaskProviderLogo } from '../../components/TaskProviderLogo' +import { getWorktreeGitIdentityDisplay } from '../../../../src/shared/worktree/git-identity-display' import { buildWorkspaceDetailLinks, buildWorkspaceOrigin, workspaceNotes, type WorkspaceDetailLink, type WorkspaceOrigin -} from '../worktree/workspace-details-model' -import type { Worktree } from '../worktree/workspace-list-types' -import type { Translate } from '../i18n/mobile-i18n' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +} from '../../worktree/workspace-details-model' +import type { Worktree } from '../../worktree/workspace-list-types' +import type { Translate } from '../../i18n/mobile-i18n' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' const ICON_SIZE = 16 diff --git a/mobile/src/host-screen/workspace-actions/workspace-rename-mutation.ts b/mobile/src/host-screen/workspace-actions/workspace-rename-mutation.ts new file mode 100644 index 00000000000..88fa50851dc --- /dev/null +++ b/mobile/src/host-screen/workspace-actions/workspace-rename-mutation.ts @@ -0,0 +1,22 @@ +import { worktreeDisplayNameWrite } from '../host-screen-operations' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import { defineHostMutation } from '../../query/host-mutation' + +async function sendWorkspaceRename( + client: RpcClient, + vars: { worktreeId: string; displayName: string } +): Promise { + worktreeDisplayNameWrite.interpret( + await worktreeDisplayNameWrite.request(client, { + worktree: `id:${vars.worktreeId}`, + displayName: vars.displayName + }) + ) +} + +export const workspaceRename = defineHostMutation({ + offline: 'draft', + send: sendWorkspaceRename, + // Renaming is idempotent, so a lost reply is settled by sending the same name again. + confirm: sendWorkspaceRename +}) diff --git a/mobile/src/host-screen/workspace-rename-sheet.tsx b/mobile/src/host-screen/workspace-actions/workspace-rename-sheet.tsx similarity index 78% rename from mobile/src/host-screen/workspace-rename-sheet.tsx rename to mobile/src/host-screen/workspace-actions/workspace-rename-sheet.tsx index 52f525515db..a9bc55d11bc 100644 --- a/mobile/src/host-screen/workspace-rename-sheet.tsx +++ b/mobile/src/host-screen/workspace-actions/workspace-rename-sheet.tsx @@ -1,10 +1,10 @@ -import { automaticWorktreeDisplayName } from '../../../src/shared/worktree/display-name-provenance' -import { TextInputSheetContent } from '../components/TextInputModal' -import { useDraftRenameSheet } from '../components/use-draft-rename-sheet' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import type { HostMutationPhase } from '../query/host-mutation' -import type { Worktree } from '../worktree/workspace-list-types' -import { getWorktreeRowIdentity } from '../worktree/worktree-host-row-identity' +import { automaticWorktreeDisplayName } from '../../../../src/shared/worktree/display-name-provenance' +import { TextInputSheetContent } from '../../components/TextInputModal' +import { useDraftRenameSheet } from '../../components/use-draft-rename-sheet' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' +import type { HostMutationPhase } from '../../query/host-mutation' +import type { Worktree } from '../../worktree/workspace-list-types' +import { getWorktreeRowIdentity } from '../../worktree/worktree-host-row-identity' export function workspaceRenameDraftKey(hostId: string, row: Worktree): string { return `workspace-rename:${hostId}:${getWorktreeRowIdentity(row)}` diff --git a/mobile/src/host-screen/worktree-remove-request.ts b/mobile/src/host-screen/workspace-actions/worktree-remove-request.ts similarity index 78% rename from mobile/src/host-screen/worktree-remove-request.ts rename to mobile/src/host-screen/workspace-actions/worktree-remove-request.ts index 1d8fc8e8a4d..fda5a919efc 100644 --- a/mobile/src/host-screen/worktree-remove-request.ts +++ b/mobile/src/host-screen/workspace-actions/worktree-remove-request.ts @@ -1,6 +1,6 @@ -import { worktreeRemovalReplyTimeoutMs } from '../../../src/shared/worktree/archive-hook-removal-gate' -import { RECIPE_REMOVAL_REPLY_TIMEOUT_MS } from '../../../src/shared/recipe-host-removal' -import type { Worktree } from '../worktree/workspace-list-sections' +import { worktreeRemovalReplyTimeoutMs } from '../../../../src/shared/worktree/archive-hook-removal-gate' +import { RECIPE_REMOVAL_REPLY_TIMEOUT_MS } from '../../../../src/shared/recipe-host-removal' +import type { Worktree } from '../../worktree/workspace-list-types' export type WorktreeRemoveHooks = { runHooks: boolean diff --git a/mobile/src/host-screen/host-pending-creation-row.tsx b/mobile/src/host-screen/workspace-list/host-pending-creation-row.tsx similarity index 80% rename from mobile/src/host-screen/host-pending-creation-row.tsx rename to mobile/src/host-screen/workspace-list/host-pending-creation-row.tsx index 8be4f9e8ece..185643a13a0 100644 --- a/mobile/src/host-screen/host-pending-creation-row.tsx +++ b/mobile/src/host-screen/workspace-list/host-pending-creation-row.tsx @@ -1,9 +1,9 @@ import { ActivityIndicator, Text, View } from 'react-native' -import { colors, spacing, typography } from '../theme/mobile-theme' -import { themedStyleSheet } from '../theme/themed-styles' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import type { SectionPendingCreation } from '../worktree/workspace-list-types' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +import { colors, spacing, typography } from '../../theme/mobile-theme' +import { themedStyleSheet } from '../../theme/themed-styles' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import type { SectionPendingCreation } from '../../worktree/workspace-list-types' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' export function HostPendingCreationRow({ creation }: { creation: SectionPendingCreation }) { const t = useMobileTranslation() diff --git a/mobile/src/host-screen/host-workspace-list.tsx b/mobile/src/host-screen/workspace-list/host-workspace-list.tsx similarity index 88% rename from mobile/src/host-screen/host-workspace-list.tsx rename to mobile/src/host-screen/workspace-list/host-workspace-list.tsx index 745d53c1736..61d38dfb911 100644 --- a/mobile/src/host-screen/host-workspace-list.tsx +++ b/mobile/src/host-screen/workspace-list/host-workspace-list.tsx @@ -9,36 +9,36 @@ import { View } from 'react-native' import { ChevronRight, FolderTree, Pin } from 'lucide-react-native' -import { AuthFailedBanner } from '../components/AuthFailedBanner' -import { Presence } from '../components/AutoHeight' -import { DisclosureChevron } from '../components/DisclosureChevron' -import { HostDiagnosticsLink } from '../components/HostDiagnosticsLink' -import { HostOfflineBanner, useHostOfflineBanner } from '../components/HostOfflineBanner' -import { isConnectionOutage } from '../transport/connection/connection-health' -import { HostRouteNoticeBanner } from '../components/HostRouteNoticeBanner' -import { MobileRepoIcon } from '../components/MobileRepoIcon' -import type { CollapsingTitle } from '../components/nav-bar/PageTitle' -import { WorktreeListRow } from '../components/WorktreeListRow' -import { WORKTREE_ROW_LAYOUT } from '../components/worktree-list-row-layout' -import { colors, spacing } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { getWorktreeRowIdentity } from '../worktree/worktree-host-row-identity' +import { AuthFailedBanner } from '../../components/AuthFailedBanner' +import { Presence } from '../../components/AutoHeight' +import { DisclosureChevron } from '../../components/DisclosureChevron' +import { HostDiagnosticsLink } from '../../components/HostDiagnosticsLink' +import { HostOfflineBanner, useHostOfflineBanner } from '../../components/HostOfflineBanner' +import { isConnectionOutage } from '../../transport/connection/connection-health' +import { HostRouteNoticeBanner } from '../../components/HostRouteNoticeBanner' +import { MobileRepoIcon } from '../../components/MobileRepoIcon' +import type { CollapsingTitle } from '../../components/nav-bar/PageTitle' +import { WorktreeListRow } from '../../components/WorktreeListRow' +import { WORKTREE_ROW_LAYOUT } from '../../components/worktree-list-row-layout' +import { colors, spacing } from '../../theme/mobile-theme' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import { getWorktreeRowIdentity } from '../../worktree/worktree-host-row-identity' import { HostWorkspaceListReopen, HostWorkspaceListStates -} from '../worktree/host-workspace-list-states' -import { getWorktreeDotStatus, hasWorktreeAgentState } from '../worktree/workspace-list-ordering' -import type { Section, WorkspaceListItem, Worktree } from '../worktree/workspace-list-types' -import { DEFAULT_REPO_BADGE_COLOR } from '../../../src/shared/repo-badge-color' -import { getWorktreeCardDetails } from '../../../src/shared/worktree/card-properties' -import { hostScreenStyles as styles } from './host-screen-styles' -import { useRepoLookById, type RepoLook } from './host-repo-looks' +} from '../../worktree/host-workspace-list-states' +import { getWorktreeDotStatus, hasWorktreeAgentState } from '../../worktree/workspace-list-ordering' +import type { Section, WorkspaceListItem, Worktree } from '../../worktree/workspace-list-types' +import { DEFAULT_REPO_BADGE_COLOR } from '../../../../src/shared/repo-badge-color' +import { getWorktreeCardDetails } from '../../../../src/shared/worktree/card-properties' +import { hostScreenStyles as styles } from '../host-screen-styles' +import { useRepoLookById, type RepoLook } from '../catalog/host-repo-looks' import { HostPendingCreationRow } from './host-pending-creation-row' import { useSlowPendingKeys } from './use-slow-pending-keys' -import { useHostMutationDrafts } from '../query/host-mutation-drafts' -import { workspaceRenameDraftKey } from './workspace-rename-sheet' -import { projectGroupHeaderIndent } from '../worktree/project-group-indent' -import type { HostScreenController } from './use-host-screen-controller' +import { useHostMutationDrafts } from '../../query/host-mutation-drafts' +import { workspaceRenameDraftKey } from '../workspace-actions/workspace-rename-sheet' +import { projectGroupHeaderIndent } from '../../worktree/project-group-indent' +import type { HostScreenController } from '../use-host-screen-controller' export function HostWorkspaceList({ controller, diff --git a/mobile/src/host-screen/use-host-display-worktrees.ts b/mobile/src/host-screen/workspace-list/use-host-display-worktrees.ts similarity index 82% rename from mobile/src/host-screen/use-host-display-worktrees.ts rename to mobile/src/host-screen/workspace-list/use-host-display-worktrees.ts index 517c0924fa2..aac98051673 100644 --- a/mobile/src/host-screen/use-host-display-worktrees.ts +++ b/mobile/src/host-screen/workspace-list/use-host-display-worktrees.ts @@ -1,14 +1,14 @@ import { useMemo } from 'react' -import { useHostDescriptor } from '../host-descriptors/host-descriptor-store' -import type { ConnectionState } from '../transport/connection/connection-state' +import { useHostDescriptor } from '../../host-descriptors/host-descriptor-store' +import type { ConnectionState } from '../../transport/connection/connection-state' import { serverHostHealth, serverHostHealthLabels, withServerHosts -} from '../worktree/server-workspaces' -import { buildWorktreeHostContextOf } from '../worktree/worktree-host-context-labels' -import { applyWorktreeRowDisplayState } from '../worktree/worktree-host-row-identity' -import type { HostScreenState } from './use-host-screen-state' +} from '../../worktree/server-workspaces' +import { buildWorktreeHostContextOf } from '../../worktree/worktree-host-context-labels' +import { applyWorktreeRowDisplayState } from '../../worktree/worktree-host-row-identity' +import type { HostScreenState } from '../use-host-screen-state' /** The desktop's platform: its own report, or the paired descriptor's before that lands. */ export function useResolvedHostPlatform( diff --git a/mobile/src/host-screen/use-host-list-sections.ts b/mobile/src/host-screen/workspace-list/use-host-list-sections.ts similarity index 78% rename from mobile/src/host-screen/use-host-list-sections.ts rename to mobile/src/host-screen/workspace-list/use-host-list-sections.ts index 96ca76f8333..e1ac7799ab2 100644 --- a/mobile/src/host-screen/use-host-list-sections.ts +++ b/mobile/src/host-screen/workspace-list/use-host-list-sections.ts @@ -1,25 +1,28 @@ import { useMemo } from 'react' -import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../src/shared/sidebar-inputs-switch' -import { buildSidebarFeedRows } from '../../../src/shared/sidebar-list/sidebar-feed-rows' -import { repoIconFeedKey } from '../../../src/shared/sidebar-list/repo-icon-data-urls-contract' -import type { ProjectOrderBy } from '../../../src/shared/ui-chrome-types' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import { buildProjectSectionCatalog } from '../worktree/project-section-catalog' -import { useWorkspaceSections } from '../worktree/use-workspace-sections' -import { buildFilterRepos, type WorkspaceSectionRepo } from '../worktree/workspace-filter-repos' -import type { Section, SectionRepo, Worktree } from '../worktree/workspace-list-types' -import type { WorktreeHostContextOf } from '../worktree/worktree-host-context-labels' -import type { MobileLineageStores } from '../worktree/mobile-workspace-lineage' -import { useLastVisitedWorktreeOnHost } from '../worktree/use-last-visited-worktree-on-host' -import { rowFiltersForView } from '../worktree/workspace-view-settings' -import { buildSidebarFeedCatalog } from '../../../src/shared/sidebar-list/sidebar-feed-catalog' -import { buildHostFeedView } from './host-sidebar-feed-inputs' -import { buildHostFeedSections, buildPreFeedSections } from './host-sidebar-feed-sections' -import { useHostRepoIconDataUrls } from './use-host-repo-icon-data-urls' -import type { HostSidebarFeed } from './use-host-sidebar-feed' -import type { HostScreenState } from './use-host-screen-state' -import { indexHiddenProjects } from '../../../src/shared/hidden-projects' -import { LOCAL_EXECUTION_HOST_ID } from '../../../src/shared/execution-host' +import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../../src/shared/sidebar-inputs-switch' +import { buildSidebarFeedRows } from '../../../../src/shared/sidebar-list/sidebar-feed-rows' +import { repoIconFeedKey } from '../../../../src/shared/sidebar-list/repo-icon-data-urls-contract' +import type { ProjectOrderBy } from '../../../../src/shared/ui-chrome-types' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' +import { buildProjectSectionCatalog } from '../../worktree/project-section-catalog' +import { useWorkspaceSections } from '../../worktree/use-workspace-sections' +import { buildFilterRepos, type WorkspaceSectionRepo } from '../../worktree/workspace-filter-repos' +import type { Section, SectionRepo, Worktree } from '../../worktree/workspace-list-types' +import type { WorktreeHostContextOf } from '../../worktree/worktree-host-context-labels' +import type { MobileLineageStores } from '../../worktree/mobile-workspace-lineage' +import { useLastVisitedWorktreeOnHost } from '../../worktree/use-last-visited-worktree-on-host' +import { rowFiltersForView } from '../../worktree/workspace-view-settings' +import { buildSidebarFeedCatalog } from '../../../../src/shared/sidebar-list/sidebar-feed-catalog' +import { buildHostFeedView } from '../sidebar-feed/host-sidebar-feed-inputs' +import { + buildHostFeedSections, + buildPreFeedSections +} from '../sidebar-feed/host-sidebar-feed-sections' +import { useHostRepoIconDataUrls } from '../catalog/use-host-repo-icon-data-urls' +import type { HostSidebarFeed } from '../sidebar-feed/use-host-sidebar-feed' +import type { HostScreenState } from '../use-host-screen-state' +import { indexHiddenProjects } from '../../../../src/shared/hidden-projects' +import { LOCAL_EXECUTION_HOST_ID } from '../../../../src/shared/execution-host' export type HostListSectionsInputs = { client: RpcClient | null diff --git a/mobile/src/host-screen/use-slow-pending-keys.test.tsx b/mobile/src/host-screen/workspace-list/use-slow-pending-keys.test.tsx similarity index 100% rename from mobile/src/host-screen/use-slow-pending-keys.test.tsx rename to mobile/src/host-screen/workspace-list/use-slow-pending-keys.test.tsx diff --git a/mobile/src/host-screen/use-slow-pending-keys.ts b/mobile/src/host-screen/workspace-list/use-slow-pending-keys.ts similarity index 100% rename from mobile/src/host-screen/use-slow-pending-keys.ts rename to mobile/src/host-screen/workspace-list/use-slow-pending-keys.ts diff --git a/mobile/src/host-screen/phone-card-property-choices.ts b/mobile/src/host-screen/workspace-options/phone-card-property-choices.ts similarity index 75% rename from mobile/src/host-screen/phone-card-property-choices.ts rename to mobile/src/host-screen/workspace-options/phone-card-property-choices.ts index 66f4736120c..d68a4fa5e6e 100644 --- a/mobile/src/host-screen/phone-card-property-choices.ts +++ b/mobile/src/host-screen/workspace-options/phone-card-property-choices.ts @@ -1,5 +1,5 @@ -import type { WorkspaceViewChoice } from '../../../src/shared/workspace-view/workspace-view-options' -import type { WorktreeCardProperty } from '../../../src/shared/ui-chrome-types' +import type { WorkspaceViewChoice } from '../../../../src/shared/workspace-view/workspace-view-options' +import type { WorktreeCardProperty } from '../../../../src/shared/ui-chrome-types' // Omit Ports: the phone has no port scan; desktop retains it. const PHONE_HIDDEN_CARD_PROPERTIES: ReadonlySet = new Set(['ports']) diff --git a/mobile/src/host-screen/workspace-option-controls-copy.ts b/mobile/src/host-screen/workspace-options/workspace-option-controls-copy.ts similarity index 92% rename from mobile/src/host-screen/workspace-option-controls-copy.ts rename to mobile/src/host-screen/workspace-options/workspace-option-controls-copy.ts index 89d8213811b..188a6a2507c 100644 --- a/mobile/src/host-screen/workspace-option-controls-copy.ts +++ b/mobile/src/host-screen/workspace-options/workspace-option-controls-copy.ts @@ -1,9 +1,9 @@ -import * as viewOptions from '../../../src/shared/workspace-view/workspace-view-options' -import type { WorkspaceViewChoice } from '../../../src/shared/workspace-view/workspace-view-options' -import type { WorktreeCardProperty } from '../../../src/shared/ui-chrome-types' -import type { Translate } from '../i18n/mobile-i18n' +import * as viewOptions from '../../../../src/shared/workspace-view/workspace-view-options' +import type { WorkspaceViewChoice } from '../../../../src/shared/workspace-view/workspace-view-options' +import type { WorktreeCardProperty } from '../../../../src/shared/ui-chrome-types' +import type { Translate } from '../../i18n/mobile-i18n' import { phoneCardPropertyChoices } from './phone-card-property-choices' -import type { HostScreenController } from './use-host-screen-controller' +import type { HostScreenController } from '../use-host-screen-controller' function workspaceOptionChoices( choices: readonly WorkspaceViewChoice[], diff --git a/mobile/src/host-screen/workspace-option-controls.tsx b/mobile/src/host-screen/workspace-options/workspace-option-controls.tsx similarity index 91% rename from mobile/src/host-screen/workspace-option-controls.tsx rename to mobile/src/host-screen/workspace-options/workspace-option-controls.tsx index 95556c3c644..9863b9fe464 100644 --- a/mobile/src/host-screen/workspace-option-controls.tsx +++ b/mobile/src/host-screen/workspace-options/workspace-option-controls.tsx @@ -1,19 +1,19 @@ import { useState, type ComponentType } from 'react' import { View } from 'react-native' -import { MobileSearchField } from '../components/MobileSearchField' -import { MobileSegmentedControl } from '../components/MobileSegmentedControl' -import * as viewOptions from '../../../src/shared/workspace-view/workspace-view-options' +import { MobileSearchField } from '../../components/MobileSearchField' +import { MobileSegmentedControl } from '../../components/MobileSegmentedControl' +import * as viewOptions from '../../../../src/shared/workspace-view/workspace-view-options' import type { WorkspaceViewChoice, WorkspaceViewOptionKey -} from '../../../src/shared/workspace-view/workspace-view-options' -import * as hostVisibility from '../../../src/shared/workspace-view/workspace-host-visibility' -import type { ExecutionHostId } from '../../../src/shared/execution-host' -import type { WorktreeCardProperty } from '../../../src/shared/ui-chrome-types' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { hostScreenStyles as styles } from './host-screen-styles' +} from '../../../../src/shared/workspace-view/workspace-view-options' +import * as hostVisibility from '../../../../src/shared/workspace-view/workspace-host-visibility' +import type { ExecutionHostId } from '../../../../src/shared/execution-host' +import type { WorktreeCardProperty } from '../../../../src/shared/ui-chrome-types' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import { hostScreenStyles as styles } from '../host-screen-styles' import { withoutPhoneHiddenProperties } from './phone-card-property-choices' -import type { HostScreenController } from './use-host-screen-controller' +import type { HostScreenController } from '../use-host-screen-controller' import { groupByChoices, sortByChoices, @@ -23,11 +23,11 @@ import { cardPropertyChoices, cardDisplayChoices } from './workspace-option-controls-copy' -import { ListGroup, SectionHeader } from '../components/list/ListGroup' +import { ListGroup, SectionHeader } from '../../components/list/ListGroup' import { CheckRow, ChoiceRows, SwitchRow } from './workspace-option-rows' import { choiceLabel, summarizeChecklist } from './workspace-option-values' -import type { Translate } from '../i18n/mobile-i18n' -import { useMobileTranslation } from '../i18n/use-mobile-translation' +import type { Translate } from '../../i18n/mobile-i18n' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' export type WorkspaceOptionHost = { id: ExecutionHostId; label: string } diff --git a/mobile/src/host-screen/workspace-option-rows.tsx b/mobile/src/host-screen/workspace-options/workspace-option-rows.tsx similarity index 88% rename from mobile/src/host-screen/workspace-option-rows.tsx rename to mobile/src/host-screen/workspace-options/workspace-option-rows.tsx index a90265472df..caa67a9ced1 100644 --- a/mobile/src/host-screen/workspace-option-rows.tsx +++ b/mobile/src/host-screen/workspace-options/workspace-option-rows.tsx @@ -1,8 +1,8 @@ import type { ReactNode } from 'react' import { View } from 'react-native' -import { MobileSwitch } from '../components/MobileSwitch' -import { ListGroup, ListRow } from '../components/list/ListGroup' -import type { WorkspaceViewChoice } from '../../../src/shared/workspace-view/workspace-view-options' +import { MobileSwitch } from '../../components/MobileSwitch' +import { ListGroup, ListRow } from '../../components/list/ListGroup' +import type { WorkspaceViewChoice } from '../../../../src/shared/workspace-view/workspace-view-options' export function CheckRow(props: { label: string diff --git a/mobile/src/host-screen/workspace-option-values.ts b/mobile/src/host-screen/workspace-options/workspace-option-values.ts similarity index 100% rename from mobile/src/host-screen/workspace-option-values.ts rename to mobile/src/host-screen/workspace-options/workspace-option-values.ts diff --git a/mobile/src/host-screen/workspace-options-sheet.tsx b/mobile/src/host-screen/workspace-options/workspace-options-sheet.tsx similarity index 90% rename from mobile/src/host-screen/workspace-options-sheet.tsx rename to mobile/src/host-screen/workspace-options/workspace-options-sheet.tsx index 284c9d08a9d..d3757a8be0b 100644 --- a/mobile/src/host-screen/workspace-options-sheet.tsx +++ b/mobile/src/host-screen/workspace-options/workspace-options-sheet.tsx @@ -1,35 +1,35 @@ import { useMemo, type ReactNode } from 'react' import { ActivityIndicator, Text, View } from 'react-native' import { ChevronLeft } from 'lucide-react-native' -import { colors } from '../theme/mobile-theme' -import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { useBackClaim } from '../navigation/use-back-claim' +import { colors } from '../../theme/mobile-theme' +import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' +import { useBackClaim } from '../../navigation/use-back-claim' import { BottomDrawer, OptionPager, useOptionPager, type OptionPagerControls -} from '../components/bottom-drawer/BottomDrawer' -import { NavBarButton } from '../components/nav-bar/NavBarButton' -import { SheetHeader } from '../components/nav-bar/SheetHeader' +} from '../../components/bottom-drawer/BottomDrawer' +import { NavBarButton } from '../../components/nav-bar/NavBarButton' +import { SheetHeader } from '../../components/nav-bar/SheetHeader' import { LOCAL_EXECUTION_HOST_ID, parseExecutionHostId, type ExecutionHostId -} from '../../../src/shared/execution-host' +} from '../../../../src/shared/execution-host' import { getShownWorkspaceViewSections, type WorkspaceViewOptionKey, type WorkspaceViewSection -} from '../../../src/shared/workspace-view/workspace-view-options' -import { getHostContextLabel } from '../../../src/shared/worktree/host-context-labels' -import type { Translate } from '../i18n/mobile-i18n' -import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { resolveWorktreeHostId } from '../worktree/worktree-host-context-labels' -import { withServerHosts } from '../worktree/server-workspaces' -import { hostScreenStyles as styles } from './host-screen-styles' -import type { HostScreenController } from './use-host-screen-controller' -import { ListGroup, SectionHeader } from '../components/list/ListGroup' +} from '../../../../src/shared/workspace-view/workspace-view-options' +import { getHostContextLabel } from '../../../../src/shared/worktree/host-context-labels' +import type { Translate } from '../../i18n/mobile-i18n' +import { useMobileTranslation } from '../../i18n/use-mobile-translation' +import { resolveWorktreeHostId } from '../../worktree/worktree-host-context-labels' +import { withServerHosts } from '../../worktree/server-workspaces' +import { hostScreenStyles as styles } from '../host-screen-styles' +import type { HostScreenController } from '../use-host-screen-controller' +import { ListGroup, SectionHeader } from '../../components/list/ListGroup' import { DrillRow } from './workspace-option-rows' import { WORKSPACE_OPTION_CONTROLS, diff --git a/mobile/src/host-screen/worktree-remove-request.test.ts b/mobile/src/host-screen/worktree-remove-request.test.ts deleted file mode 100644 index a787e7ca421..00000000000 --- a/mobile/src/host-screen/worktree-remove-request.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { worktreeRemovalReplyTimeoutMs } from '../../../src/shared/worktree/archive-hook-removal-gate' -import { RECIPE_REMOVAL_REPLY_TIMEOUT_MS } from '../../../src/shared/recipe-host-removal' -import { buildWorktreeRemoveRequest } from './worktree-remove-request' - -describe('phone worktree delete request', () => { - it('keeps the ordinary delete for workspaces the desktop did not tag', () => { - expect(buildWorktreeRemoveRequest({ worktreeId: 'r::/w' }, { runHooks: false })).toEqual({ - params: { worktree: 'id:r::/w', force: true, runHooks: false }, - timeoutMs: worktreeRemovalReplyTimeoutMs(false) - }) - }) - - it('asks for the named recipe environment to be destroyed, and waits for its destroy step', () => { - const request = buildWorktreeRemoveRequest( - { worktreeId: 'r::/w', recipeEnvironment: { name: 'task' } }, - { runHooks: true } - ) - expect(request.params).toEqual({ - worktree: 'id:r::/w', - force: true, - runHooks: true, - destroyHost: true - }) - expect(request.timeoutMs).toBe(RECIPE_REMOVAL_REPLY_TIMEOUT_MS) - }) -}) diff --git a/mobile/src/mobile-web-shell/bridge/bridge-native-verbs.ts b/mobile/src/mobile-web-shell/bridge/bridge-native-verbs.ts index 7dfff72f157..25af8db2513 100644 --- a/mobile/src/mobile-web-shell/bridge/bridge-native-verbs.ts +++ b/mobile/src/mobile-web-shell/bridge/bridge-native-verbs.ts @@ -21,7 +21,7 @@ import { imageParamsSchema, imageResultSchema } from './bridge-image-verbs' import { BRIDGE_SPEECH_VERB, speechParamsSchema, speechResultSchema } from './bridge-speech-verbs' import { BRIDGE_STORE_VERB, storeParamsSchema, storeResultSchema } from './bridge-store-verb' import { PAGE_STORAGE_MAX_KEY_CHARS } from '../page-storage-keys' -import { mobileFileMediaMime, PDF_MIME_TYPE } from '../../files/mobile-file-media' +import { mobileFileMediaMime, PDF_MIME_TYPE } from '../../files/media/mobile-file-media' // Fence native-prefixed requests on-device; desktop allowlist refusal alone would hide leaked forwarding and depend on desktop version. // Native replies use existing frames/in-flight caps without runtime _meta; adding a verb needs a table row and handler, never a new frame kind. diff --git a/mobile/src/mobile-web-shell/shell-screen-route.test.ts b/mobile/src/mobile-web-shell/shell-screen-route.test.ts deleted file mode 100644 index 62da0363a8b..00000000000 --- a/mobile/src/mobile-web-shell/shell-screen-route.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { BRIDGE_MAX_ROUTE_PARAM_CHARS } from './bridge/bridge-caps' -import { BridgeInitRouteSchema, type BridgeInitRoute } from './bridge/bridge-envelope' -import { shellRouteHref } from './bridge/page-bootstrap' -import { shellScreenRoute, shellScreenRouteKey } from './shell-screen-route' -import { - mobileFilePreviewShellParams, - normalizeMobileFilePreviewRouteParams -} from '../files/mobile-file-preview-route' - -const PREVIEW_PATH = '/h/host-1/files/preview/wt-1' - -function previewRoute(absolutePath: string) { - const route = normalizeMobileFilePreviewRouteParams({ - hostId: 'host-1', - worktreeId: 'wt-1', - source: 'terminalArtifact', - absolutePath, - grantId: 'grant-1' - }) - if (!route.ok) { - throw new Error(route.message) - } - return { pathname: PREVIEW_PATH, params: mobileFilePreviewShellParams(route.params) } -} - -describe('the route a switch hands the shell', () => { - it('is one the page could actually be given', () => { - const route = previewRoute('/logs/run.txt') - expect(BridgeInitRouteSchema.safeParse(route).success).toBe(true) - expect(shellScreenRoute(route)).toEqual(route) - }) - - it('is nothing when a file path is longer than a param may be', () => { - // Use a real Windows long path: without a length guard the bridge drops init.route to null and shows an update refusal over a working native screen. - const route = previewRoute(`/logs/${'a'.repeat(BRIDGE_MAX_ROUTE_PARAM_CHARS)}.txt`) - expect(BridgeInitRouteSchema.safeParse(route).success).toBe(false) - expect(shellScreenRoute(route)).toBeNull() - }) - - it('is nothing when a worktree id is not a segment the page will route', () => { - // Dot segments survive encodeURIComponent and escape the intended /h/ prefix when resolved. - const route = { pathname: '/h/host-1/files/..', params: { name: 'Files' } } - // Prove schema refusal too so an always-null guard cannot pass. - expect(BridgeInitRouteSchema.safeParse(route).success).toBe(false) - expect(shellScreenRoute(route)).toBeNull() - }) - - it('keeps a path with a slash, a space and a dot segment, which are params and not segments', () => { - const route = { - pathname: '/h/host-1/files/preview/wt-1', - params: { relativePath: 'docs/../my notes/readme.md', source: 'worktree' } - } - expect(shellScreenRoute(route)).toEqual(route) - }) -}) - -describe('the key a shell screen remounts on', () => { - // Pin native remount keys to the page serializer’s URL; importing its RPC-adjacent module into native routes would cross the platform boundary. - it.each([ - { pathname: '/h/host-1/files/wt-1' }, - { pathname: '/h/host-1/files/wt-1', params: { name: 'my worktree' } }, - { - pathname: '/h/host-1/files/preview/wt-1', - params: { relativePath: 'docs/my notes/readme.md', source: 'worktree', line: '12' } - } - ])('is the href the page would write into its history: %o', (route) => { - expect(shellScreenRouteKey(route)).toBe(shellRouteHref(route)) - }) -}) diff --git a/mobile/src/mobile-web-shell/shell-screen-route.ts b/mobile/src/mobile-web-shell/shell-screen-route.ts index 79dfadc1924..4cbafc53914 100644 --- a/mobile/src/mobile-web-shell/shell-screen-route.ts +++ b/mobile/src/mobile-web-shell/shell-screen-route.ts @@ -7,7 +7,7 @@ export function shellScreenRoute(route: BridgeInitRoute): BridgeInitRoute | null } // Include params in remount identity so files/labels update the document; pane taps deliberately use a separate in-place init path. -// Match the page bootstrap serializer, pinned by tests instead of importing its RPC/document dependencies into native routes. +// Match the page bootstrap serializer without importing its RPC/document dependencies into native routes. export function shellScreenRouteKey(route: BridgeInitRoute): string { const search = new URLSearchParams(route.params ?? {}).toString() return search === '' ? route.pathname : `${route.pathname}?${search}` diff --git a/mobile/src/new-workspace/use-host-label-by-id.ts b/mobile/src/new-workspace/use-host-label-by-id.ts index 6ea165fc630..8bc1410d4c1 100644 --- a/mobile/src/new-workspace/use-host-label-by-id.ts +++ b/mobile/src/new-workspace/use-host-label-by-id.ts @@ -4,7 +4,7 @@ import { HOST_LIST_TARGET, hostRepoLabelsQuery, NO_HOST_LIST_INPUT -} from '../host-screen/host-list-queries' +} from '../host-screen/catalog/host-list-queries' import { useHostQuery } from '../query/host-query' import type { RpcClient } from '../transport/rpc/rpc-client-contract' diff --git a/mobile/src/new-workspace/use-host-worktree-rows.ts b/mobile/src/new-workspace/use-host-worktree-rows.ts index 8b3a5693abe..d78cf11e4ed 100644 --- a/mobile/src/new-workspace/use-host-worktree-rows.ts +++ b/mobile/src/new-workspace/use-host-worktree-rows.ts @@ -5,7 +5,7 @@ import { HOST_LIST_TARGET, hostWorktreeCatalogQuery, NO_HOST_LIST_INPUT -} from '../host-screen/host-list-queries' +} from '../host-screen/catalog/host-list-queries' type WorktreeRow = Pick diff --git a/mobile/src/platform/native-pdf-view.ts b/mobile/src/platform/native-pdf-view.ts index 4f86f7c2771..c1627e2573a 100644 --- a/mobile/src/platform/native-pdf-view.ts +++ b/mobile/src/platform/native-pdf-view.ts @@ -4,12 +4,12 @@ import { File as FsFile } from 'expo-file-system' import { useFocusEffect, useNavigation } from 'expo-router' import { presentPdf } from '../../modules/orca-mobile-web-shell/src' import { getRuntimePathBasename } from '../../../src/shared/cross-platform-path' -import { PDF_MIME_TYPE } from '../files/mobile-file-media' +import { PDF_MIME_TYPE } from '../files/media/mobile-file-media' import { downloadMobileFileMedia, MobileMediaHostOutdatedError, type MobileMediaSink -} from '../files/mobile-file-media-download' +} from '../files/media/mobile-file-media-download' import { pdfViewParamsSchema, type BridgePdfOpenParams diff --git a/mobile/src/query/first-paint-query-keys.ts b/mobile/src/query/first-paint-query-keys.ts index c36ff57d591..38a7e5db0b2 100644 --- a/mobile/src/query/first-paint-query-keys.ts +++ b/mobile/src/query/first-paint-query-keys.ts @@ -10,9 +10,9 @@ import { hostWorktreeCatalogKey, hostWorktreeCatalogQuery, NO_HOST_LIST_INPUT -} from '../host-screen/host-list-queries' -import { hostViewFamily, hostViewKey } from '../host-screen/host-view-memory' -import { hostProjectSectionsQuery } from '../host-screen/use-host-project-sections' +} from '../host-screen/catalog/host-list-queries' +import { hostViewFamily, hostViewKey } from '../host-screen/view-settings/host-view-memory' +import { hostProjectSectionsQuery } from '../host-screen/catalog/use-host-project-sections' import { sessionTabsFamily, sessionTabsKey } from '../session/tabs/mobile-session-last-tabs' import type { MobileSessionTab } from '../session/tabs/mobile-session-tabs' import { activeSessionTabOf } from '../session/tabs/active-session-tab' diff --git a/mobile/src/query/host-query-persistence.test.ts b/mobile/src/query/host-query-persistence.test.ts index 4439806d94c..153cf64c71d 100644 --- a/mobile/src/query/host-query-persistence.test.ts +++ b/mobile/src/query/host-query-persistence.test.ts @@ -1,12 +1,11 @@ -import { hashKey, QueryClient } from '@tanstack/react-query' +import { QueryClient } from '@tanstack/react-query' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { OrcaQueryStoreBackend } from '../device-store/query-storage' -import { hostWorktreeCatalogKey, hostWorktreeCatalogQuery } from '../host-screen/host-list-queries' import { - createHostQueryPersistence, - holdsRestoredHostQueryData, - persistedHostQueryKey -} from './host-query-persistence' + hostWorktreeCatalogKey, + hostWorktreeCatalogQuery +} from '../host-screen/catalog/host-list-queries' +import { createHostQueryPersistence, persistedHostQueryKey } from './host-query-persistence' vi.mock('react-native', () => ({ AppState: {} })) @@ -123,44 +122,4 @@ describe('createHostQueryPersistence', () => { expect(relaunched.getQueryData(key)).toBe(live) }) - - it.each([ - { replaceOnHearing: false, expected: [true, false] }, - { replaceOnHearing: true, expected: [true, false, false] } - ])( - "marks restored data as an earlier launch's before anyone hears of it, until this launch's own answer (a listener's replacement: $replaceOnHearing)", - async ({ replaceOnHearing, expected }) => { - const store = memoryStore('host-a') - await storeFromEarlierLaunch(store, [liveRow]) - - const relaunched = new QueryClient() - const persistence = createHostQueryPersistence(relaunched, store.backend) - relaunched.getQueryCache().build(relaunched, { - queryKey: key, - meta: { persist: hostWorktreeCatalogQuery.persist } - }) - const heard: boolean[] = [] - const replacement = { snapshotId: 'snap-8', worktrees: [liveRow] } - relaunched.getQueryCache().subscribe((event) => { - if (event.type === 'updated' && event.action.type === 'success') { - heard.push(holdsRestoredHostQueryData(relaunched, hashKey(key))) - // A listener replacing the restored rows as it hears of them. - if (replaceOnHearing && heard.length === 1) { - relaunched.setQueryData(key, replacement, { updatedAt: SAVED_AT - 2 }) - } - } - }) - const restoring = persistence.restore(firstPaint) - await vi.runAllTimersAsync() - await restoring - // A live answer timed before the stored one, as after a clock correction, is still this launch's. - relaunched.setQueryData( - key, - { snapshotId: 'snap-8', worktrees: [liveRow] }, - { updatedAt: SAVED_AT - 1 } - ) - - expect(heard).toEqual(expected) - } - ) }) diff --git a/mobile/src/session/MobileSessionActiveContent.tsx b/mobile/src/session/MobileSessionActiveContent.tsx index b23e9941286..b4f4232506a 100644 --- a/mobile/src/session/MobileSessionActiveContent.tsx +++ b/mobile/src/session/MobileSessionActiveContent.tsx @@ -32,7 +32,7 @@ import { MobileSessionContentState } from './MobileSessionContentState' import { MarkdownReader } from '../files/session-documents/MobileSessionMarkdownReader' import { markdownTabDraftKey } from '../drafts/document-edit-draft-rows' import { workspaceDraftRowKey } from '../drafts/durable-draft-rows' -import { loadMarkdownPreviewImage } from '../files/mobile-markdown-image-source' +import { loadMarkdownPreviewImage } from '../files/markdown/mobile-markdown-image-source' export function MobileSessionActiveContent({ controller diff --git a/mobile/src/session/MobileSourceControlButton.tsx b/mobile/src/session/MobileSourceControlButton.tsx index 132e05fea14..b28abc798d0 100644 --- a/mobile/src/session/MobileSourceControlButton.tsx +++ b/mobile/src/session/MobileSourceControlButton.tsx @@ -6,7 +6,7 @@ import { HOST_LIST_TARGET, hostWorktreeCatalogQuery, NO_HOST_LIST_INPUT -} from '../host-screen/host-list-queries' +} from '../host-screen/catalog/host-list-queries' import { useMobileTranslation } from '../i18n/use-mobile-translation' import { useHostQuery } from '../query/host-query' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' diff --git a/mobile/src/session/SessionDockPanelContent.tsx b/mobile/src/session/SessionDockPanelContent.tsx index e4bcfd06bf6..0b776e20e40 100644 --- a/mobile/src/session/SessionDockPanelContent.tsx +++ b/mobile/src/session/SessionDockPanelContent.tsx @@ -1,6 +1,6 @@ import { memo } from 'react' import { MobileSourceControlRoutePanel } from '../source-control/MobileSourceControlRoutePanel' -import { MobileFileExplorerPanel } from '../files/MobileFileExplorerPanel' +import { MobileFileExplorerPanel } from '../files/explorer/MobileFileExplorerPanel' import type { ActivePanel } from './session-panel-host' export type SessionDockPanelContentProps = { diff --git a/mobile/src/session/native-chat/agent-session/mobile-structured-create-operations.ts b/mobile/src/session/native-chat/agent-session/mobile-structured-create-operations.ts index a6a263cd543..10274054a00 100644 --- a/mobile/src/session/native-chat/agent-session/mobile-structured-create-operations.ts +++ b/mobile/src/session/native-chat/agent-session/mobile-structured-create-operations.ts @@ -1,6 +1,6 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../../../transport/rpc/rpc-operation' import { rpcResultVariant } from '../../../transport/rpc/rpc-operation-result-reader' -import '../../../files/mobile-file-preview-operations' +import '../../../files/preview/mobile-file-preview-operations' import { sessionLaunchUnreadReplySchema } from '../../../transport/rpc/rpc-uninterpreted-reply-schemas' import '../../../agent-history/mobile-ai-vault-resume-reply-schema' import '../../../browser/tab-navigation/mobile-session-browser-reply-schema' diff --git a/mobile/src/session/native-chat/composer/MobileNativeChatComposerAttachments.tsx b/mobile/src/session/native-chat/composer/MobileNativeChatComposerAttachments.tsx index f55e60f05ce..1fea9f8088f 100644 --- a/mobile/src/session/native-chat/composer/MobileNativeChatComposerAttachments.tsx +++ b/mobile/src/session/native-chat/composer/MobileNativeChatComposerAttachments.tsx @@ -14,7 +14,7 @@ import { FileText, Image as ImageIcon, X } from 'lucide-react-native' import { colors, concentricRadius, radii, spacing, pressedStyle } from '../../../theme/mobile-theme' import { themedStyleSheet } from '../../../theme/themed-styles' import { useMobileColorScheme } from '../../../theme/use-mobile-color-scheme' -import { formatPreviewByteLength } from '../../../files/mobile-file-preview-response' +import { formatPreviewByteLength } from '../../../files/preview/mobile-file-preview-response' import type { PendingNativeChatImage } from './mobile-native-chat-image-attachment' import type { PendingNativeChatFile } from './mobile-native-chat-file-attachment' import { useMobileNativeChatOpenFile } from '../mobile-native-chat-open-file-context' diff --git a/mobile/src/session/native-chat/composer/use-mobile-native-chat-file-search.ts b/mobile/src/session/native-chat/composer/use-mobile-native-chat-file-search.ts index 642420bd6ed..1de6d1ac320 100644 --- a/mobile/src/session/native-chat/composer/use-mobile-native-chat-file-search.ts +++ b/mobile/src/session/native-chat/composer/use-mobile-native-chat-file-search.ts @@ -7,7 +7,7 @@ import { import { nativeChatFileInventoryRead, nativeChatFileSearchRead -} from '../../../files/native-chat-file-search-operations' +} from '../../../files/explorer/native-chat-file-search-operations' import { getPreparedQuickOpenFiles, rankQuickOpenFiles diff --git a/mobile/src/session/native-chat/input/mobile-native-chat-options-operations.ts b/mobile/src/session/native-chat/input/mobile-native-chat-options-operations.ts index 51337993088..fea9c439707 100644 --- a/mobile/src/session/native-chat/input/mobile-native-chat-options-operations.ts +++ b/mobile/src/session/native-chat/input/mobile-native-chat-options-operations.ts @@ -1,6 +1,6 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../../../transport/rpc/rpc-operation' import { rpcResultVariant } from '../../../transport/rpc/rpc-operation-result-reader' -import '../../../files/mobile-file-preview-operations' +import '../../../files/preview/mobile-file-preview-operations' import { sessionLaunchUnreadReplySchema } from '../../../transport/rpc/rpc-uninterpreted-reply-schemas' import '../../../agent-history/mobile-ai-vault-resume-reply-schema' import '../../../browser/tab-navigation/mobile-session-browser-reply-schema' diff --git a/mobile/src/session/native-chat/visual/mobile-native-chat-image-preview.ts b/mobile/src/session/native-chat/visual/mobile-native-chat-image-preview.ts index 715293a1ed7..f07eafa7db5 100644 --- a/mobile/src/session/native-chat/visual/mobile-native-chat-image-preview.ts +++ b/mobile/src/session/native-chat/visual/mobile-native-chat-image-preview.ts @@ -1,7 +1,7 @@ import { buildImageDataUri } from '../../../../../src/shared/file-preview/image-data-uri' import { isAgentSessionAttachmentStorePath } from '../../../../../src/shared/agent-session/agent-session-attachments' import { PrioritySemaphore } from '../../../../../src/shared/priority-semaphore' -import { filePreviewImageSchema } from '../../../files/file-preview-reply-schema' +import { filePreviewImageSchema } from '../../../files/preview/file-preview-reply-schema' import { bindDeferredRpcOperation, defineRpcOperation } from '../../../transport/rpc/rpc-operation' import { rpcResultVariant } from '../../../transport/rpc/rpc-operation-result-reader' import type { RpcClient } from '../../../transport/rpc/rpc-client-contract' diff --git a/mobile/src/session/use-mobile-session-content-create-actions.ts b/mobile/src/session/use-mobile-session-content-create-actions.ts index cec20da1636..b4dc401f892 100644 --- a/mobile/src/session/use-mobile-session-content-create-actions.ts +++ b/mobile/src/session/use-mobile-session-content-create-actions.ts @@ -1,6 +1,6 @@ import { normalizeBrowserUrl } from '../browser/pane-view/browser-url' import { translate } from '../i18n/mobile-i18n' -import { captureMobileFileMutationOwnership } from '../files/mobile-file-mutation-ownership' +import { captureMobileFileMutationOwnership } from '../files/mutation-ownership/mobile-file-mutation-ownership' import { browserGoBack, browserGoForward, @@ -11,7 +11,7 @@ import { sessionBrowserTabCreate } from '../browser/tab-navigation/mobile-sessio import { sessionMarkdownNoteCreate } from '../files/session-documents/mobile-markdown-tab-operations' import { interpretOrThrowRefusalMessage } from '../transport/rpc/rpc-refusal-message' import type { MobileBrowserNavigationMethod } from './tabs/MobileBrowserTabActionSheet' -import { isFileExistsErrorMessage } from '../files/file-exists-error' +import { isFileExistsErrorMessage } from '../files/mutation-ownership/file-exists-error' import type { MobileSessionTab } from './tabs/mobile-session-tabs' import type { MobileSessionTerminalCreateActionsModel } from '../terminal/session/use-mobile-session-terminal-create-actions' diff --git a/mobile/src/source-control/folder/use-mobile-folder-attached-worktrees.ts b/mobile/src/source-control/folder/use-mobile-folder-attached-worktrees.ts index 027e2836d1a..0f5d3fce4ce 100644 --- a/mobile/src/source-control/folder/use-mobile-folder-attached-worktrees.ts +++ b/mobile/src/source-control/folder/use-mobile-folder-attached-worktrees.ts @@ -8,7 +8,7 @@ import { hostServerWorkspacesQuery, hostWorktreeCatalogQuery, NO_HOST_LIST_INPUT -} from '../../host-screen/host-list-queries' +} from '../../host-screen/catalog/host-list-queries' import { useWorkspaceExecutionHost } from '../../navigation/workspace-execution-host' import { useHostQuery } from '../../query/host-query' import { useHostClient } from '../../transport/connection/host-client-hooks' diff --git a/mobile/src/stats/home-stats-summary-read.ts b/mobile/src/stats/home-stats-summary-read.ts index e1ee4807167..07fc222ce15 100644 --- a/mobile/src/stats/home-stats-summary-read.ts +++ b/mobile/src/stats/home-stats-summary-read.ts @@ -1,6 +1,6 @@ import { z } from 'zod' import { salvagedOptional } from '../../../src/shared/zod-salvage' -import { HOST_LIST_TARGET, NO_HOST_LIST_INPUT } from '../host-screen/host-list-queries' +import { HOST_LIST_TARGET, NO_HOST_LIST_INPUT } from '../host-screen/catalog/host-list-queries' import { defineHostQuery } from '../query/host-query' import { hostQueryKey } from '../query/host-query-key' import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc/rpc-operation' diff --git a/mobile/src/test-support/rpc-recording/adapters/client-event-stream-mount-adapters.ts b/mobile/src/test-support/rpc-recording/adapters/client-event-stream-mount-adapters.ts index 3fa2de1e3e5..86f6e5925e0 100644 --- a/mobile/src/test-support/rpc-recording/adapters/client-event-stream-mount-adapters.ts +++ b/mobile/src/test-support/rpc-recording/adapters/client-event-stream-mount-adapters.ts @@ -1,6 +1,6 @@ import type * as UseLiveWorktreeNameModule from '../../../worktree/use-live-worktree-name' import type * as HostWorktreeRefreshModule from '../../../worktree/host-worktree-refresh' -import type * as HostListQueries from '../../../host-screen/host-list-queries' +import type * as HostListQueries from '../../../host-screen/catalog/host-list-queries' import type * as HostQueryModule from '../../../query/host-query' import { hookMount } from '../hook-mount' import { hookScreenMount } from '../mounted-screen-tree' @@ -53,7 +53,7 @@ export function clientEventStreamMountAdapters( 'mobile/src/query/host-query.ts' ) const queries = modules.load( - 'mobile/src/host-screen/host-list-queries.ts' + 'mobile/src/host-screen/catalog/host-list-queries.ts' ) let status: string | null = null const hook = hookMount(() => { diff --git a/mobile/src/test-support/rpc-recording/adapters/file-explorer-screen-mount-adapters.ts b/mobile/src/test-support/rpc-recording/adapters/file-explorer-screen-mount-adapters.ts index 41b41d8facd..0f1b86b63a7 100644 --- a/mobile/src/test-support/rpc-recording/adapters/file-explorer-screen-mount-adapters.ts +++ b/mobile/src/test-support/rpc-recording/adapters/file-explorer-screen-mount-adapters.ts @@ -1,3 +1,4 @@ +import type * as FileExplorerScreenModule from '../../../files/explorer/MobileFileExplorerPanel' import { createElement } from 'react' import { projectMountedScreen, renderedElementProps, screenMount } from '../mounted-screen-tree' import { mountFixture } from '../recorder-fixture-shape' @@ -19,8 +20,8 @@ export function fileExplorerScreenMountAdapters( ): Record { return { 'files.explorer-screen': ({ client, effect }) => { - const Panel = modules.load( - 'mobile/src/files/MobileFileExplorerPanel.tsx' + const Panel = modules.load( + 'mobile/src/files/explorer/MobileFileExplorerPanel.tsx' ).MobileFileExplorerPanel const hostClientContext = loadHostClientContext(modules) const context = mountFixture({ diff --git a/mobile/src/test-support/rpc-recording/adapters/file-request-mount-adapters.ts b/mobile/src/test-support/rpc-recording/adapters/file-request-mount-adapters.ts index 69aee295011..a838c88cd6a 100644 --- a/mobile/src/test-support/rpc-recording/adapters/file-request-mount-adapters.ts +++ b/mobile/src/test-support/rpc-recording/adapters/file-request-mount-adapters.ts @@ -1,3 +1,6 @@ +import type * as FileMutationOwnershipModule from '../../../files/mutation-ownership/mobile-file-mutation-ownership' +import type * as FilePreviewRequestModule from '../../../files/preview/mobile-file-preview-request' +import type * as FileTabDocModule from '../../../files/session-documents/mobile-file-tab-doc' import type { MountAdapter } from '../recording-scenario' import type { operationModuleLoader } from '../operation-module-loader' @@ -21,8 +24,8 @@ export function fileRequestMountAdapters( ): Record { return { 'files.mutation-ownership': ({ client }) => { - const capture = modules.load( - 'mobile/src/files/mobile-file-mutation-ownership.ts' + const capture = modules.load( + 'mobile/src/files/mutation-ownership/mobile-file-mutation-ownership.ts' ).captureMobileFileMutationOwnership let ownership: unknown = 'uncaptured' return { @@ -36,8 +39,8 @@ export function fileRequestMountAdapters( } }, 'files.preview-load': ({ client, effect }) => { - const load = modules.load( - 'mobile/src/files/mobile-file-preview-request.ts' + const load = modules.load( + 'mobile/src/files/preview/mobile-file-preview-request.ts' ).loadMobileFilePreview let preview: unknown = 'unloaded' return { @@ -59,8 +62,8 @@ export function fileRequestMountAdapters( } }, 'files.preview-save': ({ client, effect }) => { - const save = modules.load( - 'mobile/src/files/mobile-file-preview-request.ts' + const save = modules.load( + 'mobile/src/files/preview/mobile-file-preview-request.ts' ).saveMobileTerminalArtifactPreview let saved: unknown = 'unsaved' return { @@ -78,8 +81,8 @@ export function fileRequestMountAdapters( } }, 'files.tab-doc': ({ client }) => { - const resolve = modules.load( - 'mobile/src/files/mobile-file-tab-doc.ts' + const resolve = modules.load( + 'mobile/src/files/session-documents/mobile-file-tab-doc.ts' ).resolveMobileFileTabDoc const docs: Record = {} return { diff --git a/mobile/src/test-support/rpc-recording/adapters/host-screen-mount-adapters.ts b/mobile/src/test-support/rpc-recording/adapters/host-screen-mount-adapters.ts index 269cec1e798..c502d7a387d 100644 --- a/mobile/src/test-support/rpc-recording/adapters/host-screen-mount-adapters.ts +++ b/mobile/src/test-support/rpc-recording/adapters/host-screen-mount-adapters.ts @@ -1,3 +1,4 @@ +import type * as HostViewSettingsModule from '../../../host-screen/view-settings/use-host-view-settings' import type { MountAdapter } from '../recording-scenario' import { INITIAL_MOBILE_VIEW } from '../../../worktree/workspace-view-settings' import { hookMount, performHookAction } from '../hook-mount' @@ -9,9 +10,9 @@ export function hostScreenMountAdapters( ): Record { return { 'host.view-settings': (context) => { - const useViewSettings = modules.load< - typeof import('../../../host-screen/use-host-view-settings') - >('mobile/src/host-screen/use-host-view-settings.ts').useHostViewSettings + const useViewSettings = modules.load( + 'mobile/src/host-screen/view-settings/use-host-view-settings.ts' + ).useHostViewSettings const state = observableModel(context, { clientRef: { current: context.client }, view: INITIAL_MOBILE_VIEW, diff --git a/mobile/src/test-support/rpc-recording/adapters/host-worktree-action-mount-adapters.ts b/mobile/src/test-support/rpc-recording/adapters/host-worktree-action-mount-adapters.ts index d927a211549..54cc47e1bdc 100644 --- a/mobile/src/test-support/rpc-recording/adapters/host-worktree-action-mount-adapters.ts +++ b/mobile/src/test-support/rpc-recording/adapters/host-worktree-action-mount-adapters.ts @@ -1,3 +1,4 @@ +import type * as HostWorktreeActionsModule from '../../../host-screen/workspace-actions/use-host-worktree-actions' import type { MountAdapter } from '../recording-scenario' import { hookMount, performHookAction } from '../hook-mount' import { observableModel, projectObservable } from '../observable-model' @@ -24,9 +25,9 @@ export function hostWorktreeActionMountAdapters( ): Record { return { 'host.worktree-actions': (context) => { - const useActions = modules.load< - typeof import('../../../host-screen/use-host-worktree-actions') - >('mobile/src/host-screen/use-host-worktree-actions.ts').useHostWorktreeActions + const useActions = modules.load( + 'mobile/src/host-screen/workspace-actions/use-host-worktree-actions.ts' + ).useHostWorktreeActions const state = observableModel(context, { pinnedIds: new Set(), worktrees: [ROW], diff --git a/mobile/src/test-support/rpc-recording/adapters/settings-mount-adapters.ts b/mobile/src/test-support/rpc-recording/adapters/settings-mount-adapters.ts index 734ad92cd5d..120f69c359c 100644 --- a/mobile/src/test-support/rpc-recording/adapters/settings-mount-adapters.ts +++ b/mobile/src/test-support/rpc-recording/adapters/settings-mount-adapters.ts @@ -1,9 +1,9 @@ import type * as UsePrBotAuthorOverridesModule from '../../../source-control/review/use-pr-bot-author-overrides' import type * as UseNewWorkspaceRuntimeContextModule from '../../../components/use-new-workspace-runtime-context' -import type * as UseHostRepoMetadataModule from '../../../host-screen/use-host-repo-metadata' +import type * as UseHostRepoMetadataModule from '../../../host-screen/catalog/use-host-repo-metadata' import type * as UseMobileTasksRuntimeHydrationModule from '../../../tasks/screen-state/use-mobile-tasks-runtime-hydration' import { notifyManager, useQueryClient, type QueryClient } from '@tanstack/react-query' -import type * as HostListQueries from '../../../host-screen/host-list-queries' +import type * as HostListQueries from '../../../host-screen/catalog/host-list-queries' import type * as ResumeMetadata from '../../../agent-history/mobile-agent-history-resume-metadata' import { hostQueryKey, hostScopeKey } from '../../../query/host-query-key' import type { OperationExposure } from '../operation-module-loader' @@ -94,10 +94,10 @@ export function settingsMountAdapters( }, 'settings.repo-metadata': (context) => { const useMetadata = modules.load( - 'mobile/src/host-screen/use-host-repo-metadata.ts' + 'mobile/src/host-screen/catalog/use-host-repo-metadata.ts' ).useHostRepoMetadata const queries = modules.load( - 'mobile/src/host-screen/host-list-queries.ts' + 'mobile/src/host-screen/catalog/host-list-queries.ts' ) const state = observableModel(context, { clientRef: { current: context.client } }) // A fault schedule ends with the read settling on a timeout, after the recorder's last zero-time drain; deliver diff --git a/mobile/src/worktree/home-host-worktree-fetch.ts b/mobile/src/worktree/home-host-worktree-fetch.ts index 53e48cd9f70..1abb6cf37c9 100644 --- a/mobile/src/worktree/home-host-worktree-fetch.ts +++ b/mobile/src/worktree/home-host-worktree-fetch.ts @@ -2,7 +2,7 @@ import * as z from 'zod' import { HOME_WORKTREE_COUNTS_FAMILY, type NO_HOST_LIST_INPUT -} from '../host-screen/host-list-queries' +} from '../host-screen/catalog/host-list-queries' import { defineHostQuery } from '../query/host-query' import { homeAgentEntries, diff --git a/mobile/src/worktree/host-worktree-refresh.ts b/mobile/src/worktree/host-worktree-refresh.ts index 0231ff7e6fd..f933f3f496a 100644 --- a/mobile/src/worktree/host-worktree-refresh.ts +++ b/mobile/src/worktree/host-worktree-refresh.ts @@ -2,7 +2,7 @@ import { subscribeSharedClientEvents, type SharedClientEventsClient } from '../transport/connection/shared-client-events' -import type { HostSidebarFeedTriggers } from '../host-screen/host-sidebar-feed-connection' +import type { HostSidebarFeedTriggers } from '../host-screen/sidebar-feed/host-sidebar-feed-connection' // Keep lifecycle callers independent of RN types. type HostAppState = { diff --git a/mobile/tsconfig.test.mobile-declarations.json b/mobile/tsconfig.test.mobile-declarations.json index 623991763da..2db5ec60c48 100644 --- a/mobile/tsconfig.test.mobile-declarations.json +++ b/mobile/tsconfig.test.mobile-declarations.json @@ -31,7 +31,7 @@ "src/test-support/rpc-recording/derived-goldens.ts", "src/test-support/rpc-recording/golden-recording.ts", "src/test-support/rpc-recording/scenario-input.ts", - "src/host-screen/host-sidebar-feed-sections.ts", + "src/host-screen/sidebar-feed/host-sidebar-feed-sections.ts", "src/worktree/workspace-list-sections.ts", "src/worktree/worktree-catalog-snapshot-client.ts", "src/worktree/worktree-host-context-labels.ts", diff --git a/tests/e2e/helpers/phone-feed-list.ts b/tests/e2e/helpers/phone-feed-list.ts index db1a1b49fa4..0e3ab4537d2 100644 --- a/tests/e2e/helpers/phone-feed-list.ts +++ b/tests/e2e/helpers/phone-feed-list.ts @@ -2,15 +2,15 @@ import { expect } from '@stablyai/playwright-test' import type { Page } from '@stablyai/playwright-test' import { buildSidebarFeedRows } from '../../../src/shared/sidebar-list/sidebar-feed-rows' import { buildSidebarFeedCatalog } from '../../../src/shared/sidebar-list/sidebar-feed-catalog' -import { buildHostFeedView } from '../../../mobile/src/host-screen/host-sidebar-feed-inputs' -import { buildHostFeedSections } from '../../../mobile/src/host-screen/host-sidebar-feed-sections' +import { buildHostFeedView } from '../../../mobile/src/host-screen/sidebar-feed/host-sidebar-feed-inputs' +import { buildHostFeedSections } from '../../../mobile/src/host-screen/sidebar-feed/host-sidebar-feed-sections' import { NO_WORKTREE_HOST_CONTEXT } from '../../../mobile/src/worktree/worktree-host-context-labels' import { readHostSidebarFeed, type HostSidebarFeedRead -} from '../../../mobile/src/host-screen/use-host-sidebar-feed' -import { createHostSidebarFeedReader } from '../../../mobile/src/host-screen/host-sidebar-feed-connection' -import { readHostMethodReply } from '../../../mobile/src/host-screen/host-sidebar-feed-operations' +} from '../../../mobile/src/host-screen/sidebar-feed/use-host-sidebar-feed' +import { createHostSidebarFeedReader } from '../../../mobile/src/host-screen/sidebar-feed/host-sidebar-feed-connection' +import { readHostMethodReply } from '../../../mobile/src/host-screen/sidebar-feed/host-sidebar-feed-operations' import type { SharedClientEventsClient } from '../../../mobile/src/transport/connection/shared-client-events' import { hostPlatformRead, From e9d5b35d64e2d5faa87f8c1d1971ee84a74f8178 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 05:25:55 -0800 Subject: [PATCH 090/178] test: remove low-value mobile and CLI runtime tests (#42022) * test: remove low-value mobile suites and repeated vectors * test: trim low-value CLI runtime cases and obsolete allowance * test: satisfy changed-line template lint in click case --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 1 - mobile/src/mock-server-key-pair.test.ts | 24 -- .../onboarding/MobileOnboardingPage.test.ts | 122 ------ .../NotificationOnboardingPreview.test.ts | 116 ------ .../legacy-notification-opt-in-route.test.ts | 24 -- .../settings/settings-screen-state.test.tsx | 30 -- .../terminal-live-preedit-mirror.test.ts | 29 +- .../terminal-live-text-commit.test.ts | 22 - ...inal-text-field-submit-binding.web.test.ts | 27 +- .../terminal-webview-cell-box.test.ts | 16 - ...rminal-webview-interactive-output.test.tsx | 106 ----- ...rminal-webview-query-reply-routing.test.ts | 70 ---- .../terminal-webview-tap-routing.test.ts | 375 ------------------ .../terminal-webview-wheel-scroll.test.ts | 278 ------------- .../terminal/terminal-write-coalescer.test.ts | 64 +-- .../use-terminal-live-input-click.test.ts | 37 +- .../use-terminal-live-input-commit.test.ts | 49 +-- ...erminal-text-field-submit-binding.test.tsx | 55 +-- src/cli/runtime/envelope-schema.test.ts | 117 +----- .../profile-state-recovery-launch.test.ts | 71 +--- .../runtime/runtime-listener-probe.test.ts | 26 -- src/cli/runtime/runtime-source-fence.test.ts | 25 +- src/cli/runtime/serve-appimage-update.test.ts | 14 +- src/cli/runtime/serve-bundle-park.test.ts | 9 - .../runtime/serve-electron-preflight.test.ts | 6 - src/cli/runtime/serve-install-hold.test.ts | 62 --- .../runtime/serve-pid1-init-reaper.test.ts | 43 -- .../serve-signal-exit-diagnostic.test.ts | 80 +--- .../runtime/serve-supervised-service.test.ts | 39 +- .../serve-supervisor-relocation.test.ts | 27 -- src/cli/runtime/status.test.ts | 87 +--- 31 files changed, 74 insertions(+), 1977 deletions(-) delete mode 100644 mobile/src/onboarding/MobileOnboardingPage.test.ts delete mode 100644 mobile/src/onboarding/NotificationOnboardingPreview.test.ts delete mode 100644 mobile/src/onboarding/legacy-notification-opt-in-route.test.ts delete mode 100644 mobile/src/terminal/terminal-webview-interactive-output.test.tsx delete mode 100644 mobile/src/terminal/terminal-webview-query-reply-routing.test.ts delete mode 100644 mobile/src/terminal/terminal-webview-tap-routing.test.ts delete mode 100644 mobile/src/terminal/terminal-webview-wheel-scroll.test.ts delete mode 100644 src/cli/runtime/serve-install-hold.test.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index c4db40f9a5b..85c8cc9f0c6 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -121,7 +121,6 @@ 1 test-no-polling-loop:tests/e2e/terminal-tui-wheel-reports.spec.ts 1 test-no-polling-loop:tests/e2e/workspace-sleep-in-main.spec.ts 2 test-no-polling-loop:tests/e2e/worktree-card-downward-drag.spec.ts -1 test-no-real-time-wait:mobile/src/terminal/terminal-webview-tap-routing.test.ts 1 test-no-real-time-wait:src/main/agent-hooks/installation/scripts/posix-hook-json-stdin-reader.test.ts 2 test-no-real-time-wait:src/main/agent-hooks/server/testing/http/server-transport-interference.test.ts 1 test-no-real-time-wait:src/main/agent-hooks/server/testing/main-agent/server-turn-ended-at.test.ts diff --git a/mobile/src/mock-server-key-pair.test.ts b/mobile/src/mock-server-key-pair.test.ts index 1db9757e1e2..b4dbcccd3dd 100644 --- a/mobile/src/mock-server-key-pair.test.ts +++ b/mobile/src/mock-server-key-pair.test.ts @@ -3,7 +3,6 @@ import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { pathToFileURL } from 'node:url' -import nacl from 'tweetnacl' import { afterEach, describe, expect, it, vi } from 'vitest' import { loadOrCreateMockServerKeyPair } from '../scripts/mock-server-key-pair' @@ -186,20 +185,6 @@ describe('mock server key persistence', () => { } }) - it('re-keys canonical-length content with malformed base64', () => { - const keyFile = keyFilePath() - const encoded = Buffer.from(nacl.box.keyPair().secretKey).toString('base64') - const malformed = `${encoded.slice(0, 4)}!${encoded.slice(4)}` - expect(Buffer.from(malformed, 'base64')).toHaveLength(nacl.box.secretKeyLength) - writeFileSync(keyFile, malformed) - const logger = { warn: vi.fn() } - - const loaded = loadOrCreateMockServerKeyPair(keyFile, logger) - - expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('invalid base64')) - expect(readFileSync(keyFile, 'utf-8')).toBe(Buffer.from(loaded.secretKey).toString('base64')) - }) - it('makes concurrent creators converge on the persisted winner', async () => { const keyFile = keyFilePath() const lockFile = `${keyFile}.lock` @@ -224,13 +209,4 @@ describe('mock server key persistence', () => { await cleanupConcurrentCreators(creators, lockFile, parentOwnsLock) } }) - - it('does not overwrite an invalid key owned by another creator', () => { - const keyFile = keyFilePath() - writeFileSync(keyFile, 'invalid') - writeFileSync(`${keyFile}.lock`, '', { flag: 'wx', mode: 0o600 }) - - expect(() => loadOrCreateMockServerKeyPair(keyFile)).toThrow('remained busy') - expect(readFileSync(keyFile, 'utf-8')).toBe('invalid') - }) }) diff --git a/mobile/src/onboarding/MobileOnboardingPage.test.ts b/mobile/src/onboarding/MobileOnboardingPage.test.ts deleted file mode 100644 index 48612248143..00000000000 --- a/mobile/src/onboarding/MobileOnboardingPage.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { createElement, type ReactNode } from 'react' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { MobileOnboardingPage } from './MobileOnboardingPage' -import { findByHostType } from '../test-support/react-test-renderer-host-nodes' - -vi.mock('react-native', async () => { - const React = await import('react') - class AnimatedValue { - interpolate() { - return 0 - } - setValue() {} - } - return { - AccessibilityInfo: { - addEventListener: vi.fn(() => ({ remove: vi.fn() })), - isReduceMotionEnabled: vi.fn(() => Promise.resolve(true)) - }, - ActivityIndicator: 'ActivityIndicator', - Animated: { - Value: AnimatedValue, - View: 'AnimatedView', - delay: vi.fn(() => ({ start: vi.fn(), stop: vi.fn() })), - loop: vi.fn(() => ({ start: vi.fn(), stop: vi.fn() })), - parallel: vi.fn(() => ({ start: vi.fn(), stop: vi.fn() })), - sequence: vi.fn(() => ({ start: vi.fn(), stop: vi.fn() })), - timing: vi.fn(() => ({ start: vi.fn(), stop: vi.fn() })) - }, - Easing: { cubic: (t: number) => t, in: (e: unknown) => e, out: (e: unknown) => e }, - Image: 'Image', - Pressable: 'Pressable', - ScrollView: ({ children, ...props }: { children?: ReactNode }) => - React.createElement('ScrollView', props, children), - StyleSheet: { create: (styles: unknown) => styles }, - Text: 'Text', - View: 'View' - } -}) - -vi.mock('lucide-react-native', () => ({ - MessageSquare: 'MessageSquare' -})) - -vi.mock('../components/OrcaLogo', () => ({ OrcaLogo: 'OrcaLogo' })) - -describe('MobileOnboardingPage', () => { - let renderer: ReactTestRenderer | null = null - - afterEach(() => { - act(() => renderer?.unmount()) - renderer = null - vi.restoreAllMocks() - }) - - async function renderPage( - step: 'session-view' | 'notifications', - options: { active?: boolean; busyChoice?: 'chat' | 'enable' | null } = {} - ) { - const onSessionChoice = vi.fn() - const onNotificationChoice = vi.fn() - const consoleError = vi.spyOn(console, 'error').mockImplementation((...args) => { - if (typeof args[0] !== 'string' || !args[0].includes('react-test-renderer is deprecated')) { - throw new Error(String(args[0])) - } - }) - await act(async () => { - renderer = create( - createElement(MobileOnboardingPage, { - step, - width: 390, - active: options.active ?? true, - busyChoice: options.busyChoice ?? null, - error: null, - onSessionChoice, - onNotificationChoice - }) - ) - }) - consoleError.mockRestore() - return { onSessionChoice, onNotificationChoice } - } - - function button(label: string) { - return renderer!.root.find( - (node) => String(node.type) === 'Pressable' && node.props.accessibilityLabel === label - ) - } - - it('renders the session choices and sends exactly one selected view', async () => { - const callbacks = await renderPage('session-view') - - act(() => button('Open sessions in Chat UI').props.onPress()) - expect(callbacks.onSessionChoice).toHaveBeenCalledExactlyOnceWith('chat') - expect(callbacks.onNotificationChoice).not.toHaveBeenCalled() - }) - - it('renders the notification choices and sends the selected option', async () => { - const callbacks = await renderPage('notifications') - act(() => button('Skip notifications for now').props.onPress()) - - expect(callbacks.onNotificationChoice).toHaveBeenCalledWith('skip') - expect(callbacks.onSessionChoice).not.toHaveBeenCalled() - }) - - it('disables both notification choices while permission is pending', async () => { - await renderPage('notifications', { busyChoice: 'enable' }) - const enable = button('Enable agent notifications') - const secondary = button('Skip notifications for now') - - expect(enable.props.disabled).toBe(true) - expect(secondary.props.disabled).toBe(true) - }) - - it('hides an off-screen page from assistive technology', async () => { - await renderPage('notifications', { active: false }) - const scrollView = findByHostType(renderer!.root, 'ScrollView') - - expect(scrollView.props.accessibilityElementsHidden).toBe(true) - expect(scrollView.props.importantForAccessibility).toBe('no-hide-descendants') - }) -}) diff --git a/mobile/src/onboarding/NotificationOnboardingPreview.test.ts b/mobile/src/onboarding/NotificationOnboardingPreview.test.ts deleted file mode 100644 index fa400485a9f..00000000000 --- a/mobile/src/onboarding/NotificationOnboardingPreview.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { createElement, type ReactNode } from 'react' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { NotificationOnboardingPreview } from './NotificationOnboardingPreview' -import { findAllByHostType } from '../test-support/react-test-renderer-host-nodes' - -const mocks = vi.hoisted(() => { - const anim = () => ({ start: vi.fn(), stop: vi.fn() }) - return { - reducedMotion: true, - reducedMotionResult: null as Promise | null, - timing: vi.fn(anim), - loop: vi.fn(anim) - } -}) - -vi.mock('react-native', async () => { - const React = await import('react') - class AnimatedValue { - interpolate() { - return 0 - } - setValue() {} - } - return { - AccessibilityInfo: { - addEventListener: vi.fn(() => ({ remove: vi.fn() })), - isReduceMotionEnabled: vi.fn( - () => mocks.reducedMotionResult ?? Promise.resolve(mocks.reducedMotion) - ) - }, - Animated: { - Value: AnimatedValue, - View: ({ children, ...props }: { children?: ReactNode }) => - React.createElement('AnimatedView', props, children), - delay: vi.fn(() => ({ start: vi.fn(), stop: vi.fn() })), - loop: mocks.loop, - parallel: vi.fn(() => ({ start: vi.fn(), stop: vi.fn() })), - sequence: vi.fn(() => ({ start: vi.fn(), stop: vi.fn() })), - timing: mocks.timing - }, - Easing: { cubic: (t: number) => t, in: (e: unknown) => e, out: (e: unknown) => e }, - StyleSheet: { create: (styles: unknown) => styles }, - Text: 'Text', - View: 'View' - } -}) - -vi.mock('../components/OrcaLogo', () => ({ OrcaLogo: 'OrcaLogo' })) - -describe('NotificationOnboardingPreview', () => { - let renderer: ReactTestRenderer | null = null - - afterEach(() => { - act(() => renderer?.unmount()) - renderer = null - mocks.reducedMotion = true - mocks.reducedMotionResult = null - mocks.timing.mockClear() - mocks.loop.mockClear() - vi.restoreAllMocks() - }) - - async function renderPreview(active = true) { - const consoleError = vi.spyOn(console, 'error').mockImplementation((...args) => { - if (typeof args[0] !== 'string' || !args[0].includes('react-test-renderer is deprecated')) { - throw new Error(String(args[0])) - } - }) - await act(async () => { - renderer = create(createElement(NotificationOnboardingPreview, { active })) - }) - consoleError.mockRestore() - } - - it('shows sample agent-done and needs-input banners as a decorative mock', async () => { - await renderPreview() - const preview = renderer!.root.findByProps({ testID: 'notification-onboarding-preview' }) - const copy = findAllByHostType(renderer!.root, 'Text') - .flatMap((node) => node.props.children) - .join(' ') - - expect(preview.props.accessibilityElementsHidden).toBe(true) - expect(preview.props.importantForAccessibility).toBe('no-hide-descendants') - expect(copy).toContain('Codex finished') - expect(copy).toContain('Claude needs input') - }) - - it('does not animate the banners when the page is off-screen', async () => { - mocks.reducedMotion = false - await renderPreview(false) - expect(mocks.loop).not.toHaveBeenCalled() - }) - - it('loops the arriving banners while the page is active', async () => { - mocks.reducedMotion = false - await renderPreview(true) - expect(mocks.loop).toHaveBeenCalledOnce() - expect(mocks.loop.mock.results[0]?.value.start).toHaveBeenCalledOnce() - }) - - it('does not start the loop until reduced-motion is known', async () => { - let resolvePreference: (enabled: boolean) => void = () => {} - mocks.reducedMotionResult = new Promise((resolve) => { - resolvePreference = resolve - }) - - await renderPreview(true) - expect(mocks.loop).not.toHaveBeenCalled() - - await act(async () => { - resolvePreference(false) - }) - expect(mocks.loop).toHaveBeenCalledOnce() - }) -}) diff --git a/mobile/src/onboarding/legacy-notification-opt-in-route.test.ts b/mobile/src/onboarding/legacy-notification-opt-in-route.test.ts deleted file mode 100644 index edbb44f8e72..00000000000 --- a/mobile/src/onboarding/legacy-notification-opt-in-route.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { legacyNotificationOptInDestination } from './legacy-notification-opt-in-destination' - -describe('legacy notification opt-in destination', () => { - it('selects only notifications and preserves a scalar hostId', () => { - expect(legacyNotificationOptInDestination({ hostId: 'paired-host' })).toEqual({ - pathname: '/mobile-onboarding', - params: { steps: 'notifications', hostId: 'paired-host' } - }) - }) - - it('ignores unrelated legacy parameters', () => { - const legacyParams = { - hostId: 'paired-host', - steps: 'session-view', - returnTo: '/settings' - } - - expect(legacyNotificationOptInDestination(legacyParams)).toEqual({ - pathname: '/mobile-onboarding', - params: { steps: 'notifications', hostId: 'paired-host' } - }) - }) -}) diff --git a/mobile/src/settings/settings-screen-state.test.tsx b/mobile/src/settings/settings-screen-state.test.tsx index 0bcef5862e3..6bd21b6b58c 100644 --- a/mobile/src/settings/settings-screen-state.test.tsx +++ b/mobile/src/settings/settings-screen-state.test.tsx @@ -55,36 +55,6 @@ afterEach(() => { act(() => renderer?.unmount()) }) describe('shared settings screen state', () => { - it('shows a desktop on Auto as Auto and switches it to plain Toggle', async () => { - const operations: VoiceSettingsOperations = { - load: vi.fn().mockResolvedValue({ - enabled: true, - dictationMode: 'toggle', - dictationHoldToRecord: true, - selectedModelId: '', - models: [] - }), - configure: vi.fn(() => new Promise(() => {})), - download: vi.fn(), - delete: vi.fn() - } - await act(async () => { - renderer = create( - createElement(VoiceSettingsScreen, { operations, focused: true, onBack: vi.fn() }) - ) - }) - const modeProps = () => renderer.root.findByProps({ testIDPrefix: 'voice-mode-' }).props - expect(modeProps().selected).toBe('auto') - - await act(async () => { - modeProps().onSelect('toggle') - }) - expect(operations.configure).toHaveBeenCalledWith({ - dictationMode: 'toggle', - dictationHoldToRecord: false - }) - expect(modeProps().selected).toBe('toggle') - }) it('flips the Voice switch before the desktop replies and surfaces a rejected save', async () => { const loaded = { enabled: true, diff --git a/mobile/src/terminal/terminal-live-preedit-mirror.test.ts b/mobile/src/terminal/terminal-live-preedit-mirror.test.ts index d6a966b2348..04cba45fb51 100644 --- a/mobile/src/terminal/terminal-live-preedit-mirror.test.ts +++ b/mobile/src/terminal/terminal-live-preedit-mirror.test.ts @@ -12,10 +12,7 @@ type MirrorRun = { type MirrorFrame = { readonly text: string; readonly composing?: boolean } -function runMirrorSequence( - fieldStates: readonly (string | MirrorFrame)[], - options: { readonly commitAtEnd: boolean } = { commitAtEnd: false } -): MirrorRun { +function runMirrorSequence(fieldStates: readonly (string | MirrorFrame)[]): MirrorRun { const payloads: string[] = [] let sentText = '' let heldText = '' @@ -29,16 +26,6 @@ function runMirrorSequence( sentText = step.nextSentText heldText = step.heldText } - if (options.commitAtEnd) { - const lastField = sentText + heldText - const step = computeTerminalLiveMirrorStep(sentText, lastField, { commitHeld: true }) - const payload = buildTerminalLiveMirrorPayload(step) - if (payload.length > 0) { - payloads.push(payload) - } - sentText = step.nextSentText - heldText = step.heldText - } return { payloads, sentText, heldText } } @@ -117,19 +104,7 @@ const twoSetFields = [ describe('terminal live mirror with iOS keyboard edits', () => { it.each([ { keyboard: 'Cheonjiin', fields: cheonjiinFields }, - { keyboard: 'two-set', fields: twoSetFields }, - { - keyboard: 'autocorrect around a held Korean tail', - fields: ['teh 하', 'teh 하ᄉᆞ', 'the 하ᄉᆞ', 'the 하세 '] - }, - { - keyboard: 'replacement of an already-sent Korean prefix', - fields: ['안녕하', '안녕하ᄉᆞ', '반가ᄉᆞ', '반가워 '] - }, - { - keyboard: 'delete-all with a held Korean tail', - fields: ['안녕하', '안녕하ᄉᆞ', '', '새 입력 '] - } + { keyboard: 'two-set', fields: twoSetFields } ])('replays $keyboard edits for both receiver deletion units', ({ fields }) => { const run = runMirrorSequence(fields.map((text) => ({ text, composing: false }))) const segmenter = new Intl.Segmenter('ko', { granularity: 'grapheme' }) diff --git a/mobile/src/terminal/terminal-live-text-commit.test.ts b/mobile/src/terminal/terminal-live-text-commit.test.ts index c363419cfa5..610b2679b6f 100644 --- a/mobile/src/terminal/terminal-live-text-commit.test.ts +++ b/mobile/src/terminal/terminal-live-text-commit.test.ts @@ -17,26 +17,4 @@ describe('terminal live accessory local edit text', () => { ) } ) - - it.each([ - ['', ''], - ['a', ''], - ['a🙂', 'a'], - ['🙂a', '🙂'], - ['🙂🙂', '🙂'], - ['a\ud800', 'a'], - ['a\udc00', 'a'], - ['\ud800\ud800', '\ud800'], - ['\udc00\ud800', '\udc00'], - ['e\u0301', 'e'], - ['👩‍💻', '👩‍'], - ['\r\n', '\r'] - ])('preserves code-point deletion for %j', (fieldText, expected) => { - expect(getTerminalLiveAccessoryLocalEditText({ localEdit: 'backspace', fieldText })).toBe( - expected - ) - expect(getTerminalLiveAccessoryLocalEditText({ localEdit: 'delete', fieldText })).toBe( - fieldText - ) - }) }) diff --git a/mobile/src/terminal/terminal-text-field-submit-binding.web.test.ts b/mobile/src/terminal/terminal-text-field-submit-binding.web.test.ts index a3a52b32a35..4be80b442b8 100644 --- a/mobile/src/terminal/terminal-text-field-submit-binding.web.test.ts +++ b/mobile/src/terminal/terminal-text-field-submit-binding.web.test.ts @@ -80,13 +80,8 @@ describe.each(['android', 'ios'] as const)('live terminal keyboard chords on %s' it.each([ ['c', { ctrlKey: true }, '\x03'], - ['Escape', {}, '\x1b'], - ['Tab', {}, '\t'], - ['Tab', { shiftKey: true }, '\x1b[Z'], - ['ArrowUp', {}, '\x1b[A'], ['ArrowLeft', { ctrlKey: true }, '\x1b[1;5D'], - ['Enter', { shiftKey: true }, '\x1b[13;2u'], - ['ArrowLeft', { altKey: true }, '\x1b[1;3D'] + ['Enter', { shiftKey: true }, '\x1b[13;2u'] ])('claims %s once before RNWeb can resend it', (name, options, bytes) => { const { field, onChord, key } = setup() const parentKey = vi.fn() @@ -96,28 +91,18 @@ describe.each(['android', 'ios'] as const)('live terminal keyboard chords on %s' expect(parentKey).not.toHaveBeenCalled() }) - it.each(['b', '@', '[', ']', '{', ' '])( - 'keeps Option-generated %j native on iOS and Alt chords on Android', - (name) => { - const { onChord, key } = setup() - expect(key(name, { altKey: true }).defaultPrevented).toBe(os === 'android') - expect(onChord.mock.calls).toEqual(os === 'android' ? [[{ bytes: `\x1b${name}` }]] : []) - } - ) + it.each(['b'])('keeps Option-generated %j native on iOS and Alt chords on Android', (name) => { + const { onChord, key } = setup() + expect(key(name, { altKey: true }).defaultPrevented).toBe(os === 'android') + expect(onChord.mock.calls).toEqual(os === 'android' ? [[{ bytes: `\x1b${name}` }]] : []) + }) it.each([ ['a', {}], ['Enter', {}], ['Enter', { ctrlKey: true, shiftKey: true }], - ['Enter', { altKey: true, shiftKey: true }], - ['Enter', { shiftKey: true, isComposing: true }], - ['Enter', { shiftKey: true, keyCode: 229 }], ['Backspace', {}], - ['Delete', {}], ['ArrowLeft', {}], - ['ArrowRight', {}], - ['Home', {}], - ['End', {}], ['c', { ctrlKey: true, isComposing: true }], ['c', { ctrlKey: true, keyCode: 229 }], ['c', { ctrlKey: true, altKey: true }], diff --git a/mobile/src/terminal/terminal-webview-cell-box.test.ts b/mobile/src/terminal/terminal-webview-cell-box.test.ts index 5dfcb5dc351..fdbe3b06d85 100644 --- a/mobile/src/terminal/terminal-webview-cell-box.test.ts +++ b/mobile/src/terminal/terminal-webview-cell-box.test.ts @@ -109,22 +109,6 @@ describe('the cell box xterm laid out', () => { expect(handle().fitDimensions(FRAME)).toEqual({ cols: 55, rows: 47 }) }) - it('fits in the app from the reported box, and sends the frame with every grid', () => { - const { handle, notify } = mount() - notify({ type: 'web-ready', cellBox: cellAt(scale) }) - expect(handle().fitDimensions({ width: 427.5, height: 0 })).toBeNull() - expect(handle().fitDimensions({ width: 427.5, height: 710 })).toEqual({ cols: 55, rows: 47 }) - const frame = { width: 427.5, height: 710 } - handle().init({ cols: 55, rows: 47, initialData: '', frame }) - handle().resize(55, 47, frame) - handle().reflow(55, 47, frame) - const grids = nativeWebViewMethods.postMessage.mock.calls - .map(([message]) => JSON.parse(message)) - .filter((message) => ['init', 'resize', 'reflow'].includes(message.type)) - expect(grids.map((message) => message.frame)).toEqual([frame, frame, frame]) - expect(postedTypes()).not.toContain('measure') - }) - it('sends nothing before a ready: a load start drops what was queued', () => { const { handle, notify, webView } = mount() handle().init({ cols: 55, rows: 47, initialData: 'snapshot', frame: null }) diff --git a/mobile/src/terminal/terminal-webview-interactive-output.test.tsx b/mobile/src/terminal/terminal-webview-interactive-output.test.tsx deleted file mode 100644 index a84f902af16..00000000000 --- a/mobile/src/terminal/terminal-webview-interactive-output.test.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { - useTerminalWebViewController, - type TerminalWebViewTransport -} from './use-terminal-webview-controller' - -vi.mock('react-native', () => ({ AppState: { currentState: 'active' } })) - -vi.mock('./terminal-webview-engine-error-state', () => ({ - useTerminalWebViewEngineErrorState: () => ({ - clearEngineError: vi.fn(), - engineError: null, - reportEngineError: vi.fn(), - reportNativeEngineError: vi.fn() - }) -})) - -let root: ReactTestRenderer | undefined - -function mountController(interactionActive = true) { - const post = vi.fn() - const onTerminalInput = vi.fn() - const exposed: { current: ReturnType | null } = { - current: null - } - const transport = { post, pingsOnForegroundRecovery: () => false } - function Probe() { - exposed.current = useTerminalWebViewController( - { onTerminalInput, interactionActive }, - transport - ) - return null - } - act(() => { - root = create() - }) - const controller = exposed.current - if (!controller) { - throw new Error('Controller did not mount') - } - act(() => controller.receive({ type: 'web-ready' })) - const writes = () => - post.mock.calls - .map(([command]) => command) - .filter((command) => command.type === 'write') - .map((command) => command.data) - return { controller, onTerminalInput, writes } -} - -describe('terminal scroll response batching', () => { - afterEach(() => { - act(() => root?.unmount()) - root = undefined - vi.useRealTimers() - }) - - it.each(['\x1b[<64;2;2M', '\x1b[A'])('shortens pending output for gesture %j', (bytes) => { - vi.useFakeTimers() - const { controller, onTerminalInput, writes } = mountController() - controller.handle.write('first') - vi.advanceTimersByTime(5) - controller.handle.write('response') - vi.advanceTimersByTime(3) - controller.receive({ type: 'terminal-input', bytes }) - vi.advanceTimersByTime(8) - expect(writes()).toEqual(['first', 'response']) - expect(onTerminalInput).toHaveBeenCalledWith(bytes) - }) - - it.each(['', 'ordinary text', 123])('keeps ordinary batching for non-gesture %j', (bytes) => { - vi.useFakeTimers() - const { controller, writes } = mountController() - controller.handle.write('first') - controller.handle.write('pending') - controller.receive({ type: 'terminal-input', bytes }) - vi.advanceTimersByTime(16) - expect(writes()).toEqual(['first']) - vi.advanceTimersByTime(32) - expect(writes()).toEqual(['first', 'pending']) - }) - - it('rejects inactive gesture notifications without changing output batching', () => { - vi.useFakeTimers() - const { controller, onTerminalInput, writes } = mountController(false) - controller.handle.write('first') - controller.handle.write('pending') - controller.receive({ type: 'terminal-input', bytes: '\x1b[A' }) - vi.advanceTimersByTime(16) - expect(onTerminalInput).not.toHaveBeenCalled() - expect(writes()).toEqual(['first']) - vi.advanceTimersByTime(32) - expect(writes()).toEqual(['first', 'pending']) - }) - - it('drops pending gesture output before a new document snapshot', () => { - vi.useFakeTimers() - const { controller, writes } = mountController() - controller.handle.write('first') - controller.handle.write('stale') - controller.receive({ type: 'terminal-input', bytes: '\x1b[A' }) - controller.handle.init({ cols: 80, rows: 24, initialData: 'snapshot', frame: null }) - vi.advanceTimersByTime(48) - expect(writes()).toEqual(['first']) - }) -}) diff --git a/mobile/src/terminal/terminal-webview-query-reply-routing.test.ts b/mobile/src/terminal/terminal-webview-query-reply-routing.test.ts deleted file mode 100644 index b5296d75b21..00000000000 --- a/mobile/src/terminal/terminal-webview-query-reply-routing.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { createElement } from 'react' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { TerminalWebView } from './TerminalWebView' -import { findByHostType } from '../test-support/react-test-renderer-host-nodes' - -vi.mock('react-native', () => ({ - Platform: { OS: 'ios' }, - StyleSheet: { - absoluteFill: { - bottom: 0, - left: 0, - position: 'absolute', - right: 0, - top: 0 - }, - create: (styles: unknown) => styles - }, - Text: 'Text', - View: 'View' -})) - -vi.mock('react-native-webview', async () => { - const React = await import('react') - const WebView = React.forwardRef((props: Record, _ref) => - React.createElement('WebView', props) - ) - return { WebView, default: WebView } -}) - -vi.mock('lucide-react-native', () => ({ - RefreshCw: 'RefreshCw' -})) - -let renderer: ReactTestRenderer | null = null - -function postWebViewMessage(payload: Record): void { - if (!renderer) { - throw new Error('TerminalWebView did not render') - } - const webView = findByHostType(renderer.root, 'WebView') - act(() => { - webView.props.onMessage({ nativeEvent: { data: JSON.stringify(payload) } }) - }) -} - -describe('TerminalWebView query reply routing', () => { - afterEach(() => { - if (renderer) { - act(() => renderer?.unmount()) - renderer = null - } - vi.restoreAllMocks() - }) - - it('routes only complete terminal query replies to native', () => { - const onTerminalQueryReply = vi.fn() - vi.spyOn(console, 'error').mockImplementation(() => {}) - act(() => { - renderer = create(createElement(TerminalWebView, { onTerminalQueryReply })) - }) - - postWebViewMessage({ type: 'terminal-data', bytes: '\x1b[3;4R' }) - postWebViewMessage({ type: 'terminal-data', bytes: 'a' }) - postWebViewMessage({ type: 'terminal-data', bytes: '\x1b[A' }) - - expect(onTerminalQueryReply).toHaveBeenCalledTimes(1) - expect(onTerminalQueryReply).toHaveBeenCalledWith('\x1b[3;4R') - }) -}) diff --git a/mobile/src/terminal/terminal-webview-tap-routing.test.ts b/mobile/src/terminal/terminal-webview-tap-routing.test.ts deleted file mode 100644 index d8b30559234..00000000000 --- a/mobile/src/terminal/terminal-webview-tap-routing.test.ts +++ /dev/null @@ -1,375 +0,0 @@ -// @vitest-environment happy-dom -// URL taps with slight jitter once hit the long-press slop gate and never reached the native browser bridge. -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { TERMINAL_DOCUMENT_SCRIPT } from './terminal-webview-document-script.generated' -import { TERMINAL_DOCUMENT_MARKUP } from './terminal-webview-html' - -function bodyMarkup(): string { - return TERMINAL_DOCUMENT_MARKUP -} - -function makeTerminal( - lineRef: { current: string }, - mouseTrackingMode = 'none', - scrollbackRows = 0 -) { - return { - cols: 80, - rows: 24, - options: { fontSize: 13 }, - modes: { mouseTrackingMode }, - element: { scrollWidth: 800, scrollHeight: 360 }, - _core: { _renderService: { dimensions: { css: { cell: { width: 8, height: 15 } } } } }, - buffer: { - active: { - viewportY: 0, - baseY: scrollbackRows, - length: 1, - cursorY: 0, - type: 'normal' as const, - getLine(row: number) { - // Rows after the first are xterm soft-wrap continuations. - const text = lineRef.current.split('\n')[row] - if (text === undefined) { - return null - } - return { - length: 80, - isWrapped: row > 0, - // Honor xterm’s translateToString slice arguments so wide-cell offset conversion is realistic. - translateToString: (_trim?: boolean, start?: number, end?: number) => - text.slice(start ?? 0, end ?? text.length), - getCell: () => ({ extended: undefined }) - } - } - } - }, - write(_d: string, cb?: () => void) { - cb?.() - }, - open() {}, - resize() {}, - clear() {}, - reset() {}, - refresh() {}, - selectAll() {}, - clearSelection() {}, - select() {}, - scrollLines() {}, - scrollToBottom() {}, - getSelection: () => '', - onLineFeed: () => ({ dispose() {} }), - onScroll: () => ({ dispose() {} }), - onWriteParsed: () => ({ dispose() {} }), - dispose() {} - } -} - -type Posted = Array> - -type OscLinkRange = { row: number; startCol: number; endCol: number; uri: string } - -function boot( - line: string, - oscLinks?: OscLinkRange[], - mouseTrackingMode = 'none', - scrollbackRows = 0 -): { posted: Posted; setLine: (line: string) => void } { - const posted: Posted = [] - const lineRef = { current: line } - const w = window as unknown as { Terminal: unknown; ReactNativeWebView: unknown } - w.Terminal = function () { - return makeTerminal(lineRef, mouseTrackingMode, scrollbackRows) - } - w.ReactNativeWebView = { - postMessage(s: string) { - posted.push(JSON.parse(s)) - } - } - document.body.innerHTML = bodyMarkup() - // The bundle the WebView loads, run as the WebView runs it. - new Function(TERMINAL_DOCUMENT_SCRIPT)() - window.dispatchEvent( - new MessageEvent('message', { - data: JSON.stringify({ type: 'init', cols: 80, rows: 24, initialData: '', oscLinks }) - }) - ) - return { - posted, - setLine: (nextLine: string) => { - lineRef.current = nextLine - } - } -} - -function fireTouch(type: string, touches: Array<{ x: number; y: number }>): void { - const surface = document.getElementById('terminal-surface') as HTMLElement - const ev = new Event(type, { bubbles: true, cancelable: true }) - Object.defineProperty(ev, 'touches', { - value: touches.map((p, i) => ({ identifier: i, clientX: p.x, clientY: p.y, target: surface })) - }) - surface.dispatchEvent(ev) -} - -const settle = (): Promise => new Promise((r) => setTimeout(r, 50)) - -describe('terminal WebView tap routing', () => { - const URL_LINE = 'visit https://example.com/foo now' - const tapX = 12 * 8 * 0.3125 - const tapY = 2 - const screenXForCol = (col: number): number => col * 8 * 0.3125 - - beforeEach(() => { - vi.spyOn(window, 'addEventListener') - vi.spyOn(document, 'addEventListener') - Object.defineProperty(window, 'innerWidth', { value: 200, configurable: true }) - Object.defineProperty(window, 'innerHeight', { value: 400, configurable: true }) - }) - - afterEach(() => { - for (const [type, listener, options] of vi.mocked(window.addEventListener).mock.calls) { - window.removeEventListener(type, listener, options) - } - for (const [type, listener, options] of vi.mocked(document.addEventListener).mock.calls) { - document.removeEventListener(type, listener, options) - } - vi.restoreAllMocks() - }) - - it('still posts open-url when the tap jitters a few pixels', async () => { - // Small finger jitter must stay a tap and open the touched URL. - const { posted } = boot(URL_LINE) - await settle() - fireTouch('touchstart', [{ x: tapX, y: tapY }]) - fireTouch('touchmove', [{ x: tapX + 11, y: tapY + 4 }]) - fireTouch('touchend', []) - expect(posted.find((m) => m.type === 'open-url')?.url).toBe('https://example.com/foo') - }) - - it('focuses native input after reporting a touch tap to a mouse-tracking TUI', async () => { - const { posted } = boot('interactive prompt', undefined, 'drag') - await settle() - window.dispatchEvent( - new MessageEvent('message', { - data: JSON.stringify({ type: 'write', data: '\u001b[?1006l' }) - }) - ) - - fireTouch('touchstart', [{ x: 20, y: tapY }]) - fireTouch('touchend', []) - - expect( - posted - .filter((message) => message.type === 'terminal-input' || message.type === 'terminal-tap') - .map((message) => message.type) - ).toEqual(['terminal-input', 'terminal-tap']) - }) - - it('focuses native input without mouse bytes while the tracking encoding is unproven', async () => { - const { posted } = boot('interactive prompt', undefined, 'drag') - await settle() - - fireTouch('touchstart', [{ x: 20, y: tapY }]) - fireTouch('touchend', []) - - expect(posted.find((message) => message.type === 'terminal-input')).toBeUndefined() - expect(posted.filter((message) => message.type === 'terminal-tap')).toHaveLength(1) - }) - - it('opens first-load OSC links from snapshot metadata on the exact cell range', async () => { - const oscLinks = [{ row: 0, startCol: 6, endCol: 11, uri: 'https://example.com/issue/1234' }] - const { posted } = boot('issue #1234 done', oscLinks) - await settle() - - fireTouch('touchstart', [{ x: screenXForCol(7), y: tapY }]) - fireTouch('touchend', []) - - expect(posted.find((m) => m.type === 'open-url')?.url).toBe('https://example.com/issue/1234') - }) - - it('opens first-load file OSC links through terminal file taps', async () => { - const oscLinks = [{ row: 0, startCol: 5, endCol: 15, uri: 'file:///tmp/result.json#L12C3' }] - const { posted } = boot('open artifact here', oscLinks) - await settle() - - fireTouch('touchstart', [{ x: screenXForCol(7), y: tapY }]) - fireTouch('touchend', []) - - expect(posted.find((m) => m.type === 'open-url')).toBeUndefined() - expect(posted.find((m) => m.type === 'terminal-file-tap')).toEqual({ - type: 'terminal-file-tap', - candidates: [{ pathText: '/tmp/result.json', line: 12, column: 3 }] - }) - }) - - it('keeps the UNC host of an OSC file link for a Windows share workspace', async () => { - const oscLinks = [ - { row: 0, startCol: 5, endCol: 15, uri: 'file://server/share/repo/app.ts#L12' } - ] - const { posted } = boot('open artifact here', oscLinks) - await settle() - - fireTouch('touchstart', [{ x: screenXForCol(7), y: tapY }]) - fireTouch('touchend', []) - - expect(posted.find((m) => m.type === 'terminal-file-tap')).toEqual({ - type: 'terminal-file-tap', - candidates: [{ pathText: '//server/share/repo/app.ts', line: 12, column: null }] - }) - }) - - it('keeps a tap on prose beside a path an ordinary tap', async () => { - const line = 'me@host:~/proj$ git status' - const { posted } = boot(line) - await settle() - - fireTouch('touchstart', [{ x: screenXForCol(line.indexOf('git') + 1), y: tapY }]) - fireTouch('touchend', []) - - expect(posted.find((m) => m.type === 'terminal-file-tap')).toBeUndefined() - expect(posted.find((m) => m.type === 'terminal-tap')).toBeDefined() - }) - - it('opens a markdown link whose destination has spaces', async () => { - const line = 'see [PDF]()' - const { posted } = boot(line) - await settle() - - fireTouch('touchstart', [{ x: screenXForCol(line.indexOf('with') + 1), y: tapY }]) - fireTouch('touchend', []) - - expect(posted.find((m) => m.type === 'terminal-file-tap')).toEqual({ - type: 'terminal-file-tap', - candidates: [{ pathText: '/tmp/folder with spaces/report.pdf', line: null, column: null }] - }) - }) - - it('opens plain file URLs through terminal file taps', async () => { - const line = 'open file:///tmp/result.json#L12C3 now' - const { posted } = boot(line) - await settle() - - fireTouch('touchstart', [{ x: screenXForCol(line.indexOf('result')), y: tapY }]) - fireTouch('touchend', []) - - expect(posted.find((m) => m.type === 'terminal-file-tap')).toEqual({ - type: 'terminal-file-tap', - candidates: [{ pathText: '/tmp/result.json', line: 12, column: 3 }] - }) - }) - - it('opens the whole URL when the tap lands on its soft-wrapped second row', async () => { - const url = `https://example.com/${'a'.repeat(70)}/end` - const text = `see ${url} now` - const { posted } = boot(`${text.slice(0, 80)}\n${text.slice(80)}`) - await settle() - - fireTouch('touchstart', [{ x: screenXForCol(2), y: tapY + 15 * 0.3125 }]) - fireTouch('touchend', []) - - expect(posted.find((m) => m.type === 'open-url')?.url).toBe(url) - }) - - it('does not open snapshot OSC links from adjacent terminal cells', async () => { - const oscLinks = [{ row: 0, startCol: 6, endCol: 11, uri: 'https://example.com/issue/1234' }] - const { posted } = boot('issue #1234 done', oscLinks) - await settle() - - fireTouch('touchstart', [{ x: screenXForCol(12), y: tapY }]) - fireTouch('touchend', []) - - expect(posted.find((m) => m.type === 'open-url')).toBeUndefined() - }) - - it('does not open stale snapshot OSC links after the row text changes', async () => { - const oscLinks = [{ row: 0, startCol: 6, endCol: 11, uri: 'https://example.com/issue/1234' }] - const { posted, setLine } = boot('issue #1234 done', oscLinks) - await settle() - setLine('issue plain done') - - fireTouch('touchstart', [{ x: screenXForCol(7), y: tapY }]) - fireTouch('touchend', []) - - expect(posted.find((m) => m.type === 'open-url')).toBeUndefined() - }) - - it('does not post open-url for a scroll gesture past the tap slop', async () => { - const { posted } = boot(URL_LINE) - await settle() - fireTouch('touchstart', [{ x: tapX, y: tapY }]) - fireTouch('touchmove', [{ x: tapX, y: tapY + 120 }]) - fireTouch('touchend', []) - expect(posted.find((m) => m.type === 'open-url')).toBeUndefined() - }) - - // Scaled rows are smaller than tap slop; a short drag can still count as a real row scroll. - it('does not tap after a short drag that scrolled scrollback', async () => { - const { posted } = boot('plain output', undefined, 'none', 50) - await settle() - fireTouch('touchstart', [{ x: 20, y: 100 }]) - fireTouch('touchmove', [{ x: 20, y: 88 }]) - fireTouch('touchend', []) - expect(posted.find((m) => m.type === 'terminal-tap')).toBeUndefined() - }) - - it('does not tap or click after a short drag that scrolled a mouse-tracking TUI', async () => { - const { posted } = boot('interactive prompt', undefined, 'drag') - await settle() - window.dispatchEvent( - new MessageEvent('message', { - data: JSON.stringify({ type: 'write', data: '\u001b[?1006l' }) - }) - ) - - fireTouch('touchstart', [{ x: 20, y: 100 }]) - fireTouch('touchmove', [{ x: 20, y: 88 }]) - fireTouch('touchend', []) - - expect(posted.find((m) => m.type === 'terminal-tap')).toBeUndefined() - const input = posted - .filter((m) => m.type === 'terminal-input') - .map((m) => String(m.bytes)) - .join('') - const reports = input.split('\u001b[M').slice(1) - expect(reports.length).toBeGreaterThan(0) - expect(reports.every((report) => report[0] === '`' || report[0] === 'a')).toBe(true) - }) - - it('still taps when the touch moves less than a row', async () => { - const { posted } = boot('plain output', undefined, 'none', 50) - await settle() - fireTouch('touchstart', [{ x: 20, y: 100 }]) - fireTouch('touchmove', [{ x: 20, y: 97 }]) - fireTouch('touchend', []) - expect(posted.filter((m) => m.type === 'terminal-tap')).toHaveLength(1) - }) - - it('still taps when the touch moves a row and back before anything scrolls', async () => { - const { posted } = boot('plain output', undefined, 'none', 50) - await settle() - fireTouch('touchstart', [{ x: 20, y: 100 }]) - fireTouch('touchmove', [{ x: 20, y: 94 }]) - fireTouch('touchmove', [{ x: 20, y: 100 }]) - fireTouch('touchend', []) - expect(posted.filter((m) => m.type === 'terminal-tap')).toHaveLength(1) - }) - - it('does not tap when the touch stops a coast', async () => { - let now = 1_000 - vi.spyOn(Date, 'now').mockImplementation(() => now) - const { posted } = boot('plain output', undefined, 'none', 50) - await settle() - fireTouch('touchstart', [{ x: 20, y: 300 }]) - now += 16 - fireTouch('touchmove', [{ x: 20, y: 240 }]) - now += 16 - fireTouch('touchmove', [{ x: 20, y: 180 }]) - fireTouch('touchend', []) - const beforeStoppingTouch = posted.length - - fireTouch('touchstart', [{ x: 20, y: 180 }]) - fireTouch('touchend', []) - - expect(posted.slice(beforeStoppingTouch).find((m) => m.type === 'terminal-tap')).toBeUndefined() - }) -}) diff --git a/mobile/src/terminal/terminal-webview-wheel-scroll.test.ts b/mobile/src/terminal/terminal-webview-wheel-scroll.test.ts deleted file mode 100644 index 33f0c635258..00000000000 --- a/mobile/src/terminal/terminal-webview-wheel-scroll.test.ts +++ /dev/null @@ -1,278 +0,0 @@ -// @vitest-environment happy-dom -import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from 'vitest' -import { TERMINAL_DOCUMENT_SCRIPT } from './terminal-webview-document-script.generated' -import { TERMINAL_DOCUMENT_MARKUP } from './terminal-webview-html' - -function bodyMarkup(): string { - return TERMINAL_DOCUMENT_MARKUP -} - -type BufferState = { - baseY: number - type: 'alternate' | 'normal' - viewportY: number -} - -type TerminalStub = ReturnType -type RegisteredWindowListener = { - listener: EventListenerOrEventListenerObject - options?: boolean | AddEventListenerOptions - type: string -} - -const CELL_HEIGHT = 15 -const ESC = '\u001b' -const ESC_ARROW_DOWN = `${ESC}[B` -const ESC_APP_ARROW_UP = `${ESC}OA` - -function makeTerminal(buffer: BufferState, scrollLines: (lines: number) => void) { - const terminal = { - cols: 40, - rows: 24, - options: { fontSize: 13 }, - modes: { mouseTrackingMode: 'none' as string }, - element: null as HTMLElement | null, - _core: { - _renderService: { dimensions: { css: { cell: { width: 8, height: CELL_HEIGHT } } } } - }, - buffer: { - active: { - get baseY() { - return buffer.baseY - }, - get type() { - return buffer.type - }, - get viewportY() { - return buffer.viewportY - }, - cursorY: 0, - length: 1, - getLine: () => null - } - }, - write(_data: string, callback?: () => void) { - callback?.() - }, - open(surface: HTMLElement) { - terminal.element = surface - }, - loadAddon() {}, - resize(cols: number, rows: number) { - terminal.cols = cols - terminal.rows = rows - }, - clear() {}, - reset() {}, - refresh() {}, - selectAll() {}, - clearSelection() {}, - select() {}, - scrollLines, - scrollToBottom() {}, - scrollToLine() {}, - attachCustomKeyEventHandler() {}, - getSelection: () => '', - onData: () => ({ dispose() {} }), - onLineFeed: () => ({ dispose() {} }), - onScroll: () => ({ dispose() {} }), - onWriteParsed: () => ({ dispose() {} }), - dispose() {} - } - return terminal -} - -function dispatchWheel(deltaY: number, init: WheelEventInit = {}): WheelEvent { - const surface = document.getElementById('terminal-surface') - if (!surface) { - throw new Error('terminal surface missing') - } - const event = new WheelEvent('wheel', { - bubbles: true, - cancelable: true, - clientX: 40, - clientY: 60, - deltaMode: 0, - deltaY, - ...init - }) - if (init.ctrlKey) { - // Happy DOM drops wheel modifier initialization; set flags explicitly. - Object.defineProperty(event, 'ctrlKey', { value: true }) - } - surface.dispatchEvent(event) - return event -} - -function terminalInputBytes(postMessage: ReturnType): string { - return postMessage.mock.calls - .map(([raw]) => JSON.parse(String(raw)) as { bytes?: string; type: string }) - .filter((msg) => msg.type === 'terminal-input') - .map((msg) => msg.bytes ?? '') - .join('') -} - -describe('terminal WebView external pointer wheel scrolling', () => { - let animationFrames: Array<() => void> - let buffer: BufferState - let postMessage: Mock<(data: string) => void> - let registeredWindowListeners: RegisteredWindowListener[] - let scrollLines: Mock<(lines: number) => void> - let terminals: TerminalStub[] - - function boot(): void { - document.body.innerHTML = bodyMarkup() - new Function(TERMINAL_DOCUMENT_SCRIPT)() - window.dispatchEvent( - new MessageEvent('message', { - data: JSON.stringify({ type: 'init', cols: 40, rows: 24, initialData: '' }) - }) - ) - // Wait for init’s next-frame surface commit. - while (animationFrames.length > 0) { - animationFrames.shift()?.() - } - } - - function flushFrames(): void { - for (let i = 0; i < 4 && animationFrames.length > 0; i++) { - animationFrames.shift()?.() - } - } - - beforeEach(() => { - animationFrames = [] - buffer = { baseY: 0, type: 'normal', viewportY: 0 } - registeredWindowListeners = [] - scrollLines = vi.fn<(lines: number) => void>() - terminals = [] - const addWindowEventListener = window.addEventListener.bind(window) - vi.spyOn(window, 'addEventListener').mockImplementation((( - type: string, - listener: EventListenerOrEventListenerObject, - options?: boolean | AddEventListenerOptions - ) => { - registeredWindowListeners.push({ type, listener, options }) - addWindowEventListener(type, listener, options) - }) as typeof window.addEventListener) - vi.stubGlobal('requestAnimationFrame', (callback: () => void) => { - animationFrames.push(callback) - return animationFrames.length - }) - vi.stubGlobal('cancelAnimationFrame', () => {}) - Object.defineProperty(window, 'innerWidth', { value: 381, configurable: true }) - Object.defineProperty(window, 'innerHeight', { value: 612, configurable: true }) - postMessage = vi.fn<(data: string) => void>() - const webWindow = window as unknown as { - Terminal: new () => TerminalStub - ReactNativeWebView: { postMessage: (data: string) => void } - } - webWindow.Terminal = function () { - const terminal = makeTerminal(buffer, scrollLines) - terminals.push(terminal) - return terminal - } as unknown as new () => TerminalStub - webWindow.ReactNativeWebView = { postMessage } - }) - - afterEach(() => { - for (const { type, listener, options } of registeredWindowListeners) { - window.removeEventListener(type, listener as EventListener, options) - } - vi.restoreAllMocks() - vi.unstubAllGlobals() - }) - - it('turns an alternate-screen wheel scroll into cursor keys (#6863, #8818)', () => { - buffer = { baseY: 0, type: 'alternate', viewportY: 0 } - boot() - expect(terminals).toHaveLength(1) - - dispatchWheel(3 * CELL_HEIGHT) - - // Alternate-screen TUIs have no scrollback; wheels must reach the PTY as input, matching touch. - expect(terminalInputBytes(postMessage)).toBe(ESC_ARROW_DOWN.repeat(3)) - }) - - it('sends application cursor keys when the TUI enabled DECCKM', () => { - buffer = { baseY: 0, type: 'alternate', viewportY: 0 } - boot() - const terminal = terminals[0] - if (!terminal) { - throw new Error('terminal missing') - } - ;(terminal.modes as Record).applicationCursorKeysMode = true - - dispatchWheel(-2 * CELL_HEIGHT) - - expect(terminalInputBytes(postMessage)).toBe(ESC_APP_ARROW_UP.repeat(2)) - }) - - it('reports the wheel as mouse tracking bytes when the TUI asked for them', () => { - buffer = { baseY: 0, type: 'alternate', viewportY: 0 } - boot() - const terminal = terminals[0] - if (!terminal) { - throw new Error('terminal missing') - } - terminal.modes.mouseTrackingMode = 'any' - window.dispatchEvent( - new MessageEvent('message', { - data: JSON.stringify({ type: 'write', data: `${ESC}[?1006l` }) - }) - ) - - dispatchWheel(CELL_HEIGHT) - - const bytes = terminalInputBytes(postMessage) - expect(bytes.slice(0, 4)).toBe(`${ESC}[Ma`) - expect(bytes).toHaveLength(6) - expect(bytes.charCodeAt(4)).toBeGreaterThanOrEqual(33) - expect(bytes.charCodeAt(5)).toBeGreaterThanOrEqual(33) - }) - - it('scrolls normal-buffer scrollback from a wheel instead of leaving it frozen', () => { - buffer = { baseY: 20, type: 'normal', viewportY: 20 } - boot() - - dispatchWheel(-3 * CELL_HEIGHT) - flushFrames() - - expect(scrollLines).toHaveBeenCalledWith(-3) - }) - - it('converts line-mode deltas that Android reports for external mouse wheels', () => { - buffer = { baseY: 0, type: 'alternate', viewportY: 0 } - boot() - - dispatchWheel(2, { deltaMode: 1 }) - - expect(terminalInputBytes(postMessage)).toBe(ESC_ARROW_DOWN.repeat(2)) - }) - - it('swallows a trackpad pinch instead of firing cursor keys at the TUI', () => { - buffer = { baseY: 0, type: 'alternate', viewportY: 0 } - boot() - - const event = dispatchWheel(3 * CELL_HEIGHT, { ctrlKey: true }) - - expect(terminalInputBytes(postMessage)).toBe('') - expect(event.defaultPrevented).toBe(true) - }) - - it('clears fractional wheel state when the terminal is recreated', () => { - buffer = { baseY: 0, type: 'alternate', viewportY: 0 } - boot() - dispatchWheel(CELL_HEIGHT - 1) - - window.dispatchEvent( - new MessageEvent('message', { - data: JSON.stringify({ type: 'init', cols: 40, rows: 24, initialData: '' }) - }) - ) - flushFrames() - dispatchWheel(1) - - expect(terminalInputBytes(postMessage)).toBe('') - }) -}) diff --git a/mobile/src/terminal/terminal-write-coalescer.test.ts b/mobile/src/terminal/terminal-write-coalescer.test.ts index 5cc20d8fef1..41e833f8990 100644 --- a/mobile/src/terminal/terminal-write-coalescer.test.ts +++ b/mobile/src/terminal/terminal-write-coalescer.test.ts @@ -1,11 +1,5 @@ import { afterEach, describe, expect, it, vi } from 'vitest' -import { - createTerminalWriteCoalescer, - TERMINAL_WRITE_FLUSH_WINDOW_MS, - TERMINAL_WRITE_MAX_PENDING_UNITS, - TERMINAL_INTERACTIVE_WRITE_WINDOW_MS, - TERMINAL_INTERACTIVE_WRITE_DURATION_MS -} from './terminal-write-coalescer' +import { createTerminalWriteCoalescer } from './terminal-write-coalescer' function createDeliverySink() { const delivered: string[] = [] @@ -32,7 +26,7 @@ describe('terminal write coalescer', () => { coalescer.write('c') expect(sink.delivered).toEqual(['a']) - vi.advanceTimersByTime(TERMINAL_WRITE_FLUSH_WINDOW_MS) + vi.advanceTimersByTime(48) expect(sink.delivered).toEqual(['a', 'bc']) }) @@ -47,25 +41,10 @@ describe('terminal write coalescer', () => { vi.setSystemTime(100_000 - 60 * 60 * 1000) coalescer.write('b') - vi.advanceTimersByTime(TERMINAL_WRITE_FLUSH_WINDOW_MS) + vi.advanceTimersByTime(48) expect(sink.delivered).toEqual(['a', 'b']) }) - it('flushes immediately when the pending buffer exceeds the size cap', () => { - vi.useFakeTimers() - const sink = createDeliverySink() - const coalescer = createTerminalWriteCoalescer(sink.deliver) - - coalescer.write('a') - coalescer.write('x'.repeat(TERMINAL_WRITE_MAX_PENDING_UNITS + 1)) - - expect(sink.delivered).toHaveLength(2) - expect(sink.delivered[1]).toHaveLength(TERMINAL_WRITE_MAX_PENDING_UNITS + 1) - expect(vi.getTimerCount()).toBe(0) - vi.runOnlyPendingTimers() - expect(sink.delivered).toHaveLength(2) - }) - it('flushNow() delivers the joined pending buffer synchronously and cancels the timer', () => { vi.useFakeTimers() const sink = createDeliverySink() @@ -77,25 +56,10 @@ describe('terminal write coalescer', () => { coalescer.flushNow() expect(sink.delivered).toEqual(['a', 'bc']) - expect(vi.getTimerCount()).toBe(0) vi.runOnlyPendingTimers() expect(sink.delivered).toEqual(['a', 'bc']) }) - it('shortens an existing output wait when a scroll gesture arrives', () => { - vi.useFakeTimers() - const sink = createDeliverySink() - const coalescer = createTerminalWriteCoalescer(sink.deliver) - coalescer.write('first') - vi.advanceTimersByTime(5) - coalescer.write('response') - vi.advanceTimersByTime(3) - coalescer.markInteractive() - vi.advanceTimersByTime(TERMINAL_INTERACTIVE_WRITE_WINDOW_MS - 8) - expect(sink.delivered).toEqual(['first', 'response']) - expect(vi.getTimerCount()).toBe(0) - }) - it('does not postpone output when gestures repeat faster than one frame', () => { vi.useFakeTimers() const sink = createDeliverySink() @@ -105,11 +69,9 @@ describe('terminal write coalescer', () => { for (let i = 0; i < 8; i += 1) { coalescer.write(`${i}`) coalescer.markInteractive() - expect(vi.getTimerCount()).toBe(1) vi.advanceTimersByTime(2) } expect(sink.delivered).toEqual(['first', '01234567']) - expect(vi.getTimerCount()).toBe(0) }) it('clears interactive state and pending output when a document is replaced', () => { @@ -122,25 +84,9 @@ describe('terminal write coalescer', () => { coalescer.clear() coalescer.write('new') coalescer.write('new-trailing') - vi.advanceTimersByTime(TERMINAL_INTERACTIVE_WRITE_WINDOW_MS) + vi.advanceTimersByTime(16) expect(sink.delivered).toEqual(['old', 'new']) - vi.advanceTimersByTime(TERMINAL_WRITE_FLUSH_WINDOW_MS - TERMINAL_INTERACTIVE_WRITE_WINDOW_MS) + vi.advanceTimersByTime(32) expect(sink.delivered).toEqual(['old', 'new', 'new-trailing']) }) - - it('expires interactive batching on a monotonic clock after a wall-clock rollback', () => { - vi.useFakeTimers() - vi.setSystemTime(100_000) - const sink = createDeliverySink() - const coalescer = createTerminalWriteCoalescer(sink.deliver) - coalescer.markInteractive() - vi.setSystemTime(0) - vi.advanceTimersByTime(TERMINAL_INTERACTIVE_WRITE_DURATION_MS) - coalescer.write('first') - coalescer.write('second') - vi.advanceTimersByTime(TERMINAL_INTERACTIVE_WRITE_WINDOW_MS) - expect(sink.delivered).toEqual(['first']) - vi.advanceTimersByTime(TERMINAL_WRITE_FLUSH_WINDOW_MS - TERMINAL_INTERACTIVE_WRITE_WINDOW_MS) - expect(sink.delivered).toEqual(['first', 'second']) - }) }) diff --git a/mobile/src/terminal/use-terminal-live-input-click.test.ts b/mobile/src/terminal/use-terminal-live-input-click.test.ts index c855decfb7d..059167df485 100644 --- a/mobile/src/terminal/use-terminal-live-input-click.test.ts +++ b/mobile/src/terminal/use-terminal-live-input-click.test.ts @@ -17,12 +17,11 @@ type ClickHarness = { readonly captures: readonly string[] readonly handlers: Handlers readonly sent: readonly string[] - readonly changeHandle: (handle: string) => void readonly unmount: () => void } function createClickHarness(sender?: TerminalLiveInputSender): ClickHarness { - let activeHandle = 'terminal-a' + const activeHandle = 'terminal-a' const activeHandleRef: RefObject = { current: activeHandle } const activeSessionTabTypeRef: RefObject = { current: 'terminal' } const captures: string[] = [] @@ -69,11 +68,6 @@ function createClickHarness(sender?: TerminalLiveInputSender): ClickHarness { return handlers }, sent, - changeHandle: (handle) => { - activeHandle = handle - activeHandleRef.current = handle - act(() => renderer?.update(createElement(Harness))) - }, unmount: () => { act(() => renderer?.unmount()) } @@ -171,21 +165,18 @@ describe('terminal live input around a click', () => { } }) - it.each(['insertText', 'insertFromPaste', 'insertCompositionText'])( - 'preserves immediate matching DOM input %s without a key event', - async (inputType) => { - const { handlers, sent, captures, unmount } = createClickHarness() - try { - change(handlers, 'x') - await handlers.sendLiveInputClick('terminal-a', CLICK) - handlers.handleLiveInputChange({ - nativeEvent: { text: 'x', isComposing: false, inputType } - }) - await vi.waitFor(() => expect(sent.join('')).toBe('x' + CLICK + 'x')) - expect(captures.at(-1)).toBe('x') - } finally { - unmount() - } + it('preserves immediate matching DOM input without a key event', async () => { + const { handlers, sent, captures, unmount } = createClickHarness() + try { + change(handlers, 'x') + await handlers.sendLiveInputClick('terminal-a', CLICK) + handlers.handleLiveInputChange({ + nativeEvent: { text: 'x', isComposing: false, inputType: 'insertText' } + }) + await vi.waitFor(() => expect(sent.join('')).toBe(`x${CLICK}x`)) + expect(captures.at(-1)).toBe('x') + } finally { + unmount() } - ) + }) }) diff --git a/mobile/src/terminal/use-terminal-live-input-commit.test.ts b/mobile/src/terminal/use-terminal-live-input-commit.test.ts index 69927053d8e..9f45a1c1abc 100644 --- a/mobile/src/terminal/use-terminal-live-input-commit.test.ts +++ b/mobile/src/terminal/use-terminal-live-input-commit.test.ts @@ -3,7 +3,6 @@ import { act, create, type ReactTestRenderer } from 'react-test-renderer' import type { TextInputInstance } from 'react-native' import { afterEach, describe, expect, it, vi } from 'vitest' import type { TerminalLiveInputSender } from './terminal-live-input-sender' -import { TERMINAL_LIVE_HELD_PREEDIT_COMMIT_DELAY_MS } from './terminal-live-preedit-mirror' import { useTerminalLiveInputCommit } from './use-terminal-live-input-commit' vi.mock('../platform/live-input-composing-range', () => ({ @@ -21,25 +20,18 @@ function changeLiveInput( } type TerminalLiveInputCommitHarness = { - readonly failures: ReturnType readonly captures: readonly string[] - readonly getHandlers: () => TerminalLiveInputCommitHandlers readonly handlers: TerminalLiveInputCommitHandlers readonly sent: readonly string[] - readonly setActiveSessionTabType: (next: string | undefined) => void - readonly setConnected: (next: boolean) => void - readonly setSendResult: (next: boolean) => void readonly unmount: () => void } type TerminalLiveInputCommitHarnessOptions = { - readonly sendResult?: boolean readonly sender?: TerminalLiveInputSender readonly bracketedPaste?: boolean } function createTerminalLiveInputCommitHarness({ - sendResult = true, sender, bracketedPaste = false }: TerminalLiveInputCommitHarnessOptions = {}): TerminalLiveInputCommitHarness { @@ -52,16 +44,12 @@ function createTerminalLiveInputCommitHarness({ } const liveInputRef: RefObject = { current: null } const sent: string[] = [] - const failures = vi.fn() - let currentSendResult = sendResult const sendLiveTerminalInputRef: RefObject = { current: async (handle, bytes) => { sent.push(bytes) - return sender ? sender(handle, bytes) : currentSendResult + return sender ? sender(handle, bytes) : true } } - let currentActiveSessionTabType: string | undefined = 'terminal' - let currentConnected = true let handlers: TerminalLiveInputCommitHandlers | null = null let renderer: ReactTestRenderer | null = null @@ -69,10 +57,9 @@ function createTerminalLiveInputCommitHarness({ handlers = useTerminalLiveInputCommit({ activeHandle, activeHandleRef, - activeSessionTabType: currentActiveSessionTabType, + activeSessionTabType: 'terminal', activeSessionTabTypeRef, - connected: currentConnected, - onSendFailure: failures, + connected: true, framesLivePaste: () => bracketedPaste, liveInputRef, sendLiveTerminalInputRef, @@ -89,33 +76,9 @@ function createTerminalLiveInputCommitHarness({ } return { - failures, captures, - getHandlers: () => { - if (!handlers) { - throw new Error('terminal live input hook is not mounted') - } - return handlers - }, handlers, sent, - setActiveSessionTabType: (next: string | undefined): void => { - currentActiveSessionTabType = next - // Couple tab ref and prop as production does; both become null during inventory lag. - activeSessionTabTypeRef.current = next ?? null - act(() => { - renderer?.update(createElement(Harness)) - }) - }, - setConnected: (next: boolean): void => { - currentConnected = next - act(() => { - renderer?.update(createElement(Harness)) - }) - }, - setSendResult: (next: boolean): void => { - currentSendResult = next - }, unmount: () => { act(() => renderer?.unmount()) } @@ -134,7 +97,7 @@ describe('terminal live input commit hook', () => { .fn() .mockReturnValueOnce(pending) .mockResolvedValue(true) - const { handlers, sent, captures, failures, unmount } = createTerminalLiveInputCommitHarness({ + const { handlers, sent, captures, unmount } = createTerminalLiveInputCommitHarness({ sender }) changeLiveInput(handlers, 'abc', false) @@ -145,14 +108,12 @@ describe('terminal live input commit hook', () => { await expect(submit).resolves.toBe(false) expect(sent).toEqual(['abc']) expect(captures.at(-1)).toBe('') - expect(failures).toHaveBeenCalledTimes(1) changeLiveInput(handlers, 'ab', false) changeLiveInput(handlers, 'x', false) await vi.waitFor(() => expect(sent).toEqual(['abc', 'x'])) changeLiveInput(handlers, '', false) await vi.waitFor(() => expect(sent).toEqual(['abc', 'x', '\x7f'])) - expect(failures).toHaveBeenCalledTimes(1) unmount() } ) @@ -170,7 +131,7 @@ describe('terminal live input commit hook', () => { expect(sent).toEqual([`\x1b[200~${text}\x1b[201~`, 'CLICK']) expect(captures.at(-1)).toBe('') changeLiveInput(handlers, 'x', false) - await vi.advanceTimersByTimeAsync(TERMINAL_LIVE_HELD_PREEDIT_COMMIT_DELAY_MS * 2) + await vi.advanceTimersByTimeAsync(600) expect(sent).toEqual([`\x1b[200~${text}\x1b[201~`, 'CLICK', 'x']) } finally { unmount() diff --git a/mobile/src/terminal/use-terminal-text-field-submit-binding.test.tsx b/mobile/src/terminal/use-terminal-text-field-submit-binding.test.tsx index 37688c3ff3a..e26bca9fc6b 100644 --- a/mobile/src/terminal/use-terminal-text-field-submit-binding.test.tsx +++ b/mobile/src/terminal/use-terminal-text-field-submit-binding.test.tsx @@ -7,27 +7,19 @@ import { useTerminalTextFieldSubmitBinding } from './use-terminal-text-field-sub // Mock only the platform listener seam so tests can invoke the closure it actually captured. const mocks = vi.hoisted(() => { - const boundHandlers: Array<() => void> = [] return { - boundHandlers, - chordHandlers: new Array<(input: TerminalLiveAccessoryInput) => boolean>(), - unbindCount: 0, - releasedCount: 0 + chordHandlers: new Array<(input: TerminalLiveAccessoryInput) => boolean>() } }) vi.mock('./terminal-text-field-submit-binding', () => ({ bindTerminalTextFieldSubmit: ( _node: unknown, - onSubmit: () => void, + _onSubmit: () => void, onChord: (input: TerminalLiveAccessoryInput) => boolean ) => { mocks.chordHandlers.push(onChord) - mocks.boundHandlers.push(onSubmit) - mocks.unbindCount += 1 - return () => { - mocks.releasedCount += 1 - } + return () => {} } })) @@ -59,31 +51,10 @@ const node = {} as TextInputInstance function reset(): void { submitted = [] mocks.chordHandlers.length = 0 - mocks.boundHandlers.length = 0 - mocks.unbindCount = 0 - mocks.releasedCount = 0 } describe('the terminal text field submit binding', () => { - it('calls the handler the newest commit supplied, not the first', () => { - reset() - let renderer: ReactTestRenderer | null = null - - act(() => { - renderer = create(createElement(Harness, { value: 'first' })) - }) - act(() => { - renderer?.update(createElement(Harness, { value: 'second' })) - }) - act(() => { - mocks.boundHandlers.at(-1)?.() - }) - - expect(submitted).toEqual(['second']) - act(() => renderer?.unmount()) - }) - - it('refreshes a keyboard chord handler after a terminal or connection change', () => { + it('refreshes a captured keyboard chord handler after a commit', () => { reset() let renderer: ReactTestRenderer | null = null act(() => { @@ -98,22 +69,4 @@ describe('the terminal text field submit binding', () => { expect(submitted).toEqual(['first', 'second']) act(() => renderer?.unmount()) }) - - it('drops the binding when the field unmounts', () => { - reset() - let renderer: ReactTestRenderer | null = null - - act(() => { - renderer = create(createElement(Harness, { value: 'first' })) - }) - act(() => { - mocks.boundHandlers.at(-1)?.() - }) - - expect(submitted).toEqual(['first']) - expect(field.current).toBe(node) - act(() => renderer?.unmount()) - expect(field.current).toBeNull() - expect(mocks.releasedCount).toBe(mocks.unbindCount) - }) }) diff --git a/src/cli/runtime/envelope-schema.test.ts b/src/cli/runtime/envelope-schema.test.ts index 75a00185cde..365f4c5de09 100644 --- a/src/cli/runtime/envelope-schema.test.ts +++ b/src/cli/runtime/envelope-schema.test.ts @@ -1,17 +1,7 @@ import { describe, expect, it } from 'vitest' -import { RuntimeRpcEnvelopeSchema, isKeepaliveFrame } from './envelope-schema' +import { RuntimeRpcEnvelopeSchema } from './envelope-schema' describe('RuntimeRpcEnvelopeSchema', () => { - it('accepts a well-formed success envelope', () => { - const parsed = RuntimeRpcEnvelopeSchema.safeParse({ - id: 'req-1', - ok: true, - result: { anything: 1 }, - _meta: { runtimeId: 'runtime-1' } - }) - expect(parsed.success).toBe(true) - }) - it('accepts and strips additive future success fields', () => { const parsed = RuntimeRpcEnvelopeSchema.safeParse({ id: 'req-1', @@ -32,33 +22,6 @@ describe('RuntimeRpcEnvelopeSchema', () => { }) }) - it('accepts a well-formed failure envelope', () => { - const parsed = RuntimeRpcEnvelopeSchema.safeParse({ - id: 'req-1', - ok: false, - error: { code: 'not_found', message: 'not_found' }, - _meta: { runtimeId: 'runtime-1' } - }) - expect(parsed.success).toBe(true) - }) - - it('accepts structured failure data for agent recovery hints', () => { - const parsed = RuntimeRpcEnvelopeSchema.safeParse({ - id: 'req-1', - ok: false, - error: { - code: 'app_not_found', - message: 'app not found: Gmail', - data: { - nextSteps: ['Target the desktop browser app/window that contains Gmail.'] - } - }, - _meta: { runtimeId: 'runtime-1' } - }) - - expect(parsed.success).toBe(true) - }) - it('accepts and strips additive future failure fields', () => { const parsed = RuntimeRpcEnvelopeSchema.safeParse({ id: 'req-1', @@ -83,82 +46,4 @@ describe('RuntimeRpcEnvelopeSchema', () => { } }) }) - - it('accepts a failure envelope without _meta', () => { - // Failures before runtimeId resolution legitimately omit _meta. - const parsed = RuntimeRpcEnvelopeSchema.safeParse({ - id: 'req-1', - ok: false, - error: { code: 'runtime_unavailable', message: 'runtime_unavailable' } - }) - expect(parsed.success).toBe(true) - }) - - it('rejects a frame missing the ok discriminator', () => { - const parsed = RuntimeRpcEnvelopeSchema.safeParse({ - id: 'req-1', - result: {}, - _meta: { runtimeId: 'runtime-1' } - }) - expect(parsed.success).toBe(false) - }) - - it('rejects a frame with a non-string id', () => { - const parsed = RuntimeRpcEnvelopeSchema.safeParse({ - id: 123, - ok: true, - result: {}, - _meta: { runtimeId: 'runtime-1' } - }) - expect(parsed.success).toBe(false) - }) - - it('still rejects malformed known fields when future fields are present', () => { - const parsed = RuntimeRpcEnvelopeSchema.safeParse({ - id: 'req-1', - ok: false, - error: { code: 500, message: 'Nope', futureErrorField: true }, - futureEnvelopeField: 'new' - }) - - expect(parsed.success).toBe(false) - }) - - it('rejects a frame with unrelated fields only', () => { - const parsed = RuntimeRpcEnvelopeSchema.safeParse({ hello: 'world' }) - expect(parsed.success).toBe(false) - }) - - it('accepts a keepalive frame', () => { - const parsed = RuntimeRpcEnvelopeSchema.safeParse({ _keepalive: true }) - expect(parsed.success).toBe(true) - }) - - it('rejects a keepalive frame with _keepalive !== true', () => { - const parsed = RuntimeRpcEnvelopeSchema.safeParse({ _keepalive: false }) - expect(parsed.success).toBe(false) - }) -}) - -describe('isKeepaliveFrame', () => { - it('returns true for a well-formed keepalive', () => { - expect(isKeepaliveFrame({ _keepalive: true })).toBe(true) - }) - - it('returns false for a success envelope', () => { - expect(isKeepaliveFrame({ id: 'x', ok: true, result: {}, _meta: { runtimeId: 'r' } })).toBe( - false - ) - }) - - it('returns false for non-object inputs', () => { - expect(isKeepaliveFrame(null)).toBe(false) - expect(isKeepaliveFrame(undefined)).toBe(false) - expect(isKeepaliveFrame('keepalive')).toBe(false) - }) - - it('returns false when _keepalive is not strictly true', () => { - expect(isKeepaliveFrame({ _keepalive: 1 })).toBe(false) - expect(isKeepaliveFrame({ _keepalive: 'true' })).toBe(false) - }) }) diff --git a/src/cli/runtime/profile-state-recovery-launch.test.ts b/src/cli/runtime/profile-state-recovery-launch.test.ts index 67ea0019a0e..c27b5b7bdcf 100644 --- a/src/cli/runtime/profile-state-recovery-launch.test.ts +++ b/src/cli/runtime/profile-state-recovery-launch.test.ts @@ -4,10 +4,7 @@ import { PROFILE_STATE_RECOVERY_FLAG, PROFILE_STATE_RECOVERY_RESULT_PREFIX } from '../../shared/profile-state-recovery-command' -import { - canLaunchProfileStateRecovery, - launchProfileStateRecovery -} from './profile-state-recovery-launch' +import { launchProfileStateRecovery } from './profile-state-recovery-launch' const mocks = vi.hoisted(() => ({ run: vi.fn() })) vi.mock('@orca/process-host', () => ({ runProcess: mocks.run })) @@ -47,17 +44,6 @@ beforeEach(() => { afterEach(() => vi.unstubAllEnvs()) describe('profile-state recovery launch', () => { - it('requires an app executable or Electron Node mode to launch recovery', () => { - vi.stubEnv('ELECTRON_RUN_AS_NODE', undefined) - vi.stubEnv('ORCA_APP_EXECUTABLE', undefined) - expect(canLaunchProfileStateRecovery()).toBe(false) - vi.stubEnv('ELECTRON_RUN_AS_NODE', '1') - expect(canLaunchProfileStateRecovery()).toBe(true) - vi.stubEnv('ELECTRON_RUN_AS_NODE', undefined) - vi.stubEnv('ORCA_APP_EXECUTABLE', '/explicit/Orca') - expect(canLaunchProfileStateRecovery()).toBe(true) - }) - it('uses a foreground-safe serve request and binds the canonical recovery root', async () => { vi.stubEnv('ELECTRON_RUN_AS_NODE', '1') vi.stubEnv('ORCA_USER_DATA_PATH', '/stale/root') @@ -81,21 +67,6 @@ describe('profile-state recovery launch', () => { expect(mocks.run.mock.calls[0][0].env).not.toHaveProperty('ELECTRON_RUN_AS_NODE') }) - it('round-trips current JSON selection without requiring an invented revision', async () => { - const current = { ...result, revision: null } - mocks.run.mockResolvedValue({ - code: 0, - signal: null, - timedOut: false, - stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}${JSON.stringify({ ok: true, result: current })}\n`, - stderr: '' - }) - expect( - await launchProfileStateRecovery({ userDataPath: '.', selector: { kind: 'current-json' } }) - ).toEqual(current) - expect(mocks.run.mock.calls[0][0].args.at(-1)).toContain('"kind":"current-json"') - }) - it('preserves a structured refusal from the lock owner', async () => { mocks.run.mockResolvedValue({ code: 1, @@ -107,13 +78,6 @@ describe('profile-state recovery launch', () => { }) }) - it('round-trips latest JSON selection and the exported SQLite revision', async () => { - expect( - await launchProfileStateRecovery({ userDataPath: '.', selector: { kind: 'latest-json' } }) - ).toEqual(result) - expect(mocks.run.mock.calls[0][0].args.at(-1)).toContain('"kind":"latest-json"') - }) - it.each([ { code: 1 }, { signal: 'SIGKILL' }, @@ -121,41 +85,12 @@ describe('profile-state recovery launch', () => { { outputTruncated: true }, { stdout: '' }, { stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}{` }, - { stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}{"ok":true,"result":{}}` }, - { - stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}{}\n${PROFILE_STATE_RECOVERY_RESULT_PREFIX}{}` - } - ])('rejects incomplete or ambiguous child results %j', async (override) => { + { stdout: `${PROFILE_STATE_RECOVERY_RESULT_PREFIX}{"ok":true,"result":{}}` } + ])('rejects incomplete child results %j', async (override) => { const original = await mocks.run() mocks.run.mockResolvedValue({ ...original, ...override }) await expect(launchProfileStateRecovery(request)).rejects.toMatchObject({ code: 'runtime_error' }) }) - - it('propagates launch failure without retrying another recovery path', async () => { - mocks.run.mockRejectedValue(new Error('Executable unavailable')) - await expect(launchProfileStateRecovery(request)).rejects.toThrow('Executable unavailable') - expect(mocks.run).toHaveBeenCalledOnce() - }) - - it('retains bounded child diagnostics when recovery exits without a result', async () => { - mocks.run.mockResolvedValue({ - code: null, - signal: 'SIGTRAP', - timedOut: false, - stdout: '', - stderr: `${'x'.repeat(5000)}\nsandbox unavailable\n` - }) - await expect(launchProfileStateRecovery(request)).rejects.toMatchObject({ - data: { - exitCode: null, - signal: 'SIGTRAP', - timedOut: false, - outputTruncated: false, - stderr: `${'x'.repeat(5000)}\nsandbox unavailable`.slice(-4096) - } - }) - expect(mocks.run).toHaveBeenCalledOnce() - }) }) diff --git a/src/cli/runtime/runtime-listener-probe.test.ts b/src/cli/runtime/runtime-listener-probe.test.ts index e0018e059c8..05ddf3ff7d1 100644 --- a/src/cli/runtime/runtime-listener-probe.test.ts +++ b/src/cli/runtime/runtime-listener-probe.test.ts @@ -109,30 +109,4 @@ describe('probeRuntimeListener', () => { vi.useRealTimers() } }) - - it.each([ - ['a missing endpoint', undefined], - ['a blank endpoint', ' '], - ['a numeric endpoint', 42] - ])('frees a profile whose metadata carries %s instead of dialling it', async (_label, value) => { - // Invalid endpoints can throw or dial an unrelated numeric TCP port. - const metadata = metadataFor(nextEndpoint()) - const transport = { ...metadata.transports[0]! } - Object.assign(transport, { endpoint: value }) - - await expect(probeRuntimeListener({ ...metadata, transports: [transport] })).resolves.toBe( - 'not-listening' - ) - }) - - it('frees a profile whose metadata published no local transport to probe', async () => { - const metadata = metadataFor(nextEndpoint()) - - await expect( - probeRuntimeListener({ - ...metadata, - transports: metadata.transports.filter((transport) => transport.kind === 'websocket') - }) - ).resolves.toBe('not-listening') - }) }) diff --git a/src/cli/runtime/runtime-source-fence.test.ts b/src/cli/runtime/runtime-source-fence.test.ts index 95667fa8c8e..e73676313b3 100644 --- a/src/cli/runtime/runtime-source-fence.test.ts +++ b/src/cli/runtime/runtime-source-fence.test.ts @@ -11,7 +11,6 @@ import { } from '../../shared/runtime-source-env' import { RuntimeClient } from './client' import { RuntimeClientError } from './types' -import { resolveFencedRuntimeSource } from './runtime-source-fence' const SOURCE_A = '11111111-1111-4111-8111-111111111111' const SOURCE_B = '22222222-2222-4222-8222-222222222222' @@ -126,27 +125,16 @@ describe('RuntimeClient runtime-source fence', () => { expect(runtime.methods).toEqual(['status.get', 'status.get']) }) - it('accepts a restarted runtime of the same source and probes it only once', async () => { + it('accepts a restarted runtime of the same source and carries its verified identity', async () => { const runtime = await startRuntime({ runtimeId: 'runtime-a2', installationId: SOURCE_A }) launchedBy(SOURCE_A, 'runtime-a1') const client = new RuntimeClient(runtime.userDataPath, 2_000, null, null, 'orca') await client.call('terminal.send', { terminal: 't1', text: 'hi' }) - await client.call('worktree.list') - expect(runtime.methods).toEqual(['status.get', 'terminal.send', 'worktree.list']) + expect(runtime.methods).toEqual(['status.get', 'terminal.send']) // The host must recheck identity if it changes after the probe. const expected = { sourceId: SOURCE_A, runtimeId: 'runtime-a2' } - expect(runtime.expectations).toEqual([undefined, expected, expected]) - }) - - it('sends without a probe when the launching runtime is still the live one', async () => { - const runtime = await startRuntime({ runtimeId: 'runtime-a', installationId: SOURCE_A }) - launchedBy(SOURCE_A, 'runtime-a') - const client = new RuntimeClient(runtime.userDataPath, 2_000, null, null, 'orca') - - await client.call('worktree.list') - expect(runtime.methods).toEqual(['worktree.list']) - expect(runtime.expectations).toEqual([{ sourceId: SOURCE_A, runtimeId: 'runtime-a' }]) + expect(runtime.expectations).toEqual([undefined, expected]) }) it('refuses when the live runtime cannot say which profile it serves', async () => { @@ -177,11 +165,4 @@ describe('RuntimeClient runtime-source fence', () => { await client.call('worktree.list') expect(runtime.methods).toEqual(['worktree.list']) }) - - it('still fences a terminal whose CLI fell back to the platform default profile', () => { - const stamp = { sourceId: SOURCE_A, incarnation: 'runtime-a', profilePath: '/profiles/custom' } - expect(resolveFencedRuntimeSource(stamp, '/profiles/default', '/profiles/default')).toBe(stamp) - expect(resolveFencedRuntimeSource(stamp, '/profiles/custom', '/profiles/default')).toBe(stamp) - expect(resolveFencedRuntimeSource(stamp, '/profiles/dev', '/profiles/default')).toBeNull() - }) }) diff --git a/src/cli/runtime/serve-appimage-update.test.ts b/src/cli/runtime/serve-appimage-update.test.ts index c9535d0c42a..90b44aa2424 100644 --- a/src/cli/runtime/serve-appimage-update.test.ts +++ b/src/cli/runtime/serve-appimage-update.test.ts @@ -68,14 +68,6 @@ function restartDeps(overrides: Partial = {}): AppImageRest } describe('supervised serve and a replaced AppImage', () => { - it('finds the AppImage a stable-launcher service was unpacked from', async () => { - const { appImagePath, executable } = await makeRunningGeneration() - const watch = resolveAppImageBuildWatch(executable, {}, 'linux') - expect(watch?.appImagePath).toBe(appImagePath) - expect(inspectAppImageBuild(watch!)).toBe('current') - expect(resolveAppImageBuildWatch(executable, {}, 'darwin')).toBeNull() - }) - it('ignores a source record that names another AppImage', async () => { const { root, executable } = await makeRunningGeneration() const namespacePath = join(executable, '..', '..') @@ -159,10 +151,9 @@ describe('supervised serve and a replaced AppImage', () => { const watch = resolveAppImageBuildWatch(executable, {}, 'linux')! await swapIn(appImagePath, 'build C') const attemptPath = join(root, 'attempt.json') - const logs: string[] = [] const make = (overrides: { force?: boolean; runningKey?: string } = {}) => createSupervisedAppImageUpdate({ - ...restartDeps({ log: (line) => logs.push(line) }), + ...restartDeps(), watch: { ...watch, runningKey: overrides.runningKey ?? watch.runningKey }, attemptPath, force: overrides.force ?? false @@ -172,13 +163,10 @@ describe('supervised serve and a replaced AppImage', () => { expect(first.inspect()).toBe('superseded') await first.restart(() => false) const candidateKey = resolveAppImageCacheKey(appImagePath) - expect(JSON.parse(await readFile(attemptPath, 'utf8'))).toEqual({ candidateKey }) // The new build died early; the service manager restarted the old one. const second = make() expect(second.inspect()).toBe('current') - expect(second.inspect()).toBe('current') - expect(logs.filter((line) => line.includes('did not come up'))).toHaveLength(1) expect(make({ force: true }).inspect()).toBe('superseded') await make().restart(() => false) diff --git a/src/cli/runtime/serve-bundle-park.test.ts b/src/cli/runtime/serve-bundle-park.test.ts index 29e8aa34dc1..ac17bf1b2c8 100644 --- a/src/cli/runtime/serve-bundle-park.test.ts +++ b/src/cli/runtime/serve-bundle-park.test.ts @@ -64,13 +64,6 @@ describe('openServeBundlePark', () => { await writeFile(parkRecordPath(dir), JSON.stringify(record)) } - it('takes part only under an installed launchd label on macOS', () => { - expect(open({})).toBeNull() - expect(open({ XPC_SERVICE_NAME: 'application.com.other' })).toBeNull() - expect(open({ XPC_SERVICE_NAME: LABEL }, 'linux')).toBeNull() - expect(open()).not.toBeNull() - }) - it('registers with its child pid and removes the registration on close', async () => { const park = open()! park.heartbeat(77) @@ -157,8 +150,6 @@ describe('openServeBundlePark', () => { it('holds while committed until the bundle changes, then releases', async () => { const park = open()! await writeRecord({ phase: 'commit', commitDeadlineAt: Date.now() + 60_000 }) - const bundleBefore = JSON.parse(await readFile(parkRecordPath(dir), 'utf8')).bundleBefore - expect(bundleBefore).toBe('unused') // 'unused' differs from the real identity, which models a bundle ShipIt already replaced. expect(park.holds()).toBe(false) const identity = formatBundleIdentity( diff --git a/src/cli/runtime/serve-electron-preflight.test.ts b/src/cli/runtime/serve-electron-preflight.test.ts index 4dcd0e1cfb9..51b2d1b21b3 100644 --- a/src/cli/runtime/serve-electron-preflight.test.ts +++ b/src/cli/runtime/serve-electron-preflight.test.ts @@ -167,16 +167,11 @@ describe.skipIf(process.platform === 'win32')('serve private admission snapshots writeFileSync(join(root, 'orca-runtime.json'), JSON.stringify(metadata)) return metadata } - it('keeps the missing-M0 default path synchronous without a second read', () => { - expect(preflightElectronServe({}, root, { platform: 'linux' })).toBeNull() - expect(metadataReader.tryReadMetadata).toHaveBeenCalledTimes(1) - }) it.each([2147483647, process.pid])( 'retains launch permission for absent listener even with PID %s', async (pid) => { publish(pid) await expect(preflightElectronServe({}, root, { platform: 'linux' })).resolves.toBeNull() - expect(metadataReader.tryReadMetadata).toHaveBeenCalledTimes(2) } ) it.each(['disappeared', 'replacement', 'incomplete'])( @@ -209,7 +204,6 @@ describe.skipIf(process.platform === 'win32')('serve private admission snapshots expect(String(vi.mocked(process.stderr.write).mock.calls.at(-1)?.[0])).toContain( change === 'replacement' ? `pid ${process.pid}` : 'pid 2147483647' ) - expect(metadataReader.tryReadMetadata).toHaveBeenCalledTimes(2) } ) }) diff --git a/src/cli/runtime/serve-install-hold.test.ts b/src/cli/runtime/serve-install-hold.test.ts deleted file mode 100644 index ad7a85848dc..00000000000 --- a/src/cli/runtime/serve-install-hold.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, beforeEach, describe, expect, it } from 'vitest' -import { readServeInstallEvidence } from './serve-install-hold' - -describe('readServeInstallEvidence', () => { - let root: string - let bundle: string - let executable: string - - beforeEach(async () => { - root = await mkdtemp(join(tmpdir(), 'orca-install-hold-')) - bundle = join(root, 'Orca.app') - executable = join(bundle, 'Contents', 'MacOS', 'Orca') - await mkdir(join(bundle, 'Contents', 'MacOS'), { recursive: true }) - await writeFile(executable, '') - }) - - afterEach(async () => { - await rm(root, { recursive: true, force: true }) - }) - - const shipIt = (b: string): string => - `${b}/Contents/Frameworks/Squirrel.framework/Resources/ShipIt com.stablyai.orca.ShipIt /state.plist` - - it('holds while ShipIt replaces this bundle, and when the check cannot run', async () => { - await expect( - readServeInstallEvidence(executable, { - platform: 'darwin', - listProcesses: async () => `/sbin/launchd\n${shipIt(bundle)}\n` - }) - ).resolves.toEqual({ kind: 'hold', reason: 'installer-running' }) - await expect( - readServeInstallEvidence(executable, { platform: 'darwin', listProcesses: async () => null }) - ).resolves.toEqual({ kind: 'hold', reason: 'installer-unknown' }) - }) - - it("ignores another bundle's ShipIt and an executable that merely contains the name", async () => { - await expect( - readServeInstallEvidence(executable, { - platform: 'darwin', - listProcesses: async () => `${shipIt('/Applications/Other.app')}\n${executable} --serve\n` - }) - ).resolves.toEqual({ kind: 'clear' }) - }) - - it('holds while the executable is missing, and skips the macOS check elsewhere', async () => { - await expect(readServeInstallEvidence(join(root, 'gone'))).resolves.toEqual({ - kind: 'hold', - reason: 'bundle-missing' - }) - await expect( - readServeInstallEvidence(executable, { - platform: 'linux', - listProcesses: async () => { - throw new Error('must not list processes off macOS') - } - }) - ).resolves.toEqual({ kind: 'clear' }) - }) -}) diff --git a/src/cli/runtime/serve-pid1-init-reaper.test.ts b/src/cli/runtime/serve-pid1-init-reaper.test.ts index f7d85f427d9..e453de42c3b 100644 --- a/src/cli/runtime/serve-pid1-init-reaper.test.ts +++ b/src/cli/runtime/serve-pid1-init-reaper.test.ts @@ -24,26 +24,6 @@ function deps(overrides: Partial = {}): ServeInitReaperDeps } describe('reexecServeUnderInitReaper', () => { - it('replaces a PID 1 Linux serve with bash running the same command and environment', () => { - const d = deps() - expect(reexecServeUnderInitReaper(d)).toBe('reexec') - expect(d.execve).toHaveBeenCalledWith( - '/bin/bash', - [ - 'bash', - '-c', - SERVE_INIT_REAPER_SCRIPT, - 'orca-serve-init', - '/opt/orca/orca-ide', - '/opt/orca/resources/cli/index.js', - 'serve', - '--port', - '6768' - ], - { ELECTRON_RUN_AS_NODE: '1' } - ) - }) - it('keeps the eval script of a `-e -- serve` launch', () => { const d = deps({ execArgv: ['-e', 'require(cli)'], argv: ['/opt/orca/orca-ide', 'serve'] }) expect(reexecServeUnderInitReaper(d)).toBe('reexec') @@ -87,29 +67,6 @@ describe('reexecServeUnderInitReaper', () => { expect(reexecServeUnderInitReaper(d)).toBe('unavailable') expect(d.execve).not.toHaveBeenCalled() }) - - it('leaves a serve that is not PID 1, or not on Linux, alone', () => { - for (const d of [deps({ pid: 4242 }), deps({ platform: 'darwin' })]) { - expect(reexecServeUnderInitReaper(d)).toBe('not-pid1') - expect(d.execve).not.toHaveBeenCalled() - expect(d.warn).not.toHaveBeenCalled() - } - }) - - it('warns and keeps serving when no bash is installed or the process cannot be replaced', () => { - for (const d of [ - deps({ exists: () => false }), - deps({ execve: null }), - deps({ - execve: () => { - throw new Error('ENOENT') - } - }) - ]) { - expect(reexecServeUnderInitReaper(d)).toBe('unavailable') - expect(d.warn).toHaveBeenCalledWith(expect.stringContaining('docker run --init')) - } - }) }) const bash = ['/bin/bash', '/usr/bin/bash'].find((path) => existsSync(path)) diff --git a/src/cli/runtime/serve-signal-exit-diagnostic.test.ts b/src/cli/runtime/serve-signal-exit-diagnostic.test.ts index cbb0bb24eb8..6868d97c0da 100644 --- a/src/cli/runtime/serve-signal-exit-diagnostic.test.ts +++ b/src/cli/runtime/serve-signal-exit-diagnostic.test.ts @@ -1,15 +1,13 @@ +import { MacServeStartupAbortError, serveSignalExitError } from './serve-signal-exit-diagnostic' import { EventEmitter } from 'node:events' import { mkdtemp, rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it, vi } from 'vitest' -import { MacServeStartupAbortError, serveSignalExitError } from './serve-signal-exit-diagnostic' import { - SERVE_CHILD_FORCE_KILL_GRACE_MS, SERVE_CHILD_FORCE_KILL_SCHEDULING_MARGIN_MS, superviseForegroundServe } from './serve-update-supervisor' -import { RuntimeClientError } from './types' import { QUIT_RENDERER_ACK_TIMEOUT_MS, WILL_QUIT_TEARDOWN_DEADLINE_MS @@ -26,13 +24,6 @@ function setPlatform(platform: NodeJS.Platform): void { Object.defineProperty(process, 'platform', { configurable: true, value: platform }) } -function superviseUntilExit(code: number | null, signal: NodeJS.Signals | null): Promise { - const child = new FakeChildProcess() - const supervised = superviseChild(child) - child.emit('exit', code, signal) - return supervised -} - function superviseChild(child: FakeChildProcess): Promise { return superviseForegroundServe({ spec: { program: '/Applications/Orca.app/Contents/MacOS/Orca', args: ['--serve'] }, @@ -50,43 +41,9 @@ afterEach(() => { }) describe('serveSignalExitError', () => { - it('explains the Launch Services startup abort on darwin SIGABRT', () => { + it('classifies darwin SIGABRT as a startup abort', () => { const error = serveSignalExitError('SIGABRT', 'darwin') - expect(error).toBeInstanceOf(MacServeStartupAbortError) - expect(error).toBeInstanceOf(RuntimeClientError) - expect(error.code).toBe('runtime_serve_failed') - expect(error.message).toContain('aborted with SIGABRT on macOS') - expect(error.message).toContain('_RegisterApplication') - expect(error.data).toMatchObject({ - nextSteps: [ - expect.stringContaining('desktop Orca is already running'), - expect.stringContaining('refuse for 30 minutes'), - expect.stringContaining('~/Library/Logs/DiagnosticReports/Orca-*.ips') - ] - }) - }) - - it('does not claim the macOS cause off darwin', () => { - for (const platform of ['linux', 'win32'] as const) { - const error = serveSignalExitError('SIGABRT', platform) - - expect(error.message).toBe('Orca serve exited via SIGABRT.') - expect(error.data).toBeUndefined() - } - }) - - it('does not claim the macOS cause for other darwin signals', () => { - const error = serveSignalExitError('SIGKILL', 'darwin') - - expect(error.message).toBe('Orca serve exited via SIGKILL.') - expect(error.data).toBeUndefined() - }) - - it('stays clear when neither a code nor a signal is reported', () => { - expect(serveSignalExitError(null, 'darwin').message).toBe( - 'Orca serve exited without reporting an exit code or signal.' - ) }) }) @@ -97,13 +54,6 @@ describe('superviseForegroundServe signal exits', () => { const child = new FakeChildProcess() const supervised = superviseChild(child) - expect(SERVE_CHILD_FORCE_KILL_GRACE_MS).toBe( - QUIT_RENDERER_ACK_TIMEOUT_MS + - WILL_QUIT_TEARDOWN_DEADLINE_MS + - SERVE_CHILD_FORCE_KILL_SCHEDULING_MARGIN_MS - ) - expect(SERVE_CHILD_FORCE_KILL_GRACE_MS).toBeLessThanOrEqual(35_000) - process.emit('SIGTERM', 'SIGTERM') expect(child.kill).toHaveBeenCalledOnce() expect(child.kill).toHaveBeenLastCalledWith('SIGTERM') @@ -127,11 +77,9 @@ describe('superviseForegroundServe signal exits', () => { process.emit('SIGINT', 'SIGINT') expect(child.kill).not.toHaveBeenCalled() - expect(vi.getTimerCount()).toBe(1) child.emit('exit', 0, null) await expect(supervised).resolves.toBe(0) - expect(vi.getTimerCount()).toBe(0) }) it('forwards Linux terminal hangup and removes the listener after exit', async () => { @@ -193,29 +141,5 @@ describe('superviseForegroundServe signal exits', () => { await expect(supervised).resolves.toBe(1) expect(child.kill).not.toHaveBeenCalled() - expect(vi.getTimerCount()).toBe(0) - }) - - it('throws the macOS diagnostic when the child aborts on darwin', async () => { - setPlatform('darwin') - - await expect(superviseUntilExit(null, 'SIGABRT')).rejects.toThrow( - /aborted with SIGABRT on macOS/ - ) - }) - - it('reports the plain signal on linux', async () => { - setPlatform('linux') - - await expect(superviseUntilExit(null, 'SIGABRT')).rejects.toThrow( - 'Orca serve exited via SIGABRT.' - ) - }) - - it('returns numeric exit codes unchanged', async () => { - setPlatform('darwin') - - await expect(superviseUntilExit(0, null)).resolves.toBe(0) - await expect(superviseUntilExit(7, null)).resolves.toBe(7) }) }) diff --git a/src/cli/runtime/serve-supervised-service.test.ts b/src/cli/runtime/serve-supervised-service.test.ts index 195cb534bfe..f3b97dff825 100644 --- a/src/cli/runtime/serve-supervised-service.test.ts +++ b/src/cli/runtime/serve-supervised-service.test.ts @@ -3,14 +3,11 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { superviseServeService, type SupervisedServeOptions } from './serve-supervised-service' -import { readServeSupervisorBrake, SERVE_SUPERVISOR_FAILURE_LIMIT } from './serve-supervisor-brake' +import { readServeSupervisorBrake } from './serve-supervisor-brake' /** Virtual clock: timers fire in order as soon as the loop awaits them. */ function harness(overrides: Partial & { userDataPath: string }) { let clock = 1_000_000 - const sleeps: number[] = [] - const logs: string[] = [] - let stopListener: (() => void) | null = null const options: SupervisedServeOptions = { executable: '/opt/orca/orca', force: false, @@ -20,28 +17,16 @@ function harness(overrides: Partial & { userDataPath: st now: () => clock, random: () => 0.5, setTimer: (callback, ms) => { - sleeps.push(ms) clock += ms queueMicrotask(callback) return () => undefined }, - onStopSignal: (listener) => { - stopListener = listener - return () => { - stopListener = null - } - }, - log: (line) => logs.push(line), + onStopSignal: () => () => undefined, + log: () => undefined, ...overrides } return { - options, - sleeps, - logs, - stop: () => stopListener?.(), - advance: (ms: number) => { - clock += ms - } + options } } @@ -56,18 +41,9 @@ describe('superviseServeService', () => { await rm(userDataPath, { recursive: true, force: true }) }) - it('restarts a dying child with growing delays, then gives up and exits 0 durably', async () => { - let runs = 0 - const h = harness({ - userDataPath, - runOnce: async () => { - runs++ - return 1 - } - }) + it('persists give-up after repeated child failure and refuses another launch', async () => { + const h = harness({ userDataPath }) await expect(superviseServeService(h.options)).resolves.toBe(0) - expect(runs).toBe(SERVE_SUPERVISOR_FAILURE_LIMIT) - expect(h.sleeps).toEqual([1_000, 2_000, 4_000, 8_000, 16_000]) const record = readServeSupervisorBrake(userDataPath) expect(record?.gaveUp?.reason).toContain('exited with code 1') @@ -81,8 +57,5 @@ describe('superviseServeService', () => { }) await expect(superviseServeService(again.options)).resolves.toBe(0) expect(rerun).toBe(0) - expect(again.logs.join('\n')).toContain('--force') }) - - // #32177: a lock this machine cannot judge (a shared home) is a peer, not a fault to give up on. }) diff --git a/src/cli/runtime/serve-supervisor-relocation.test.ts b/src/cli/runtime/serve-supervisor-relocation.test.ts index c1d39a1ebfd..802db8ff75b 100644 --- a/src/cli/runtime/serve-supervisor-relocation.test.ts +++ b/src/cli/runtime/serve-supervisor-relocation.test.ts @@ -92,17 +92,6 @@ describe('relocateServeSupervisor', () => { expect(relocated.execve).not.toHaveBeenCalled() }) - it('stays in place outside a packaged app', async () => { - for (const d of [ - deps({ env: {} }), - deps({ env: { ELECTRON_RUN_AS_NODE: '1', ORCA_APP_EXECUTABLE: '/dev/Orca' } }), - deps({ appRoot: join(root, 'checkout') }) - ]) { - await expect(relocateServeSupervisor(d)).resolves.toBe('in-place') - expect(d.execve).not.toHaveBeenCalled() - } - }) - // Re-exec closes the AppImage keepalive pipe and unmounts libraries needed by the new image. it('stays in place when running from an AppImage, whose files cannot change under it', async () => { const d = deps({ @@ -235,14 +224,6 @@ describe('relocateServeSupervisor', () => { } const appCli = () => join(appRoot, 'out', 'cli', 'index.js') - it('re-runs plain serve with the same arguments on the replaced executable', () => { - const argv = ['/ignored', appCli(), 'serve', '--json'] - const { execve, execPath } = restart({ ELECTRON_RUN_AS_NODE: '1' }, argv) - expect(execve).toHaveBeenCalledWith(execPath, [execPath, ...argv.slice(1)], { - ELECTRON_RUN_AS_NODE: '1' - }) - }) - it('does not restart when the executable is the one it started from, as after a restart', () => { const argv = ['/ignored', appCli(), 'serve'] const installed = statSync(deps().execPath) @@ -265,14 +246,6 @@ describe('relocateServeSupervisor', () => { expect(log).toHaveBeenCalledWith(expect.stringContaining('not ready to run')) }) - it('logs instead of throwing on a runtime that cannot replace its own process', () => { - const execve = vi.fn(() => { - throw new Error('this runtime cannot replace its own process') - }) - expect(() => restart({}, ['/ignored', appCli(), 'serve'], { execve })).not.toThrow() - expect(log).toHaveBeenCalledWith(expect.stringContaining('cannot replace')) - }) - it('re-runs a relocated supervisor from the app, which copies the new release aside', async () => { const first = deps() await relocateServeSupervisor(first) diff --git a/src/cli/runtime/status.test.ts b/src/cli/runtime/status.test.ts index 86b9943e981..425a26d441b 100644 --- a/src/cli/runtime/status.test.ts +++ b/src/cli/runtime/status.test.ts @@ -4,9 +4,7 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it, vi } from 'vitest' import { getRuntimeMetadataPath } from '../../shared/runtime-bootstrap' -import type { RuntimeStatus } from '../../shared/runtime-types' import { RuntimeClient } from './client' -import { projectRemoteAppStatus } from './status' const servers = new Set>() const sockets = new Set() @@ -87,33 +85,30 @@ describe.skipIf(process.platform === 'win32')('CLI runtime status', () => { // A persisted host PID cannot establish liveness in the caller's namespace. describe.skipIf(process.platform === 'win32')('CLI status connection authority', () => { - it.each(['EPERM', 'ESRCH'])( - 'reports an unavailable endpoint without using a PID probe (%s)', - async (code) => { - const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-status-probe-')) - writeFileSync( - getRuntimeMetadataPath(userDataPath), - JSON.stringify({ - runtimeId: 'runtime-unreachable', - pid: 424242, - transport: { kind: 'unix', endpoint: join(userDataPath, 'absent.sock') }, - authToken: 'token', - startedAt: 1 - }) - ) - const killSpy = vi.spyOn(process, 'kill').mockImplementation(() => { - throw Object.assign(new Error(`kill ${code}`), { code }) + it('reports an unavailable endpoint without using a PID probe', async () => { + const userDataPath = mkdtempSync(join(tmpdir(), 'orca-runtime-status-probe-')) + writeFileSync( + getRuntimeMetadataPath(userDataPath), + JSON.stringify({ + runtimeId: 'runtime-unreachable', + pid: 424242, + transport: { kind: 'unix', endpoint: join(userDataPath, 'absent.sock') }, + authToken: 'token', + startedAt: 1 }) - try { - await expect(new RuntimeClient(userDataPath).getCliStatus()).rejects.toMatchObject({ - code: 'runtime_unavailable' - }) - expect(killSpy).not.toHaveBeenCalled() - } finally { - killSpy.mockRestore() - } + ) + const killSpy = vi.spyOn(process, 'kill').mockImplementation(() => { + throw new Error('unexpected PID probe') + }) + try { + await expect(new RuntimeClient(userDataPath).getCliStatus()).rejects.toMatchObject({ + code: 'runtime_unavailable' + }) + expect(killSpy).not.toHaveBeenCalled() + } finally { + killSpy.mockRestore() } - ) + }) it('reports a status timeout from a live listener without declaring its hidden PID dead', async () => { const userDataPath = mkdtempSync(join(tmpdir(), 'orca-status-timeout-')) @@ -148,41 +143,3 @@ describe.skipIf(process.platform === 'win32')('CLI status connection authority', } }) }) - -describe('projectRemoteAppStatus', () => { - function remoteStatus(overrides: Partial = {}): RuntimeStatus { - return { - runtimeId: 'remote-runtime', - graphStatus: 'ready', - authoritativeWindowId: null, - ...overrides - } as RuntimeStatus - } - - it('reports the target app as running when a renderer owns the graph there', () => { - expect(projectRemoteAppStatus(remoteStatus({ desktopWindowStatus: 'available' }))).toEqual({ - running: true, - pid: null, - desktopWindowStatus: 'available' - }) - }) - - it.each(['openable', 'initializing', 'blocked'] as const)( - 'does not claim a running desktop for window status %s', - (desktopWindowStatus) => { - expect(projectRemoteAppStatus(remoteStatus({ desktopWindowStatus })).running).toBe(false) - } - ) - - it('reports not running when the target has no desktop window status', () => { - expect(projectRemoteAppStatus(remoteStatus())).toEqual({ running: false, pid: null }) - }) - - it('honors the authoritativeWindowId fallback for older runtimes', () => { - expect(projectRemoteAppStatus(remoteStatus({ authoritativeWindowId: 3 }))).toEqual({ - running: true, - pid: null, - desktopWindowStatus: 'available' - }) - }) -}) From a5854aec6e0330f6dd7b69f075e0e6f879e2037b Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 05:43:27 -0800 Subject: [PATCH 091/178] test: remove low-value transport and shared suites (#42026) * test: remove low-value automation and shared suites * test: retire stale gate references and stabilize retained fixture date --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 2 - config/focus-setting-read-baseline.txt | 1 - config/reliability-gates.jsonc | 10 +- src/cli/runtime/transport-framing.test.ts | 70 +-- src/cli/runtime/websocket-transport.test.ts | 263 ------------ .../AutomationsPage.behavior.test.tsx | 4 - ...AutomationsPage.escape-precedence.spec.tsx | 72 ---- .../AutomationsPage.external-scope.spec.tsx | 69 --- ...AutomationsPage.refresh-selection.spec.tsx | 133 ------ .../automations-page-actions-test-harness.ts | 159 ------- ...tomations-page-child-props.test-fixture.ts | 65 --- .../automations-page-destination-fixtures.ts | 69 --- .../automations/automations-page-fixtures.ts | 198 --------- .../automations-page-listed-items.ts | 27 -- .../automations-page-runtime-fixtures.ts | 66 --- .../automations-page-test-harness.tsx | 399 ------------------ .../grok-inline-startup-pty-trace.ts | 124 ------ .../__fixtures__/grok-startup-pty-trace.ts | 163 ------- src/shared/bundled-skill-summary.test.ts | 45 -- src/shared/claimed-agent-pty-owner.test.ts | 130 ++---- src/shared/claude-model-list-probe.test.ts | 28 +- src/shared/codex-subagent-transcript.test.ts | 149 +------ .../command-code-output-retention.test.ts | 26 -- src/shared/command-code-output-status.test.ts | 75 +--- src/shared/crash-reporting.test.ts | 79 ---- .../darwin-process-table-capture.test.ts | 45 +- ...rwin-process-table-inventory-drain.test.ts | 47 +-- .../darwin-terminal-names-real-ps.test.ts | 17 - src/shared/darwin-terminal-names.test.ts | 114 ++--- ...te-ready-scanner-grok-trace-replay.test.ts | 58 --- .../dsb-native-one-shot-recognition.test.ts | 26 +- src/shared/dsh-real-cli.test.ts | 53 --- 32 files changed, 78 insertions(+), 2708 deletions(-) delete mode 100644 src/cli/runtime/websocket-transport.test.ts delete mode 100644 src/renderer/src/components/automations/AutomationsPage.behavior.test.tsx delete mode 100644 src/renderer/src/components/automations/AutomationsPage.escape-precedence.spec.tsx delete mode 100644 src/renderer/src/components/automations/AutomationsPage.external-scope.spec.tsx delete mode 100644 src/renderer/src/components/automations/AutomationsPage.refresh-selection.spec.tsx delete mode 100644 src/renderer/src/components/automations/automations-page-actions-test-harness.ts delete mode 100644 src/renderer/src/components/automations/automations-page-child-props.test-fixture.ts delete mode 100644 src/renderer/src/components/automations/automations-page-destination-fixtures.ts delete mode 100644 src/renderer/src/components/automations/automations-page-listed-items.ts delete mode 100644 src/renderer/src/components/automations/automations-page-runtime-fixtures.ts delete mode 100644 src/renderer/src/components/automations/automations-page-test-harness.tsx delete mode 100644 src/shared/__fixtures__/grok-inline-startup-pty-trace.ts delete mode 100644 src/shared/__fixtures__/grok-startup-pty-trace.ts delete mode 100644 src/shared/bundled-skill-summary.test.ts delete mode 100644 src/shared/command-code-output-retention.test.ts delete mode 100644 src/shared/draft-paste-ready-scanner-grok-trace-replay.test.ts delete mode 100644 src/shared/dsh-real-cli.test.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index 85c8cc9f0c6..7f8164b1eb4 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -214,7 +214,6 @@ 1 test-no-real-time-wait:src/renderer/src/lib/agent-session/resume/cli-fork/native-chat-cli-fork-preparation.test.ts 2 test-no-real-time-wait:src/renderer/src/store/slices/workspace-cleanup/workspace-cleanup-removal-preflight.test.ts 2 test-no-real-time-wait:src/shared/__tests__/process-host/windows-tree-kill-late-taskkill.win32.test.ts -1 test-no-real-time-wait:src/shared/darwin-process-table-capture.test.ts 1 test-no-real-time-wait:src/shared/darwin-terminal-names-real-ps.test.ts 1 test-no-real-time-wait:src/shared/darwin-terminal-names.test.ts 1 test-no-real-time-wait:src/shared/git/status/git-branch-line-total-soft-deadline.test.ts @@ -628,7 +627,6 @@ 1 test-timeout-needs-reason:src/renderer/src/hooks/agent-hook-completion-notifications.test.ts 1 test-timeout-needs-reason:src/renderer/src/lib/source-control/checks/check-run-details-log-normalization-performance.test.ts 1 test-timeout-needs-reason:src/shared/__tests__/process-host/windows-tree-kill-late-taskkill.win32.test.ts -1 test-timeout-needs-reason:src/shared/dsh-real-cli.test.ts 1 test-timeout-needs-reason:src/shared/wsl-login-shell-command.test.ts 1 test-timeout-needs-reason:tests/e2e/active-view-restart-restore.spec.ts 1 test-timeout-needs-reason:tests/e2e/agent-send-picker-reveal.spec.ts diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index 491e47fa748..bef6f0b90de 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -20,7 +20,6 @@ 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/authoring/use-automations-page-destination-form.ts -1 src/renderer/src/components/automations/automations-page-fixtures.ts 1 src/renderer/src/components/automations/hosts/automation-host-client.ts 1 src/renderer/src/components/automations/use-automations-page-controller.ts 1 src/renderer/src/components/browser-pane/ReopenBrowserPageOnServerButton.tsx diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index c361edcc424..e976b64d092 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -3614,11 +3614,11 @@ "https://github.com/stablyai/orca/issues/9563" ], "invariant": "A safely promotable headless serve process is the single app owner. Desktop activation preserves its daemon-backed sessions. On macOS, a CLI-supervised serve update keeps the node-mode parent alive across ShipIt's atomic bundle swap, restarts with the original serve arguments only after the target bundle is present, and clears handoff state only after that target version reports runtime readiness. Once a supervised child exits or fails to start, no late handoff completion may signal it or arm force-kill escalation. Unsupported or failed handoffs leave the current serving owner intact or recover it once without an install retry loop.", - "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. The whole main-window attach-services suite is removed: runtime/window attach, notifier wiring, reload/teardown and permission-handler mock orchestration alarms are intentionally lost. Prune its exact deleted command selectors, testFiles entries and notifier assertionRef where present; complete prior selectors/ref are archived. Unrelated dated receipts remain exact and retain their original scope; no fresh reduced-selector, native-window or remote execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2252: The removed synthetic lock/gate/hydration fixture supplies no current promotion continuity oracle. Existing other selectors retain only their own independent scope; changed selector execution is pending. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2020: Retire CLI supervisor outcome, local-open promotion and paired-open projection alarms; launch retains only the two referenced stream-framing cases. Remaining CLI refusal/timeout cases do not replace headless-promotion coverage. Preserve both independent dated receipts at their original commands. Reduced selectors have no fresh result or equivalent replacement claim. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Unit tests coalesce early activation, preserve daemon and SSH identity, and reproduce the update race with a staged target, old serving child, persistent CLI parent, atomic .app replacement, and replacement readiness message. They assert the parent does not exit for launchd to respawn the old app, the native updater does not launch an interactive GUI, the replacement version is verified before handoff completion, mismatches become durable failures without retries, and unsupported/preflight-failed installs do not invoke native quit or PTY cleanup. Force handoff completion to fail after the replacement child exits and require zero later child signals and zero escalation timers. A joined lock-owner/activation/hydration contract asserts that a forced relaunch opens exactly one window and that the renderer promoted inside the serve process launches zero agent resumes, creates no replacement tab or startup command, and leaves every surviving session record untouched. The Electron journey independently verifies headless promotion retains owner/runtime/daemon/PTY identity and terminal I/O.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3417 composes root-adopted3415v2/independent3416v2: intentionally removes117authored/173potential expanded registrations from12semantic sources, deleting4whole conventional suites and3registration modules and retaining4authored/5expanded cases, without equivalent replacement or fresh execution. The whole main-window attach-services suite is removed: runtime/window attach, notifier wiring, reload/teardown and permission-handler mock orchestration alarms are intentionally lost. Prune its exact deleted command selectors, testFiles entries and notifier assertionRef where present; complete prior selectors/ref are archived. Unrelated dated receipts remain exact and retain their original scope; no fresh reduced-selector, native-window or remote execution is claimed. Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2252: The removed synthetic lock/gate/hydration fixture supplies no current promotion continuity oracle. Existing other selectors retain only their own independent scope; changed selector execution is pending. Historical prior account (preserved verbatim): Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit2020: Retire CLI supervisor outcome, local-open promotion and paired-open projection alarms; launch retains only the two referenced stream-framing cases. Remaining CLI refusal/timeout cases do not replace headless-promotion coverage. Preserve both independent dated receipts at their original commands. Reduced selectors have no fresh result or equivalent replacement claim. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Unit tests coalesce early activation, preserve daemon and SSH identity, and reproduce the update race with a staged target, old serving child, persistent CLI parent, atomic .app replacement, and replacement readiness message. They assert the parent does not exit for launchd to respawn the old app, the native updater does not launch an interactive GUI, the replacement version is verified before handoff completion, mismatches become durable failures without retries, and unsupported/preflight-failed installs do not invoke native quit or PTY cleanup. Force handoff completion to fail after the replacement child exits and require zero later child signals and zero escalation timers. A joined lock-owner/activation/hydration contract asserts that a forced relaunch opens exactly one window and that the renderer promoted inside the serve process launches zero agent resumes, creates no replacement tab or startup command, and leaves every surviving session record untouched. The Electron journey independently verifies headless promotion retains owner/runtime/daemon/PTY identity and terminal I/O. Audit4587 retires the one-case CLI WebSocket protocol/preflight suite and its hand-built server; its cross-layer non-delivery witness is intentionally lost, with no replacement coverage claimed.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/cli/runtime/launch.test.ts src/main/updater/serve-update-handoff.test.ts src/main/updater/updater.headless-serve-install.test.ts src/main/updater/mac-install/updater.mac-install.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/cli/runtime/serve-signal-exit-diagnostic.test.ts", - "pnpm exec vitest run --config config/vitest.config.ts src/main/startup/serve-launch/serve-desktop-activation.test.ts src/cli/runtime/client.test.ts src/cli/runtime/websocket-transport.test.ts src/main/runtime/construction/orca-runtime.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts src/main/startup/serve-launch/serve-desktop-activation.test.ts src/cli/runtime/client.test.ts src/main/runtime/construction/orca-runtime.test.ts", "pnpm exec electron-vite build --mode e2e", "pnpm run test:e2e -- tests/e2e/headless-serve-desktop-activation.spec.ts --workers=1" ], @@ -3629,7 +3629,6 @@ "src/cli/runtime/serve-signal-exit-diagnostic.test.ts", "src/main/startup/serve-launch/serve-desktop-activation.test.ts", "src/cli/runtime/client.test.ts", - "src/cli/runtime/websocket-transport.test.ts", "src/main/runtime/construction/orca-runtime.test.ts", "tests/e2e/headless-serve-desktop-activation.spec.ts" ], @@ -6394,9 +6393,9 @@ "https://github.com/stablyai/orca/issues/9229" ], "invariant": "Reconnect, replay, or lifecycle observations from a viewer that negotiated explicit close intent must never kill a live PTY. A capable reasonless close must keep and republish; a legacy paired viewer must retain current-main behavior because its intentional close and cleanup echo are wire-identical. Lifecycle close requires the exact observed publication, terminal, environment, and authoritative liveness, never signals a process, and leaves renderer-owned or partial-split retirement to its owner. Legacy daemon hello and warm reattachment remain non-destructive.", - "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3881 intentionally removes the entire legacy E2EE channel and mobile socket-wiring test suites and the v2 additive runtime-capability refusal callback, without equivalent replacement. Those deleted suites leave current selectors and that v2 assertion alias is retired. Retained v2 replay/queue-counter cases do not establish the removed capability refusal. Reduced test bytes/selectors have no fresh execution in this static packet; independent dated daemon E2E receipts retain their complete original scope. Historical prior account (preserved verbatim): Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3774 (independent peer expansion of3773): deliberately retire7authored/13potential expanded registrations across4whole suites: standalone truncated8-byte E2EE guard; open/empty readiness null fast-path; capability parser acceptance/atomic malformed-array bounds; phone/delegated-parent/desktop/in-process creator provenance projections. These are real guard/policy alarms accepted as low marginal value, not tautologies. No equivalent replacement, authenticated-refusal, observer delivery, persisted visibility, synchronous scheduling or fresh execution is claimed. Prior dated receipts retain their exact original scopes and do not validate the changed/reduced current selector. Independent daemon E2E receipts remain scoped to their own unchanged commands. Historical prior account (preserved verbatim): Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1983: Delete the reasonless-close scalar suite and optional-capability socket forwarding alias. Delete the mocked close-attribution topology field-inventory suite and its aliases/selectors; preserve independent dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1959 (sources 1957:11, 1957:35, 1957:36): The fixture-supplied repository roundtrip, isolated capability metadata readback, parser/default-timer/size-threshold/internal failure-counter examples and standalone handshake-only replay example are removed. Source11 retains real encrypted multi-client event ordering, reconnect snapshot and multiplex delivery under synthetic runtime state, and does not establish the broad handshake-to-reasonless-close refusal alias; retire that whole assertion object. Source36 capability metadata readback alias is retired. Source35 retains the actual v2 runtime-only metadata/downgrade refusal callback and captured mutating-trace fresh-nonce rejection; its mapped mobile-v2 refusal alias remains within modeled credential lookup scope. Both independent daemon E2E dated receipts remain byte-exact with their original commands and results. No historical passed receipt is narrowed or transferred to changed source bytes. No fresh execution, equivalent replacement, physical provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed by this metadata packet. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Retain actual parked exit/disposal/cancel/leaf progression using injected exit and close adapters. The retained last-watcher case checks exact lifecyclePtyId, but no retained callback establishes the whole existing replacement-or-sibling-incarnation alias. Retire that broad assertion object rather than transfer partial support. Capability admission, static fields and duplicate routing examples are intentionally lost; unchanged daemon E2E receipts remain scoped to their own commands. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Start isolated v25 and v26 daemon generations with one capable-viewer PTY and one legacy-viewer PTY per generation in four target worktrees plus an unaddressed control PTY in a fifth worktree. Route them through the production desktop scanner, runtime, RPC dispatcher, renderer-close relay, and daemon router. First issue sequential reasonless closes from an authenticated capable connection and require refusal, snapshot republish, zero shutdown calls, exact process survival, and post-close I/O. Then issue the byte-identical requests without the negotiated capability and require current-main behavior: two ordered immediate shutdowns, session-killed events, and exact root/descendant death, while capable and control PTYs survive. An observer lists all targets before, between, and after while issuing zero closes. Unit contracts also require in-process reasonless refusal, legacy runtime/mobile compatibility, explicit user closes, encrypted client-auth advertisement, and old-server lifecycle calls never to fall back.", + "oracle": "Audit4362 intentionally deletes the imported stale-waiter retry and manually assembled OSC9999 mobile-republish cases, plus the mocked missing-worktree cleanup suite (4 authored / 5 finite cases in three suites). Lost alarms include repeated unread worker_done pointers, working-to-waiting mobile status publication, missing-worktree filtering, and scoped daemon shutdown with unrelated duplicate identities. No replacement coverage or new platform proof is claimed. Audit3881 intentionally removes the entire legacy E2EE channel and mobile socket-wiring test suites and the v2 additive runtime-capability refusal callback, without equivalent replacement. Those deleted suites leave current selectors and that v2 assertion alias is retired. Retained v2 replay/queue-counter cases do not establish the removed capability refusal. Reduced test bytes/selectors have no fresh execution in this static packet; independent dated daemon E2E receipts retain their complete original scope. Historical prior account (preserved verbatim): Audit3832 deliberately retires76 authored/79 finite cases:75 runtime compatibility cases/78 finite cases across14 fragments, plus one localization extractor default-flag case. Three whole runtime fragments are deleted;27 authored/30 finite cases remain across the reviewed sources. This gate retains only its own selected scope; no equivalent replacement or fresh runtime/native/remote evidence is claimed. Runtime component A: Audit3828 composes3822 with independent3824: deliberately retires49 authored/52 finite cases from seven compatibility fragments, leaving15 authored/18 finite cases and deleting two entire fragments. Accepted losses: positive folder-worker recovery/materialization and reveal deadline; persisted-key normalization and legacy split/group/focus projection; fixed stale CAS refusal; occupied/alias/restored/preallocated terminal-handle variants; fixed OpenCode/Cursor/Claude/Codex/Antigravity classification, readiness, title and management examples; direct private-map browser authority projections; browser-focus and OMP title projection; already-aborted claim cleanup. Additional3824 loss: src/main/runtime/orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec.ts :: does not poll a wrapper foreground for a speculative CLI prompt send :: Speculative retryForegroundWrappers:false check must return false on a wrapper after one foreground read, without polling the supplied later codex response. These real bounded alarms are deliberately retired for low marginal value. Retained callbacks are unchanged and provide only their own bounded scope, not equivalent replacement. Historical receipts do not validate reduced callbacks. No fresh runtime/native/SSH/WSL/phone/browser evidence or full transitive closure is claimed. Runtime component B: Audit3834 deliberately retires26 authored/26 finite cases from seven fresh compatibility fragments, leaving10/10 and deleting the one-case saved-binding reattachment fragment. Original position58 is excluded: its existing substantive3578 KEEP7 review remains historical, with no fresh credit. Accepted losses: fixed hook/provider-session identities and stale metadata refusal; decorative/pre-title foreground ordering variants; saved-binding surface reattachment; saved-row exit ghost removal and silent activation selection; unsupported headless wake; positive pending materialization/group projections; per-caller navigation selection; positive SSH hydration/read partition; laptop mobile publication/buffer and background/pending/status projections; abnormal/unregistered SSH grace, normal-exit refusal, persisted SSH hydration, positive serve reattachment, pending-agent launch arguments and direct seeded running-launch admission guard. These are real bounded alarms deliberately retired for low marginal value. Retained cases preserve their own distinct deferred foreground coalescing, usable handle continuity, stale inventory refresh, concurrent browser snapshot preservation, renderer graph fencing, caller dedup isolation, SSH partition writes, runtime split lifetime, independent relay-ready publication, lease admission/state/expiry. The direct running-launch no-spawn guard alarm is deliberately retired; no remaining owning-launch refusal coverage is claimed. Historical runs do not validate reduced callbacks. No equivalent replacement, runtime/native/SSH/WSL/phone/browser result or full transitive closure is claimed. Additional extractor loss: config/scripts/localization/source-extraction/audit-localization-native-dialogs.test.mjs :: does not change the existing renderer scan by default :: Default options must omit the five native-only fields in this supplied snippet. No equivalent replacement is claimed. Historical prior account (preserved verbatim): Audit3814 deliberately retires60 authored/60 finite cases:53runtime compatibility cases,4native XCTest methods and3Python clipboard methods, including the entire StableElementIndexes suite. Clipboard cuts retire the one-over-cap sample, incoming-offer destruction trace and sequential20-copy replay; actual wire, FD/pipe, startup and Xvfb/GTK boundaries remain without equivalent replacement claims. Accepted losses include narrow terminal-send payload/provider projections, legacy and direct no-op/error permutations, timing/work-count examples, private state/title/activity projections and fixture-driven readiness examples, individually enumerated below. These include real bounded production alarms consciously retired for marginal value; retained failed writes, duplicate submission, stale capture, transcript/ring and allocation fences are not equivalent replacements. No fresh native/remote/browser execution or complete transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3806 deliberately retires40 authored/40 finite runtime compatibility cases, including the terminal-side-effect-facts-part03 headless-snapshots-part02 and agent-status-and-waits-part03 fragments, plus20 scalar Swift precondition sites. Lost runtime alarms include status/title/attention projection examples, simple renderer/headless frame restoration and status waiting outcomes detailed in the accepted-loss ledger. Swift cuts retire duplicate generation-map/schema/navigation/bridge and notification-wrapper assertions; no standalone native program is deleted. These include real bounded production alarms consciously retired for marginal value. Retained races, pending writes/reflow, namespace and waiter ownership are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3798 deliberately retires21 authored/24 finite compatibility cases, including the entire SSH-worktree-lifecycle and repository-project-operations fragments. Accepted losses include navigation-only settings acknowledgement, browser session cleanup and host preservation projections, window positive/policy-state examples, terminal recovery/reload and no-incarnation remint examples, terminal listing compatibility/malformed IDs, directory browse/sort/Windows path smoke, SSH create refusal/manual lineage, recursive parent-folder creation and fork/origin/avatar/icon projections. Real bounded production alarms are consciously retired for marginal value; retained authority races, cleanup/clone ownership, pending waiters, nested-folder and scan invalidation cases are not equivalent replacements. No fresh native/remote/browser execution or full transitive closure is claimed. Earlier dated receipts and raw selectors remain historical accounts of prior callback sets. Audit3774 (independent peer expansion of3773): deliberately retire7authored/13potential expanded registrations across4whole suites: standalone truncated8-byte E2EE guard; open/empty readiness null fast-path; capability parser acceptance/atomic malformed-array bounds; phone/delegated-parent/desktop/in-process creator provenance projections. These are real guard/policy alarms accepted as low marginal value, not tautologies. No equivalent replacement, authenticated-refusal, observer delivery, persisted visibility, synchronous scheduling or fresh execution is claimed. Prior dated receipts retain their exact original scopes and do not validate the changed/reduced current selector. Independent daemon E2E receipts remain scoped to their own unchanged commands. Historical prior account (preserved verbatim): Audit3403 composes root-adopted3393/3394,3400/3401 and3398/3399: intentionally retires167 authored registrations/196 potential expanded cases across93 whole suites without equivalent replacement or fresh execution. Retained65 registrations/79 potential cases include one conditional missing-input failure sentinel; potential counts are not executions. Historical notes and complete dated receipts retain their exact original scopes. The retained runtime registration driver loses three complete imported fragments: changed-working-label title facts and suppressed-frame tracked snapshots; refusing SSH GitLab MR-base lookup before local Git/glab calls; and missing folder-workspace path creation/update refusal before spawn/store mutation. These alarms are intentionally lost. Other imported fragments and separately rooted dated receipts are unchanged; they are not equivalent replacement coverage. Audit2133 current-parent preparation: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2095 current-parent preparation2126: Current reduced sources/selectors intentionally retire the alarms listed in knownGaps; prior dated runs and replacement claims do not validate the changed set. Historical prior account (preserved verbatim): Audit2090 (sources2088/2089): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2086 (sources2082/2083): Current reduced suites intentionally retire the alarms listed in knownGaps; prior dated execution and replacement claims do not validate changed source bytes or selectors. Historical prior account (preserved verbatim): Audit2076 (sources2073/2074): Intentionally retire these low-value alarms without replacement: src/main/runtime/orca-runtime-tests/terminal-handles-part-02.spec.ts: recovers a moved SSH pane through the tab it now sits in, not the one its lease froze; reports a plain expired orphan; does not report a pane whose only recent lease was superseded; does not report a pane whose only recent lease had its relay id recycled; picks the eligible orphan over a superseded predecessor that matches first; collects the same tabs the per-tab reader reports, in one sweep of the leases; reports no candidate when no lease names any of the worktree tabs. src/main/runtime/orca-runtime-tests/runtime-availability.spec.ts: starts unavailable with no authoritative window; reports the configured Windows terminal shell on status; reports floating workspace availability from settings on status; polls floating tabs with targeted PTY liveness and no repo/provider inventory; hydrates persisted tabs when the store cannot report repos; advertises browser screencast only when a renderer window is available; routes mobile Codex reset consumption through the account mutation coordinator; maps a definite pre-provider rejection into an authoritative current snapshot. Existing unrelated evidenceRuns retain their original scope; unchanged dated receipt objects do not validate changed source bytes or reduced selectors. No fresh gate execution or historical import-closure equivalence is claimed. Frozen current literal-import graph reaches these changed test bytes; unresolved local and computed edges remain debt, and current reachability is not historical execution or equivalent replacement evidence. Historical prior account (preserved verbatim): Audit1983: Delete the reasonless-close scalar suite and optional-capability socket forwarding alias. Delete the mocked close-attribution topology field-inventory suite and its aliases/selectors; preserve independent dated receipts. Changed-source prior results do not validate reduced-source bytes. No fresh execution, equivalent replacement, red/green, measured budget, soak, provider/PTY, rendered, live remote/mobile or cross-platform result is claimed. Historical prior account (preserved verbatim): Audit1959 (sources 1957:11, 1957:35, 1957:36): The fixture-supplied repository roundtrip, isolated capability metadata readback, parser/default-timer/size-threshold/internal failure-counter examples and standalone handshake-only replay example are removed. Source11 retains real encrypted multi-client event ordering, reconnect snapshot and multiplex delivery under synthetic runtime state, and does not establish the broad handshake-to-reasonless-close refusal alias; retire that whole assertion object. Source36 capability metadata readback alias is retired. Source35 retains the actual v2 runtime-only metadata/downgrade refusal callback and captured mutating-trace fresh-nonce rejection; its mapped mobile-v2 refusal alias remains within modeled credential lookup scope. Both independent daemon E2E dated receipts remain byte-exact with their original commands and results. No historical passed receipt is narrowed or transferred to changed source bytes. No fresh execution, equivalent replacement, physical provider/PTY, live remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed by this metadata packet. Historical prior account (preserved verbatim): Audit1939 (sources1934/1935): Retain actual parked exit/disposal/cancel/leaf progression using injected exit and close adapters. The retained last-watcher case checks exact lifecyclePtyId, but no retained callback establishes the whole existing replacement-or-sibling-incarnation alias. Retire that broad assertion object rather than transfer partial support. Capability admission, static fields and duplicate routing examples are intentionally lost; unchanged daemon E2E receipts remain scoped to their own commands. Preserve meaningful remaining selectors/assertions and unchanged dated receipts within their original scope. Reduced selectors have no fresh gate execution evidence in this metadata packet. No equivalent replacement, physical watcher/PTY/provider, live SSH/remote/mobile, rendered, cross-platform, soak or measured-budget result is claimed. Historical prior account (preserved verbatim): Existing exact unchanged-selector receipts remain the only current execution evidence. Changed source selectors have no fresh execution result. Historical prior-source oracle account: Start isolated v25 and v26 daemon generations with one capable-viewer PTY and one legacy-viewer PTY per generation in four target worktrees plus an unaddressed control PTY in a fifth worktree. Route them through the production desktop scanner, runtime, RPC dispatcher, renderer-close relay, and daemon router. First issue sequential reasonless closes from an authenticated capable connection and require refusal, snapshot republish, zero shutdown calls, exact process survival, and post-close I/O. Then issue the byte-identical requests without the negotiated capability and require current-main behavior: two ordered immediate shutdowns, session-killed events, and exact root/descendant death, while capable and control PTYs survive. An observer lists all targets before, between, and after while issuing zero closes. Unit contracts also require in-process reasonless refusal, legacy runtime/mobile compatibility, explicit user closes, encrypted client-auth advertisement, and old-server lifecycle calls never to fall back. Audit4587 retires the one-case CLI WebSocket protocol/preflight suite and its hand-built server; its cross-layer non-delivery witness is intentionally lost, with no replacement coverage claimed.", "commands": [ - "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts src/shared/remote-runtime/remote-runtime-client.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/cli/runtime/websocket-transport.test.ts src/renderer/src/web/web-runtime-client.test.ts src/renderer/src/components/terminal/terminal-close-incarnation.test.ts src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/construction/orca-runtime.test.ts src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts src/shared/remote-runtime/remote-runtime-client.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/renderer/src/web/web-runtime-client.test.ts src/renderer/src/components/terminal/terminal-close-incarnation.test.ts src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts", "pnpm exec playwright test tests/e2e/daemon-generation-reconnect-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1", "pnpm exec playwright test tests/e2e/daemon-generation-legacy-close-safety.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1" ], @@ -6405,7 +6404,6 @@ "src/main/runtime/rpc/mobile/e2ee/e2ee-channel-v2.test.ts", "src/shared/remote-runtime/remote-runtime-client.test.ts", "src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts", - "src/cli/runtime/websocket-transport.test.ts", "src/renderer/src/web/web-runtime-client.test.ts", "src/renderer/src/components/terminal/terminal-close-incarnation.test.ts", "src/renderer/src/components/terminal-pane/parking/output-watchers/terminal-parked-tab-watchers.test.ts", diff --git a/src/cli/runtime/transport-framing.test.ts b/src/cli/runtime/transport-framing.test.ts index 2c7bfb5ecf0..66fbcbfa177 100644 --- a/src/cli/runtime/transport-framing.test.ts +++ b/src/cli/runtime/transport-framing.test.ts @@ -18,10 +18,10 @@ const reply = (result: unknown) => `${JSON.stringify({ id: 'request-1', ok: true, result, _meta: { runtimeId: 'runtime-1' } })}\n` class TestSocket extends EventEmitter { - setEncoding = vi.fn() - write = vi.fn() - end = vi.fn() - destroy = vi.fn() + setEncoding = (): void => {} + write = (): void => {} + end = (): void => {} + destroy = (): void => {} } let socket: TestSocket @@ -30,50 +30,16 @@ beforeEach(() => { createConnection.mockReturnValue(socket) }) afterEach(() => { - vi.restoreAllMocks() vi.useRealTimers() }) describe('CLI runtime response framing', () => { - it.each([1, 7, 256, 4096])( - 'reads a fragmented response with %i-character chunks', - async (size) => { - const result = { data: '界😀'.repeat(10000) } - const encoded = reply(result) - const pending = sendRequest(metadata, 'terminal.read', {}, 30000) - for (let offset = 0; offset < encoded.length; offset += size) { - socket.emit('data', encoded.slice(offset, offset + size)) - } - await expect(pending).resolves.toMatchObject({ result }) - expect(socket.setEncoding).toHaveBeenCalledExactlyOnceWith('utf8') - expect(socket.end).toHaveBeenCalledOnce() - } - ) - - it('searches each fragment once without rescanning the accumulated reply', async () => { - const encoded = reply({ data: 'x'.repeat(1024 * 1024) }) + it('reads a response split across chunks without losing its payload', async () => { + const encoded = reply({ data: '界😀done' }) const pending = sendRequest(metadata, 'terminal.read', {}, 30000) - const originalIndexOf = String.prototype.indexOf - let searchedCharacters = 0 - const search = vi.spyOn(String.prototype, 'indexOf').mockImplementation(function ( - this: string, - value, - position - ) { - if (value === '\n') { - searchedCharacters += this.length - (position ?? 0) - } - return originalIndexOf.call(this, value, position) - }) - try { - for (let offset = 0; offset < encoded.length; offset += 256) { - socket.emit('data', encoded.slice(offset, offset + 256)) - } - } finally { - search.mockRestore() - } - await expect(pending).resolves.toMatchObject({ ok: true }) - expect(searchedCharacters).toBe(encoded.length) + socket.emit('data', encoded.slice(0, 7)) + socket.emit('data', encoded.slice(7)) + await expect(pending).resolves.toMatchObject({ result: { data: '界😀done' } }) }) it('refreshes keepalives across chunks and ignores blanks and data after the final frame', async () => { @@ -86,13 +52,10 @@ describe('CLI runtime response framing', () => { socket.emit('data', `${reply({ data: 'done' })}invalid JSON\n`) socket.emit('data', 'more ignored data') await expect(pending).resolves.toMatchObject({ result: { data: 'done' } }) - expect(socket.end).toHaveBeenCalledOnce() - expect(vi.getTimerCount()).toBe(0) }) it.each([ ['broken JSON\n', 'invalid_runtime_response'], - ['{}\n', 'invalid_runtime_response'], ['{"id":"other","ok":true,"result":{}}\n', 'invalid_runtime_response'], [ '{"id":"request-1","ok":true,"result":{},"_meta":{"runtimeId":"other"}}\n', @@ -105,29 +68,18 @@ describe('CLI runtime response framing', () => { socket.emit('data', line.slice(0, 2)) socket.emit('data', line.slice(2) + reply({ data: 'ignored' })) await expect(pending).rejects.toMatchObject({ code }) - expect(socket.end).toHaveBeenCalledOnce() } ) - it('preserves terminal failure envelopes', async () => { - const pending = sendRequest(metadata, 'terminal.read', {}, 30000) - socket.emit('data', '{"id":"request-1","ok":false,"error":{"code":"bad","message":"no"}}\n') - await expect(pending).resolves.toMatchObject({ - ok: false, - error: { code: 'bad', message: 'no' } - }) - }) - it('rejects close with an incomplete frame and does not parse later data', async () => { const pending = sendRequest(metadata, 'terminal.read', {}, 30000) socket.emit('data', '{"id":') socket.emit('close') socket.emit('data', reply({ data: 'ignored' })) await expect(pending).rejects.toMatchObject({ code: 'runtime_unavailable' }) - expect(socket.end).toHaveBeenCalledOnce() }) - it('destroys a timed out socket holding an incomplete frame', async () => { + it('rejects a timed out incomplete frame and ignores a late reply', async () => { vi.useFakeTimers() const pending = sendRequest(metadata, 'terminal.read', {}, 100) const rejected = expect(pending).rejects.toMatchObject({ code: 'runtime_timeout' }) @@ -135,7 +87,5 @@ describe('CLI runtime response framing', () => { await vi.advanceTimersByTimeAsync(100) socket.emit('data', reply({ data: 'ignored' })) await rejected - expect(socket.destroy).toHaveBeenCalledOnce() - expect(vi.getTimerCount()).toBe(0) }) }) diff --git a/src/cli/runtime/websocket-transport.test.ts b/src/cli/runtime/websocket-transport.test.ts deleted file mode 100644 index a0cfb912efa..00000000000 --- a/src/cli/runtime/websocket-transport.test.ts +++ /dev/null @@ -1,263 +0,0 @@ -import { createServer, type Server } from 'node:http' -import 'node:fs' -import 'node:os' -import 'node:path' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { WebSocketServer } from 'ws' -import { encodePairingOffer } from '../../shared/remote-runtime/pairing/pairing' - -import type { RuntimeStatus } from '../../shared/runtime-types' -import { - decrypt, - deriveSharedKey, - encrypt, - generateKeyPair, - publicKeyToBase64 -} from '../../shared/remote-runtime/e2ee/node/e2ee-crypto' -import './websocket-transport' -import '../../shared/remote-runtime/requests/remote-runtime-prepared-request-admission' -import { RuntimeClient } from './client' -import '../workspaces/worktree-removal-request' -import { launchOrcaApp } from './launch' -import './environments' -import { - MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION, - RUNTIME_PROTOCOL_VERSION -} from '../../shared/protocol-version' - -vi.mock('./launch', () => ({ - launchOrcaApp: vi.fn() -})) - -type TestRuntime = { - endpoint: string - publicKeyB64: string - deviceToken: string - authFrames: Record[] - requestMethods: string[] - requestParams: unknown[] - connectionCount: () => number - close: () => Promise -} - -describe('CLI remote WebSocket transport', () => { - const servers: TestRuntime[] = [] - - afterEach(async () => { - vi.mocked(launchOrcaApp).mockClear() - await Promise.all(servers.splice(0).map((server) => server.close())) - }) - - it('blocks remote RPCs when the server protocol is too old', async () => { - const runtime = await startTestRuntime('runtime-old', { runtimeProtocolVersion: 1 }) - servers.push(runtime) - - const client = new RuntimeClient( - '/tmp/unused', - 5_000, - encodePairingOffer({ - v: 2, - endpoint: runtime.endpoint, - deviceToken: runtime.deviceToken, - publicKeyB64: runtime.publicKeyB64 - }) - ) - - await expect(client.call('repo.list')).rejects.toMatchObject({ - code: 'incompatible_runtime', - message: expect.stringContaining('server is too old') - }) - expect(runtime.connectionCount()).toBe(1) - expect(runtime.authFrames).toHaveLength(1) - expect(runtime.requestMethods).toEqual(['status.get']) - }) -}) - -async function startTestRuntime( - runtimeId: string, - statusOverrides: { - runtimeProtocolVersion?: number - minCompatibleRuntimeClientVersion?: number - desktopWindowStatus?: 'available' | 'openable' | 'initializing' | 'blocked' - appVersion?: string - remoteUpdateSupport?: { - installMode: 'unsupported-headless-serve' - automatic: false - reason: 'manual-service-update-required' - } - capabilities?: string[] - malformed?: boolean - failure?: boolean - hold?: string - onRequest?: (method: string) => void - disconnect?: boolean - mutationDisconnect?: boolean - rejectAuth?: boolean - degradations?: RuntimeStatus['degradations'] - } = {} -): Promise { - const serverKeyPair = generateKeyPair() - const deviceToken = `token-${runtimeId}` - const httpServer = createServer() - const wss = new WebSocketServer({ server: httpServer }) - const authFrames: Record[] = [] - const requestMethods: string[] = [] - const requestParams: unknown[] = [] - let connectionCount = 0 - - wss.on('connection', (ws) => { - let sharedKey: Uint8Array | null = null - let authenticated = false - - ws.on('message', (data) => { - const frame = data.toString() - if (!sharedKey) { - const hello = JSON.parse(frame) as Record & { - type?: string - publicKeyB64?: string - } - if (!hello.publicKeyB64) { - // Legacy-only host: refuses a v2 hello the way hosts before v2 do. - ws.close(4001, 'Invalid e2ee_hello') - return - } - connectionCount += 1 - const clientPublicKey = Buffer.from(hello.publicKeyB64 ?? '', 'base64') - sharedKey = deriveSharedKey(serverKeyPair.secretKey, clientPublicKey) - ws.send(JSON.stringify({ type: 'e2ee_ready' })) - return - } - - const plaintext = decrypt(frame, sharedKey) - if (!plaintext) { - ws.close(4003, 'decrypt failed') - return - } - if (!authenticated) { - const auth = JSON.parse(plaintext) as Record & { - type?: string - deviceToken?: string - } - authFrames.push(auth) - if ( - auth.type !== 'e2ee_auth' || - auth.deviceToken !== deviceToken || - statusOverrides.rejectAuth - ) { - ws.send(encrypt(JSON.stringify({ type: 'e2ee_error' }), sharedKey)) - ws.close(4001, 'auth failed') - return - } - authenticated = true - ws.send(encrypt(JSON.stringify({ type: 'e2ee_authenticated' }), sharedKey)) - return - } - - const request: { id: string; method: string; params?: unknown } = JSON.parse(plaintext) - requestMethods.push(request.method) - requestParams.push(request.params) - statusOverrides.onRequest?.(request.method) - if (request.method === statusOverrides.hold) { - return - } - if (request.method === 'status.get' && statusOverrides.disconnect) { - ws.close() - return - } - if (request.method === 'worktree.rm' && statusOverrides.mutationDisconnect) { - ws.close() - return - } - const response = - request.method === 'status.get' && statusOverrides.failure - ? { - id: request.id, - ok: false, - error: { code: 'runtime_unavailable', message: 'status failed' }, - _meta: { runtimeId } - } - : request.method === 'worktree.rm' - ? { id: request.id, ok: true, result: { removed: true }, _meta: { runtimeId } } - : request.method === 'status.get' - ? { - id: request.id, - ok: true, - result: statusOverrides.malformed - ? { capabilities: 42 } - : { - runtimeId, - rendererGraphEpoch: 1, - graphStatus: 'ready', - authoritativeWindowId: null, - desktopWindowStatus: statusOverrides.desktopWindowStatus, - liveTabCount: 0, - liveLeafCount: 0, - runtimeProtocolVersion: - statusOverrides.runtimeProtocolVersion ?? RUNTIME_PROTOCOL_VERSION, - minCompatibleRuntimeClientVersion: - statusOverrides.minCompatibleRuntimeClientVersion ?? - MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION, - appVersion: statusOverrides.appVersion, - remoteUpdateSupport: statusOverrides.remoteUpdateSupport, - capabilities: statusOverrides.capabilities, - degradations: statusOverrides.degradations - }, - _meta: { runtimeId } - } - : { - id: request.id, - ok: false, - error: { code: 'method_not_found', message: 'Unknown method' }, - _meta: { runtimeId } - } - ws.send(encrypt(JSON.stringify(response), sharedKey)) - }) - }) - - await listen(httpServer) - const address = httpServer.address() - if (!address || typeof address === 'string') { - throw new Error('Expected TCP test server') - } - - return { - endpoint: `ws://127.0.0.1:${address.port}`, - publicKeyB64: publicKeyToBase64(serverKeyPair.publicKey), - deviceToken, - authFrames, - requestMethods, - requestParams, - connectionCount: () => connectionCount, - close: async () => { - await new Promise((resolve) => { - wss.close(() => resolve()) - for (const client of wss.clients) { - client.close() - } - }) - await closeHttpServer(httpServer) - } - } -} - -async function listen(server: Server): Promise { - await new Promise((resolve, reject) => { - server.once('error', reject) - server.listen(0, '127.0.0.1', () => { - server.off('error', reject) - resolve() - }) - }) -} - -async function closeHttpServer(server: Server): Promise { - await new Promise((resolve, reject) => { - server.close((error) => { - if (error) { - reject(error) - return - } - resolve() - }) - }) -} diff --git a/src/renderer/src/components/automations/AutomationsPage.behavior.test.tsx b/src/renderer/src/components/automations/AutomationsPage.behavior.test.tsx deleted file mode 100644 index c5510a4f3e1..00000000000 --- a/src/renderer/src/components/automations/AutomationsPage.behavior.test.tsx +++ /dev/null @@ -1,4 +0,0 @@ -// @vitest-environment happy-dom -import './AutomationsPage.escape-precedence.spec' -import './AutomationsPage.external-scope.spec' -import './AutomationsPage.refresh-selection.spec' diff --git a/src/renderer/src/components/automations/AutomationsPage.escape-precedence.spec.tsx b/src/renderer/src/components/automations/AutomationsPage.escape-precedence.spec.tsx deleted file mode 100644 index 99171389359..00000000000 --- a/src/renderer/src/components/automations/AutomationsPage.escape-precedence.spec.tsx +++ /dev/null @@ -1,72 +0,0 @@ -// @vitest-environment happy-dom - -/** Window capture runs before Radix document capture; preventing Escape here would veto dismissal of an overlaid dialog (STA-5207). */ - -import { describe, expect, it, vi } from 'vitest' -import { - installAutomationsPageHarness, - mocks, - renderPage, - settleHostQueries -} from './automations-page-test-harness' - -function pressEscape(target: EventTarget): KeyboardEvent { - const event = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true }) - target.dispatchEvent(event) - return event -} - -async function renderWithCloseSpy(): Promise> { - const closeAutomationsPage = vi.fn() - mocks.state.closeAutomationsPage = closeAutomationsPage - await renderPage() - await settleHostQueries() - return closeAutomationsPage -} - -describe('automations page Escape precedence', () => { - installAutomationsPageHarness() - - it('closes the page when nothing is layered above it', async () => { - const closeAutomationsPage = await renderWithCloseSpy() - - const event = pressEscape(document.body) - - expect(event.defaultPrevented).toBe(true) - expect(closeAutomationsPage).toHaveBeenCalledTimes(1) - }) - - it('leaves Escape to a store modal opened over the page', async () => { - mocks.state.activeModal = 'new-workspace-composer' - const closeAutomationsPage = await renderWithCloseSpy() - const input = document.createElement('input') - document.body.appendChild(input) - - const event = pressEscape(input) - - expect(event.defaultPrevented).toBe(false) - expect(closeAutomationsPage).not.toHaveBeenCalled() - }) - - it('leaves Escape to a store modal even when focus is on page chrome', async () => { - mocks.state.activeModal = 'worktree-palette' - const closeAutomationsPage = await renderWithCloseSpy() - - const event = pressEscape(document.body) - - expect(event.defaultPrevented).toBe(false) - expect(closeAutomationsPage).not.toHaveBeenCalled() - }) - - it('leaves Escape to a visible overlay that the store does not track', async () => { - const closeAutomationsPage = await renderWithCloseSpy() - const overlay = document.createElement('div') - overlay.setAttribute('role', 'dialog') - document.body.appendChild(overlay) - - const event = pressEscape(document.body) - - expect(event.defaultPrevented).toBe(false) - expect(closeAutomationsPage).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/components/automations/AutomationsPage.external-scope.spec.tsx b/src/renderer/src/components/automations/AutomationsPage.external-scope.spec.tsx deleted file mode 100644 index 6b417860ff1..00000000000 --- a/src/renderer/src/components/automations/AutomationsPage.external-scope.spec.tsx +++ /dev/null @@ -1,69 +0,0 @@ -// @vitest-environment happy-dom - -/** Drive the page’s scope producer and inspect IPC; client tests with pre-narrowed scopes cannot verify which hosts the page asks. */ - -import { describe, expect, it } from 'vitest' - -import { - api, - installAutomationsPageHarness, - mocks, - renderPage, - settleHostQueries -} from './automations-page-test-harness' -import { listedExternalEntries } from './automations-page-listed-items' -import { makeExternalManager } from './automations-page-fixtures' - -const DESKTOP_SELF_FILTER = { - kind: 'host' as const, - host: { authority: { kind: 'desktop' as const }, selector: { kind: 'self' as const } } -} - -function sshFilter(targetId: string): unknown { - return { - kind: 'host', - host: { authority: { kind: 'desktop' }, selector: { kind: 'ssh', targetId } } - } -} - -/** Registered SSH targets, each with the generation that makes it fenceable. */ -function withSshHosts(...targetIds: string[]): void { - mocks.state.sshTargetLabels = new Map(targetIds.map((targetId) => [targetId, targetId])) - mocks.state.sshTargetGenerations = new Map(targetIds.map((targetId) => [targetId, 1])) - mocks.state.sshConnectionStates = new Map( - targetIds.map((targetId) => [targetId, { status: 'connected' }]) - ) -} - -function openclawOnly(): void { - api.automations.listExternalManagerForOwner.mockImplementation( - async ({ provider }: { provider: string }) => - provider === 'openclaw' - ? { manager: makeExternalManager(), error: null, updatedAt: 1 } - : { manager: null, error: null, updatedAt: 1 } - ) -} - -describe('AutomationsPage external manager probes', () => { - installAutomationsPageHarness() - - it('drops the previous host rows when the selection moves, not when the new probe lands', async () => { - withSshHosts('staging') - mocks.state.automationHostFilter = DESKTOP_SELF_FILTER - openclawOnly() - - const { rerender } = await renderPage() - await settleHostQueries() - expect(listedExternalEntries().length).toBeGreaterThan(0) - - // The new host never answers, so anything still listed belongs to the old one. - api.automations.listExternalManagerForOwner.mockImplementation( - async () => await new Promise(() => undefined) - ) - mocks.state.automationHostFilter = sshFilter('staging') - await rerender() - await settleHostQueries() - - expect(listedExternalEntries()).toEqual([]) - }) -}) diff --git a/src/renderer/src/components/automations/AutomationsPage.refresh-selection.spec.tsx b/src/renderer/src/components/automations/AutomationsPage.refresh-selection.spec.tsx deleted file mode 100644 index 2a96389539d..00000000000 --- a/src/renderer/src/components/automations/AutomationsPage.refresh-selection.spec.tsx +++ /dev/null @@ -1,133 +0,0 @@ -// @vitest-environment happy-dom - -/** Refresh reaches one authority at a time; deriving selection/history from that answer would drop or mislabel other hosts’ rows under All hosts. */ - -import { act } from 'react' -import { describe, expect, it, vi } from 'vitest' -import { - api, - installAutomationsPageHarness, - mocks, - refreshOnFocus, - renderPage, - RUNTIME_SELF_FILTER, - runtimeHost, - scopedList, - SELF_PRECONDITION, - settleHostQueries -} from './automations-page-test-harness' -import { listedRows } from './automations-page-listed-items' -import { makeAutomation, makeRun } from './automations-page-fixtures' - -async function openAutomationRow(container: HTMLElement, name: string): Promise { - const row = [ - ...container.querySelectorAll('[data-testid="automation-row"]') - ].find((candidate) => candidate.textContent === name) - expect(row).toBeTruthy() - await act(async () => row?.click()) - await settleHostQueries() -} - -describe('AutomationsPage selection', () => { - installAutomationsPageHarness() - - it('fetches the newly selected automation runs when the selection changes', async () => { - scopedList([ - makeAutomation({ id: 'a-1', name: 'Nightly' }), - makeAutomation({ id: 'a-2', name: 'Weekly' }) - ]) - - const { container, rerender } = await renderPage() - api.automations.listRuns.mockClear() - - mocks.state.selectedAutomationId = 'a-2' - await rerender() - await openAutomationRow(container, 'Weekly') - - // Fence history reads too so moved rows refuse rather than answer from a former host. - expect(api.automations.listRuns).toHaveBeenCalledWith({ - automationId: 'a-2', - expectedOwner: SELF_PRECONDITION, - preview: true - }) - expect(mocks.detailPane?.selected?.id).toBe('a-2') - }) -}) - -describe('AutomationsPage multi-host selection', () => { - installAutomationsPageHarness() - - it('keeps a selection that lives on another host when the window regains focus', async () => { - runtimeHost([makeAutomation({ id: 'a-remote', name: 'Remote nightly' })], []) - const desktop = makeAutomation({ id: 'a-desktop', name: 'Desktop nightly' }) - scopedList([desktop]) - api.automations.list.mockResolvedValue([desktop]) - const setSelectedAutomationId = vi.fn((id: string | null) => { - mocks.state.selectedAutomationId = id - }) - mocks.state.selectedAutomationId = 'a-remote' - mocks.state.setSelectedAutomationId = setSelectedAutomationId - - const { container, rerender } = await renderPage() - await settleHostQueries() - await refreshOnFocus() - await rerender() - - // One authority’s refresh cannot decide whether another authority’s selected row still exists. - expect(setSelectedAutomationId).not.toHaveBeenCalled() - await openAutomationRow(container, 'Remote nightly') - expect(container.querySelector('[data-testid="detail-name"]')?.textContent).toBe( - 'Remote nightly' - ) - }) - - it('shows the selected host’s runs when another authority holds the same id', async () => { - runtimeHost( - [makeAutomation({ id: 'a-1', name: 'Remote nightly' })], - [makeRun({ id: 'run-remote', automationId: 'a-1' })] - ) - // IDs can collide across authorities; desktop remains independently reachable. - api.automations.list.mockResolvedValue([makeAutomation({ id: 'a-1', name: 'Desktop nightly' })]) - api.automations.listRuns.mockResolvedValue([ - makeRun({ id: 'run-desktop', automationId: 'a-1' }) - ]) - scopedList([]) - mocks.state.automationHostFilter = RUNTIME_SELF_FILTER - mocks.state.selectedAutomationId = 'a-1' - - const { container } = await renderPage() - await settleHostQueries() - await refreshOnFocus() - await openAutomationRow(container, 'Remote nightly') - - expect(mocks.detailPane?.selected?.name).toBe('Remote nightly') - expect(mocks.detailPane?.selectedRuns.map((run) => run.id)).toEqual(['run-remote']) - }) - - it('keeps both authorities’ rows on screen and selectable under All hosts', async () => { - runtimeHost( - [makeAutomation({ id: 'a-1', name: 'Remote nightly' })], - [makeRun({ id: 'run-remote', automationId: 'a-1' })] - ) - scopedList([makeAutomation({ id: 'a-1', name: 'Desktop nightly' })]) - // The stored id names the record; only the row key can say whose copy. - mocks.state.selectedAutomationId = 'a-1' - - const { container } = await renderPage() - await settleHostQueries() - - // Two stored records, not one row and a silent drop into #44's empty state. - expect( - [...container.querySelectorAll('[data-testid="automation-row"]')].map( - (row) => row.textContent - ) - ).toEqual(['Desktop nightly', 'Remote nightly']) - - const remote = listedRows().find((row) => row.automation.name === 'Remote nightly') - await act(async () => { - mocks.listPanel?.selectAutomationRow(remote?.key ?? '') - }) - - expect(mocks.detailPane?.selected?.name).toBe('Remote nightly') - }) -}) diff --git a/src/renderer/src/components/automations/automations-page-actions-test-harness.ts b/src/renderer/src/components/automations/automations-page-actions-test-harness.ts deleted file mode 100644 index cb581cd733e..00000000000 --- a/src/renderer/src/components/automations/automations-page-actions-test-harness.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { EVERY_AGENT_CHAT } from '../../../../shared/native-chat/every-agent-chat.test-fixture' -import { - addRuntimeProject, - api, - mocks, - renderPage, - runtimeHost, - RUNTIME_ID, - RUNTIME_SELF_FILTER, - scopedList, - settleHostQueries -} from './automations-page-test-harness' -import { RUNTIME_REPO_ID, RUNTIME_WORKSPACE_ID } from './automations-page-runtime-fixtures' -import { act } from 'react' -import { expect, vi } from 'vitest' -import { checkRuntimeHooks, type HookCheckResult } from '@/runtime/repo-hooks/runtime-hooks-client' -import { loadHostStructuredAgents } from '@/runtime/native-chat/agent-catalogs/host-structured-agents' -import { getDefaultSettings } from '../../../../shared/constants' -import type { Automation } from '../../../../shared/automations/automation-definition' -import type { AutomationDraft } from './authoring/AutomationEditorDialog' -import { - AUTOMATION_LIST_HOST_SCOPE_RUNTIME_CAPABILITY as LIST_HOST_SCOPE, - AUTOMATION_OWNER_FENCING_RUNTIME_CAPABILITY as OWNER_FENCING, - AUTOMATION_CREATE_IDEMPOTENCY_RUNTIME_CAPABILITY as CREATE_IDEMPOTENCY, - AUTOMATION_SURFACE_RUNTIME_CAPABILITY, - AUTOMATION_EXTRA_AGENT_ARGS_RUNTIME_CAPABILITY -} from '../../../../shared/automations/automation-runtime-capabilities' -import { - STRUCTURED_AGENT_SESSION_RUNTIME_CAPABILITY, - STRUCTURED_AGENT_SESSION_CLIENT_LAUNCH_MODE_CAPABILITY -} from '../../../../shared/agent-session/agent-session-runtime-capabilities' -import { STRUCTURED_AGENT_SESSION_REGISTERED_AGENTS_RUNTIME_CAPABILITY } from '../../../../shared/agent-session/structured-agent-session-surface-capabilities' -import { makeAutomation } from './automations-page-fixtures' - -export const CHAT_CAPABILITIES = [ - LIST_HOST_SCOPE, - OWNER_FENCING, - CREATE_IDEMPOTENCY, - AUTOMATION_SURFACE_RUNTIME_CAPABILITY, - AUTOMATION_EXTRA_AGENT_ARGS_RUNTIME_CAPABILITY, - STRUCTURED_AGENT_SESSION_RUNTIME_CAPABILITY, - STRUCTURED_AGENT_SESSION_CLIENT_LAUNCH_MODE_CAPABILITY, - STRUCTURED_AGENT_SESSION_REGISTERED_AGENTS_RUNTIME_CAPABILITY -] - -export function draftRecord(current: unknown): object { - if (current === null || typeof current !== 'object') { - throw new Error('Missing editor draft') - } - return current -} - -/** The runtime answers a create; without this the RPC double returns an empty result. */ -export function runtimeCreateReturns(automation: Automation): void { - const previous = mocks.callRuntimeRpc.getMockImplementation() - mocks.callRuntimeRpc.mockImplementation( - async (target: unknown, method: string, params: unknown, options: unknown) => { - if (method === 'automation.create') { - return { automation } - } - return await previous?.(target, method, params, options) - } - ) -} - -export async function save(): Promise { - await act(async () => { - mocks.editorDialog?.onSave() - }) -} - -export function createRpcCalls(): unknown[][] { - return mocks.callRuntimeRpc.mock.calls.filter((call) => call[1] === 'automation.create') -} - -export const REGISTERED_CHAT_CAPABILITIES = [ - ...CHAT_CAPABILITIES, - STRUCTURED_AGENT_SESSION_REGISTERED_AGENTS_RUNTIME_CAPABILITY -] - -export function observeRuntimeIncarnation( - runtimeId: string, - capabilities = REGISTERED_CHAT_CAPABILITIES -): void { - const status = { runtimeId, capabilities } - mocks.state.runtimeStatusByEnvironmentId = new Map([[RUNTIME_ID, { status }]]) - mocks.getRuntimeEnvironmentStatus.mockResolvedValue(status) -} - -export async function learnRuntimeAgents(runtimeId: string, agents: string[]): Promise { - const previous = mocks.callRuntimeRpc.getMockImplementation() - mocks.callRuntimeRpc.mockImplementation(async (target, method, params, options) => - method === 'agentSession.agents' - ? { agents: agents.map((agent) => ({ agent, capabilities: {} })) } - : await previous?.(target, method, params, options) - ) - try { - await loadHostStructuredAgents(`runtime:${RUNTIME_ID}`, REGISTERED_CHAT_CAPABILITIES, runtimeId) - } finally { - mocks.callRuntimeRpc.mockImplementation(previous ?? (() => ({}))) - } -} - -export async function openCreateDialogFor(projectId: string, workspaceId: string): Promise { - await act(async () => { - mocks.listPanel?.openCreateDialog() - }) - await act(async () => { - mocks.editorDialog?.onDraftChange((current) => ({ - ...draftRecord(current), - name: 'Sweep', - prompt: 'Do the sweep', - projectId, - workspaceMode: 'existing', - workspaceId - })) - }) -} - -export async function holdRuntimeSave( - draft: Partial = {}, - initialAgents = ['pi'] -) { - api.automations.list.mockResolvedValue([]) - scopedList([]) - runtimeHost([], []) - addRuntimeProject() - mocks.state.settings = { ...getDefaultSettings('/tmp'), nativeChatProviders: EVERY_AGENT_CHAT } - mocks.state.automationHostFilter = RUNTIME_SELF_FILTER - observeRuntimeIncarnation('A') - await learnRuntimeAgents('A', initialAgents) - runtimeCreateReturns(makeAutomation({ id: 'a-new' })) - const held = Promise.withResolvers() - vi.mocked(checkRuntimeHooks).mockReturnValue(held.promise) - const page = await renderPage() - await settleHostQueries() - await openCreateDialogFor(RUNTIME_REPO_ID, RUNTIME_WORKSPACE_ID) - await act(async () => { - mocks.editorDialog?.onDraftChange((current) => ({ - ...draftRecord(current), - agentId: 'pi', - workspaceMode: 'new_per_run', - workspaceId: '', - ...draft - })) - }) - await save() - expect(checkRuntimeHooks).toHaveBeenCalledWith(`runtime:${RUNTIME_ID}`, RUNTIME_REPO_ID, {}) - expect(createRpcCalls()).toHaveLength(0) - return { - page, - async release() { - await act(async () => { - held.resolve({ status: 'ok', hasHooks: false, hooks: null, mayNeedUpdate: false }) - }) - await settleHostQueries() - } - } -} diff --git a/src/renderer/src/components/automations/automations-page-child-props.test-fixture.ts b/src/renderer/src/components/automations/automations-page-child-props.test-fixture.ts deleted file mode 100644 index 7852a857b10..00000000000 --- a/src/renderer/src/components/automations/automations-page-child-props.test-fixture.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { AutomationAuthorityRef } from '../../../../shared/automations/ownership/automation-owner-ref' -import type { Automation } from '../../../../shared/automations/automation-definition' -import type { Worktree } from '../../../../shared/worktree/types' -import type { AutomationHostCatalogView } from './hosts/use-automation-host-catalog' -import type { AutomationCreateDestinationControl } from './authoring/use-automation-create-destination' -import type { ExternalAutomationListEntry } from './external/external-automation-list-entries' -import type { AutomationListRow } from './listing/automation-list-row-identity' -import type { AutomationListViewItem } from './listing/automation-list-view' -import type { AutomationTemplate } from './authoring/automation-templates' - -export type ListPanelProps = { - sortedListItems: readonly AutomationListViewItem[] - selectedExternal: ExternalAutomationListEntry | null - externalActionKey: string | null - requestExternalAction: ( - manager: ExternalAutomationListEntry['manager'], - job: ExternalAutomationListEntry['job'], - action: 'run' | 'pause' | 'resume' | 'delete', - scope: ExternalAutomationListEntry['scope'] - ) => void - hasListItems: boolean - hasFilteredListItems: boolean - selectedRowKey: string | null - selectedExternalKey: string | null - hostCatalog: AutomationHostCatalogView - searchCounts: { hostRowCount: number; visibleRowCount: number; searchActive: boolean } - externalManagersUncheckedNotice: string | null - isActionEnabled: (row: AutomationListRow, action: string) => boolean - onSelectHost: (filter: unknown) => void - selectAutomationRow: (rowKey: string | null) => void - selectExternalKey: (entryKey: string | null) => void - onOpenDetail: () => void - onRefresh: () => void - onOpenRuns: () => void - runNow: (row: AutomationListRow) => void - openEditDialog: (row: AutomationListRow) => void - toggleAutomation: (row: AutomationListRow) => void - requestDeleteAutomation: (row: AutomationListRow) => void - openCreateDialog: () => void - canCreateAutomation: boolean -} - -export type EditorDialogProps = { - launchAuthority?: AutomationAuthorityRef - open: boolean - isEditing: boolean - isSaving: boolean - createDestination?: AutomationCreateDestinationControl - editDestination?: AutomationCreateDestinationControl - notice?: { message: string; recovery: string | null } | null - onNoticeRecover?: (action: string) => void - repos?: { id: string }[] - worktrees: Worktree[] - draft?: { projectId: string; workspaceId: string } - canSave?: boolean - onSave: () => void - onProjectChange: (projectId: string) => void - onDraftChange: (updater: (current: unknown) => unknown) => void - onApplyTemplate: (template: AutomationTemplate) => void -} - -export type DeleteDialogProps = { - deleteTarget: Automation | null - onConfirm: () => void -} diff --git a/src/renderer/src/components/automations/automations-page-destination-fixtures.ts b/src/renderer/src/components/automations/automations-page-destination-fixtures.ts deleted file mode 100644 index 53a9be5a2dc..00000000000 --- a/src/renderer/src/components/automations/automations-page-destination-fixtures.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { mocks, RUNTIME_ID } from './automations-page-test-harness' -import { CHAT_CAPABILITIES } from './automations-page-actions-test-harness' -import type { Repo } from '../../../../shared/repo-types' -import { hostStableKey } from '../../../../shared/automations/ownership/automation-owner-key' - -export function observeRuntimeChat(capabilities: string[] | null): void { - mocks.state.runtimeStatusByEnvironmentId = new Map( - capabilities === null ? [] : [[RUNTIME_ID, { status: { capabilities } }]] - ) -} - -export function capableChatDestination(): void { - observeRuntimeChat(CHAT_CAPABILITIES) - mocks.getRuntimeEnvironmentStatus.mockResolvedValue({ capabilities: CHAT_CAPABILITIES }) -} - -export const SSH_TARGET_ID = 'ssh-target-1' -export const SSH_REPO_ID = 'repo-ssh' -export const SSH_HOST_KEY = hostStableKey({ - authority: { kind: 'desktop' }, - selector: { kind: 'ssh', targetId: SSH_TARGET_ID } -}) - -export const DESKTOP_SELF_KEY = hostStableKey({ - authority: { kind: 'desktop' }, - selector: { kind: 'self' } -}) -export const RUNTIME_SELF_KEY = hostStableKey({ - authority: { kind: 'runtime', environmentId: RUNTIME_ID }, - selector: { kind: 'self' } -}) - -/** A desktop-registered SSH host, with the generation that makes it fenceable. */ -export function addSshHost(): void { - mocks.state.sshTargetLabels = new Map([[SSH_TARGET_ID, 'openclaw']]) - mocks.state.sshTargetGenerations = new Map([[SSH_TARGET_ID, 1]]) - mocks.state.sshConnectionStates = new Map([[SSH_TARGET_ID, { status: 'connected' }]]) -} - -/** A project checked out on that SSH host rather than locally. */ -export function addSshProject(): void { - const repo: Repo = { - id: SSH_REPO_ID, - displayName: 'orca', - path: '/repos/orca', - badgeColor: '#222222', - addedAt: 1, - worktreeBaseRef: 'main', - connectionId: SSH_TARGET_ID - } - mocks.state.repos = [...(mocks.state.repos as Repo[]), repo] - mocks.repoMap.set(SSH_REPO_ID, repo) - // The host needs a ready setup or save refuses the project before choosing a destination. - mocks.state.projectHostSetups = [ - ...mocks.state.projectHostSetups, - { - id: 'setup-ssh', - projectId: 'project-ssh', - hostId: `ssh:${SSH_TARGET_ID}`, - repoId: SSH_REPO_ID, - path: '/repos/orca', - displayName: 'orca', - setupState: 'ready', - setupMethod: 'legacy-repo', - createdAt: 1, - updatedAt: 1 - } - ] -} diff --git a/src/renderer/src/components/automations/automations-page-fixtures.ts b/src/renderer/src/components/automations/automations-page-fixtures.ts index 9b60daacd74..02c5c2e0fd5 100644 --- a/src/renderer/src/components/automations/automations-page-fixtures.ts +++ b/src/renderer/src/components/automations/automations-page-fixtures.ts @@ -1,29 +1,14 @@ -/** Default to runnable local records so Run Now reaches its handler; cases needing refusal override the relevant fields. */ - import type { Automation } from '../../../../shared/automations/automation-definition' import type { AutomationRun, AutomationRunUsage } from '../../../../shared/automations/history/automation-run' -import type { ExternalAutomationManager } from '../../../../shared/automations/external/external-automation' -import type { ProjectHostSetup } from '../../../../shared/project-types' -import type { AutomationHostCatalogEntry } from './hosts/automation-host-catalog-types' -import type { AppState } from '@/store/types' -import type { Repo } from '../../../../shared/repo-types' -import type { Worktree } from '../../../../shared/worktree/types' -import type { - ExternalAutomationScope, - ScopedExternalAutomationManager -} from './external/external-automation-scope-client' import { automationListRowKey, type AutomationListRow } from './listing/automation-list-row-identity' -import { ALL_AUTOMATION_HOSTS_FILTER } from '../../../../shared/automations/ownership/automation-host-filter' -import { getDefaultSettings } from '../../../../shared/constants' export const REPO_ID = 'repo-1' -export const WORKSPACE_ID = 'workspace-1' export function makeAutomation(overrides: Partial = {}): Automation { return { @@ -114,186 +99,3 @@ export function makeRunUsage(overrides: Partial = {}): Autom ...overrides } } - -export function makeExternalManager( - overrides: Partial = {} -): ExternalAutomationManager { - return { - id: 'openclaw:local', - provider: 'openclaw', - label: 'OpenClaw', - targetLabel: 'This computer', - target: { type: 'local' }, - status: 'available', - error: null, - canManage: true, - jobs: [ - { - id: 'job-1', - managerId: 'openclaw:local', - provider: 'openclaw', - name: 'OpenClaw job', - schedule: 'Daily at 09:00', - rawSchedule: '0 9 * * *', - enabled: true, - state: 'active', - prompt: null, - promptPreview: 'Sweep', - nextRunAt: null, - lastRunAt: null, - lastStatus: null, - lastError: null, - workdir: null, - runCount: 0, - runs: [] - } - ], - ...overrides - } -} - -/** The desktop-self scope every manager fixture is discovered under by default. */ -export function makeExternalAutomationScope( - overrides: Partial = {} -): ExternalAutomationScope { - return { - owner: { - authority: { kind: 'desktop' }, - selector: { kind: 'self' } - }, - provider: 'openclaw', - ...overrides - } -} - -export function makeScopedExternalManager( - overrides: Partial = {}, - scope: ExternalAutomationScope = makeExternalAutomationScope() -): ScopedExternalAutomationManager { - return { scope, manager: makeExternalManager(overrides) } -} - -function makeRepo(): Repo { - return { - id: REPO_ID, - displayName: 'orca', - path: '/repos/orca', - badgeColor: '#000000', - addedAt: 1, - worktreeBaseRef: 'main' - } -} - -/** Ready and local, so the editor can build a run context and state a Self destination. */ -function makeProjectHostSetup(): ProjectHostSetup { - return { - id: 'setup-1', - projectId: 'project-1', - hostId: 'local', - repoId: REPO_ID, - path: '/repos/orca', - displayName: 'orca', - setupState: 'ready', - setupMethod: 'legacy-repo', - createdAt: 1, - updatedAt: 1 - } -} - -export function makeWorktree(overrides: Partial = {}): Worktree { - return { - id: WORKSPACE_ID, - repoId: REPO_ID, - displayName: 'main', - path: '/repos/orca', - branch: 'main', - head: 'abc123', - isBare: false, - isMainWorktree: true, - comment: '', - linkedIssue: null, - linkedPR: null, - linkedLinearIssue: null, - isArchived: false, - isUnread: false, - isPinned: false, - sortOrder: 0, - lastActivityAt: 0, - ...overrides - } -} - -export type AutomationsPageFixtureState = Record & - Pick - -export type AutomationsPageStoreFixtures = { - state: AutomationsPageFixtureState - repoMap: Map - worktreeMap: Map -} - -/** A store shaped like the page's reads; actions are spies so nothing escapes the test. */ -export function makeStoreState(): AutomationsPageStoreFixtures { - const repo = makeRepo() - const worktree = makeWorktree() - const noop = (): void => undefined - return { - state: { - repos: [repo], - projectHostSetups: [makeProjectHostSetup()], - worktreesByRepo: { [REPO_ID]: [worktree] }, - unifiedTabsByWorktree: {}, - terminalLayoutsByTabId: {}, - ptyIdsByTabId: {}, - activeWorktreeId: null, - fetchWorktrees: noop, - fetchAllWorktrees: noop, - fetchRuntimeEnvironmentRepos: async () => [], - startupWorktreeRefreshCompleted: true, - updateSettings: noop, - openSettingsPage: noop, - openSettingsTarget: noop, - closeAutomationsPage: noop, - activeModal: 'none', - sshConnectionStates: new Map(), - sshTargetLabels: new Map(), - removedSshTargetLabels: new Map(), - // Hydrated absence proves no desktop SSH targets; the catalog should project only Self. - sshTargetsHydrated: true, - sshStateByEnvironment: new Map(), - runtimeEnvironments: [], - runtimeEnvironmentCatalogSettled: true, - runtimeStatusByEnvironmentId: new Map(), - automationHostFilter: ALL_AUTOMATION_HOSTS_FILTER, - setAutomationHostFilter: noop, - settings: getDefaultSettings('/tmp'), - preflightStatus: null, - preflightStatusChecked: true, - preflightStatusContextKey: null, - refreshPreflightStatus: noop, - selectedAutomationId: null, - setSelectedAutomationId: noop, - pendingAutomationRunNavigation: null, - setPendingAutomationRunNavigation: noop, - hydratePersistedUI: noop, - recordFeatureInteraction: noop, - allWorktrees: () => [worktree], - getKnownWorktreeById: () => worktree - }, - repoMap: new Map([[REPO_ID, repo]]), - worktreeMap: new Map([[WORKSPACE_ID, worktree]]) - } -} - -export type DetailPaneProps = { - selected: Automation | null - selectedHostEntry: AutomationHostCatalogEntry | null - selectedRuns: AutomationRun[] - selectedRunsNotice: { message: string } | null - runNow: (automation: Automation) => void - toggleAutomation: (automation: Automation) => void - requestDeleteAutomation: (automation: Automation) => void - openEditDialog: (automation: Automation) => void - openAutomationRunPage: (run: AutomationRun) => void - recoverSelectedRuns: (action: 'retry') => void -} diff --git a/src/renderer/src/components/automations/automations-page-listed-items.ts b/src/renderer/src/components/automations/automations-page-listed-items.ts deleted file mode 100644 index 2f64eff3037..00000000000 --- a/src/renderer/src/components/automations/automations-page-listed-items.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** Read authority-qualified row keys and render order from the mocked list panel, matching real clicks rather than synthesizing identity. */ - -import type { AutomationListRow } from './listing/automation-list-row-identity' -import type { ExternalAutomationListEntry } from './external/external-automation-list-entries' -import { mocks } from './automations-page-test-harness' - -function listedItems() { - return mocks.listPanel?.sortedListItems ?? [] -} - -/** Local rows the page listed, in render order. */ -export function listedRows(): readonly AutomationListRow[] { - return listedItems().flatMap((item) => (item.kind === 'local' ? [item.row] : [])) -} - -/** External entries the page listed, in render order. */ -export function listedExternalEntries(): readonly ExternalAutomationListEntry[] { - return listedItems().flatMap((item) => (item.kind === 'external' ? [item.entry] : [])) -} - -export function listedRow(automationId: string): AutomationListRow { - const row = listedRows().find((entry) => entry.automation.id === automationId) - if (!row) { - throw new Error(`no listed row for ${automationId}`) - } - return row -} diff --git a/src/renderer/src/components/automations/automations-page-runtime-fixtures.ts b/src/renderer/src/components/automations/automations-page-runtime-fixtures.ts deleted file mode 100644 index f35be180c75..00000000000 --- a/src/renderer/src/components/automations/automations-page-runtime-fixtures.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type { AutomationsPageFixtureState } from './automations-page-fixtures' -import type { Automation } from '../../../../shared/automations/automation-definition' -import type { ProjectHostSetup } from '../../../../shared/project-types' -import type { Repo } from '../../../../shared/repo-types' -import type { Worktree } from '../../../../shared/worktree/types' - -export const RUNTIME_REPO_ID = 'repo-2' -export const RUNTIME_WORKSPACE_ID = 'workspace-2' - -/** Builds the host-scoped response used by the page harness. */ -export function selfScopedList(automations: Automation[]): Record { - return { - automations, - items: automations.map((automation) => ({ - automationId: automation.id, - selector: { kind: 'self' } - })), - orphanCount: 0 - } -} - -type RuntimeFixtureMocks = { - state: AutomationsPageFixtureState - repoMap: Map - worktreeMap: Map -} - -/** Adds a runtime-owned project whose id is ambiguous in a flat repo lookup. */ -export function addRuntimeProject(mocks: RuntimeFixtureMocks, runtimeId: string): void { - const repo = { - id: RUNTIME_REPO_ID, - displayName: 'gpu-orca', - path: '/repos/gpu-orca', - badgeColor: '#111111', - addedAt: 1, - worktreeBaseRef: 'main', - executionHostId: `runtime:${runtimeId}` - } as Repo - const worktree = { - id: RUNTIME_WORKSPACE_ID, - repoId: RUNTIME_REPO_ID, - displayName: 'main', - path: '/repos/gpu-orca', - branch: 'main' - } as Worktree - const setup: ProjectHostSetup = { - id: 'setup-2', - projectId: 'project-2', - hostId: `runtime:${runtimeId}`, - repoId: RUNTIME_REPO_ID, - path: '/repos/gpu-orca', - displayName: 'gpu-orca', - setupState: 'ready', - setupMethod: 'legacy-repo', - createdAt: 1, - updatedAt: 1 - } - const repos = mocks.state.repos as Repo[] - const setups = mocks.state.projectHostSetups - const worktreesByRepo = mocks.state.worktreesByRepo as Record - mocks.state.repos = [...repos, repo] - mocks.state.projectHostSetups = [...setups, setup] - mocks.state.worktreesByRepo = { ...worktreesByRepo, [RUNTIME_REPO_ID]: [worktree] } - mocks.repoMap.set(RUNTIME_REPO_ID, repo) - mocks.worktreeMap.set(RUNTIME_WORKSPACE_ID, worktree) -} diff --git a/src/renderer/src/components/automations/automations-page-test-harness.tsx b/src/renderer/src/components/automations/automations-page-test-harness.tsx deleted file mode 100644 index 1dbb50644f4..00000000000 --- a/src/renderer/src/components/automations/automations-page-test-harness.tsx +++ /dev/null @@ -1,399 +0,0 @@ -import type { - ListPanelProps, - EditorDialogProps, - DeleteDialogProps -} from './automations-page-child-props.test-fixture' -import { resetHostStructuredAgentsForTests } from '@/runtime/native-chat/agent-catalogs/host-structured-agents' -import { setLocalRuntimeCapabilitiesForTests } from '@/runtime/hosts/capabilities/local-runtime-capabilities' -/** Record child props to test page orchestration rather than presentation. Import this harness first so shared vi.mock registrations precede AutomationsPage loading. */ - -import { act, createElement, StrictMode, type ReactNode } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, type Mock, vi } from 'vitest' - -import type { Automation } from '../../../../shared/automations/automation-definition' -import type { AutomationRun } from '../../../../shared/automations/history/automation-run' - -import { - AUTOMATION_LIST_HOST_SCOPE_RUNTIME_CAPABILITY as LIST_HOST_SCOPE, - AUTOMATION_OWNER_FENCING_RUNTIME_CAPABILITY as OWNER_FENCING, - AUTOMATION_CREATE_IDEMPOTENCY_RUNTIME_CAPABILITY as CREATE_IDEMPOTENCY -} from '../../../../shared/automations/automation-runtime-capabilities' -import type { AppState } from '@/store/types' - -import { AutomationDestinationField } from './authoring/AutomationDestinationField' -import { TooltipProvider } from '@/components/ui/tooltip' - -import { resetAutomationCapabilityProbes } from './hosts/automation-capability-probe' -import { - addRuntimeProject as addRuntimeProjectFixture, - selfScopedList -} from './automations-page-runtime-fixtures' - -type AutomationsPageMocks = { - state: AutomationsPageFixtureState - repoMap: Map - worktreeMap: Map - listPanel: ListPanelProps | null - detailPane: DetailPaneProps | null - editorDialog: EditorDialogProps | null - deleteDialog: DeleteDialogProps | null - callRuntimeRpc: Mock - getRuntimeEnvironmentStatus: Mock - setAutomationHostFilter: Mock - toastError: Mock - toastSuccess: Mock - toastMessage: Mock - setPendingRunNavigation: Mock -} - -// Mock factories only close over this exported binding; vi.hoisted bindings cannot be exported. -export const mocks: AutomationsPageMocks = { - state: { repos: [], projectHostSetups: [], worktreesByRepo: {} }, - repoMap: new Map(), - worktreeMap: new Map(), - listPanel: null, - detailPane: null, - editorDialog: null, - deleteDialog: null, - callRuntimeRpc: vi.fn(), - getRuntimeEnvironmentStatus: vi.fn(), - setAutomationHostFilter: vi.fn(), - toastError: vi.fn(), - toastSuccess: vi.fn(), - toastMessage: vi.fn(), - setPendingRunNavigation: vi.fn() -} - -vi.mock('@/store/index', () => { - const useAppStore = (selector: (state: Partial) => unknown): unknown => - selector(mocks.state as Partial) - useAppStore.getState = (): Partial => mocks.state as Partial - return { useAppStore } -}) - -vi.mock('@/store/repos/use-repository-catalog', () => ({ - useRepoMap: () => mocks.repoMap -})) -vi.mock('@/store/slices/worktrees/listing/use-worktree-catalog', () => ({ - useWorktreeMap: () => mocks.worktreeMap -})) - -vi.mock('@/runtime/rpc/calls/runtime-rpc-call', () => ({ - callRuntimeRpc: (...args: unknown[]) => mocks.callRuntimeRpc(...args) -})) -vi.mock('@/runtime/rpc/compatibility/runtime-compatibility-probe', () => ({ - getRuntimeEnvironmentStatus: (...args: unknown[]) => mocks.getRuntimeEnvironmentStatus(...args) -})) - -vi.mock('sonner', () => ({ - toast: { - error: (...args: unknown[]) => mocks.toastError(...args), - success: (...args: unknown[]) => mocks.toastSuccess(...args), - message: (...args: unknown[]) => mocks.toastMessage(...args) - } -})) - -vi.mock('@/lib/window-activity/window-visibility-interval', () => ({ - installWindowVisibilityInterval: () => () => undefined -})) - -vi.mock('@/components/contextual-tours/use-contextual-tour', () => ({ - useContextualTour: () => undefined -})) - -vi.mock('@/components/ui/tooltip', () => ({ - TooltipProvider: ({ children }: { children: ReactNode }) => <>{children}, - Tooltip: ({ children }: { children: ReactNode }) => <>{children}, - TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children}, - TooltipContent: () => null -})) - -vi.mock('@/runtime/repo-hooks/runtime-hooks-client', () => ({ - checkRuntimeHooks: vi.fn(async () => ({ hooks: null, ok: true })) -})) - -vi.mock('@/runtime/repo-hooks/ensure-hooks-confirmed', () => ({ - ensureHooksConfirmed: vi.fn(async () => 'inherit'), - ensureHooksApproved: vi.fn(async () => ({ decision: 'run' })) -})) - -vi.mock('@/lib/workspace/activation/worktree-activation', () => ({ - activateAndRevealWorktree: vi.fn() -})) - -vi.mock('./listing/AutomationsListPanel', () => ({ - AutomationsListPanel: (props: ListPanelProps) => { - const selectAutomationRow = (rowKey: string | null): void => { - props.selectAutomationRow(rowKey) - props.onOpenDetail() - } - mocks.listPanel = { ...props, selectAutomationRow } - return ( -
- - {props.sortedListItems.map((item) => - item.kind === 'local' ? ( - - ) : ( - - ) - )} - {props.hasListItems ? null :
} -
- ) - } -})) - -vi.mock('./details/AutomationsDetailPane', () => ({ - AutomationsDetailPane: (props: DetailPaneProps) => { - mocks.detailPane = props - return ( -
- {props.selected?.name ?? 'none'} - {props.selectedRuns.length} -
- ) - } -})) - -vi.mock('./authoring/AutomationEditorDialog', () => ({ - AutomationEditorDialog: (props: EditorDialogProps) => { - mocks.editorDialog = props - return ( -
- {props.open ? 'open' : 'closed'} - {props.open && props.editDestination ? ( - - - - - ) : null} -
- ) - } -})) - -vi.mock('./authoring/AutomationDeleteDialogs', () => ({ - AutomationDeleteDialog: (props: DeleteDialogProps) => { - mocks.deleteDialog = props - return null - }, - ExternalAutomationDeleteDialog: () => null -})) - -import AutomationsPage from './AutomationsPage' -import { - makeAutomation, - makeRun, - makeStoreState, - type AutomationsPageFixtureState, - type DetailPaneProps -} from './automations-page-fixtures' - -function mockGroup(...names: K[]): Record { - return Object.fromEntries(names.map((name) => [name, vi.fn()])) as Record -} - -export const api = { - automations: mockGroup( - 'list', - 'listRuns', - 'create', - 'update', - 'delete', - 'runNow', - 'listScoped', - 'listExternalManagerForOwner', - 'runExternalActionForOwner', - 'retainExternalScopes' - ), - ssh: mockGroup('listTargets', 'connect'), - runtimeEnvironments: mockGroup('connect'), - ui: mockGroup('get') -} - -/** The precondition a Desktop + Self row captures; every fenced request repeats it. */ -export const SELF_PRECONDITION = { selector: { kind: 'self' } } -/** The owner the catalog projects for Desktop + Self, which every scoped call names. */ -export const DESKTOP_SELF_OWNER = { authority: { kind: 'desktop' }, selector: { kind: 'self' } } - -export const RUNTIME_ID = 'gpu' -/** A runtime that advertises both automation capabilities, so its rows carry owners. */ -const RUNTIME_CAPABILITIES = [LIST_HOST_SCOPE, OWNER_FENCING, CREATE_IDEMPOTENCY] -export const RUNTIME_SELF_FILTER = { - kind: 'host' as const, - host: { - authority: { kind: 'runtime' as const, environmentId: RUNTIME_ID }, - selector: { kind: 'self' as const } - } -} - -/** Serve desktop-local RPCs from the programmable API mocks and runtime-target RPCs from runtimeHost state. */ -async function answerAutomationRpc( - target: unknown, - method: string, - params?: unknown -): Promise { - if ((target as { kind?: string } | null)?.kind === 'environment') { - const answers = mocks.state.runtimeAnswers as - | { automations: Automation[]; runs: AutomationRun[] } - | undefined - if (method === 'automation.list') { - return selfScopedList(answers?.automations ?? []) - } - return method === 'automation.runs' ? { runs: answers?.runs ?? [] } : {} - } - const args = params as Record | undefined - switch (method) { - case 'automation.list': - return args?.selector - ? await api.automations.listScoped({ selector: args.selector }) - : { automations: await api.automations.list() } - case 'automation.runs': - return { runs: await api.automations.listRuns(args) } - case 'automation.create': - return { automation: await api.automations.create(args) } - case 'automation.update': - return { automation: await api.automations.update(args) } - case 'automation.delete': - return await api.automations.delete(args).then(() => ({})) - case 'automation.runNow': - return { run: await api.automations.runNow(args) } - default: - return {} - } -} - -/** Puts a reachable, capable second authority in the catalog and answers for it. */ -export function runtimeHost(automations: Automation[], runs: AutomationRun[]): void { - mocks.state.runtimeEnvironments = [ - { id: RUNTIME_ID, name: 'GPU box', createdAt: 1, pairingRevision: 4 } - ] - mocks.state.runtimeStatusByEnvironmentId = new Map([ - [RUNTIME_ID, { status: { capabilities: RUNTIME_CAPABILITIES } }] - ]) - mocks.getRuntimeEnvironmentStatus.mockResolvedValue({ capabilities: RUNTIME_CAPABILITIES }) - mocks.state.runtimeAnswers = { automations, runs } -} - -export const addRuntimeProject = (): void => addRuntimeProjectFixture(mocks, RUNTIME_ID) - -/** Answers the host-scoped read, which is where the list's rows and owners come from. */ -export function scopedList(automations: Automation[]): void { - api.automations.listScoped.mockResolvedValue(selfScopedList(automations)) -} - -const roots: Root[] = [] - -export async function renderPage(options?: { strict?: boolean }): Promise<{ - container: HTMLDivElement - rerender: () => Promise -}> { - const container = document.createElement('div') - document.body.appendChild(container) - const root = createRoot(container) - roots.push(root) - const rerender = async (): Promise => { - await act(async () => { - // StrictMode doubles effects to expose dispose-without-revive controller bugs. - const page = options?.strict - ? createElement(StrictMode, null, createElement(AutomationsPage)) - : createElement(AutomationsPage) - root.render(page) - }) - } - await rerender() - return { container, rerender } -} - -/** Lets the per-host queries, their capability probes, and the history read settle. */ -export async function settleHostQueries(): Promise { - await act(async () => { - await new Promise((resolve) => setTimeout(resolve, 0)) - }) -} - -/** The alt-tab-back trigger: one of the two events bound to the page refresh. */ -export async function refreshOnFocus(): Promise { - await act(async () => { - window.dispatchEvent(new Event('focus')) - await new Promise((resolve) => setTimeout(resolve, 0)) - }) -} - -export function rows(container: HTMLElement, testId: string): string[] { - return [...container.querySelectorAll(`[data-testid="${testId}"]`)].map( - (node) => node.textContent ?? '' - ) -} - -/** Registers the per-case store reset and renderer cleanup for the current suite. */ -export function installAutomationsPageHarness(): void { - beforeEach(() => { - globalThis.IS_REACT_ACT_ENVIRONMENT = true - vi.clearAllMocks() - setLocalRuntimeCapabilitiesForTests([]) - // Confirmed capabilities are module-level and must not leak between tests. - resetAutomationCapabilityProbes() - resetHostStructuredAgentsForTests() - // A prior test's wholesale mockImplementation must not leak forward. - mocks.callRuntimeRpc.mockReset() - mocks.callRuntimeRpc.mockImplementation(answerAutomationRpc) - mocks.listPanel = null - mocks.detailPane = null - mocks.editorDialog = null - mocks.deleteDialog = null - const fixtures = makeStoreState() - mocks.state = fixtures.state - mocks.state.folderWorkspaces = [] - mocks.state.setAutomationHostFilter = mocks.setAutomationHostFilter - mocks.repoMap = fixtures.repoMap - mocks.worktreeMap = fixtures.worktreeMap - api.automations.list.mockResolvedValue([makeAutomation()]) - api.automations.listRuns.mockResolvedValue([]) - api.automations.listExternalManagerForOwner.mockResolvedValue({ - manager: null, - error: null, - updatedAt: 0 - }) - api.automations.retainExternalScopes.mockResolvedValue(undefined) - api.automations.listScoped.mockResolvedValue(selfScopedList([makeAutomation()])) - api.automations.create.mockResolvedValue(makeAutomation()) - api.automations.update.mockResolvedValue(makeAutomation()) - api.automations.delete.mockResolvedValue(undefined) - api.automations.runNow.mockResolvedValue(makeRun()) - api.ui.get.mockResolvedValue({}) - // @ts-expect-error test window mock - globalThis.window.api = api - }) - - afterEach(async () => { - await act(async () => { - roots.splice(0).forEach((root) => root.unmount()) - }) - document.body.innerHTML = '' - setLocalRuntimeCapabilitiesForTests(null) - }) -} diff --git a/src/shared/__fixtures__/grok-inline-startup-pty-trace.ts b/src/shared/__fixtures__/grok-inline-startup-pty-trace.ts deleted file mode 100644 index 97846def799..00000000000 --- a/src/shared/__fixtures__/grok-inline-startup-pty-trace.ts +++ /dev/null @@ -1,124 +0,0 @@ -import type { GrokStartupTraceChunk } from './grok-startup-pty-trace' - -/** - * Recorded PTY output of `grok --no-alt-screen`, grok's inline (non - * alternate-screen) rendering mode — also reachable via `--minimal` and - * `[ui] screen_mode = "minimal"` in ~/.grok/config.toml. - * - * Recorded from: grok 1.0.0 (3cd0d0cbcebe) [stable] on macOS (darwin 25.3.0), - * xterm-256color 120x30 via node-pty, cwd = an Orca git worktree, 10s of output - * from spawn. - * - * This mode emits NO alternate-screen switch, so the composer-glyph marker never - * anchors and the 2004-anchored quiet window is the only delivery path. Elision - * follows the alt-screen fixture: entries without `data` are marker-free render - * frames kept only as timestamp + byte count. - */ -export const GROK_INLINE_STARTUP_PTY_TRACE: GrokStartupTraceChunk[] = [ - { - t: 279, - data: '\u001b]0;grok\u0007\u001b[?1000h\u001b[?1002h\u001b[?1003h\u001b[?1015h\u001b[?1006h\u001b[?1004h\u001b[?2004h\u001b[?25l\u001b]12;rgb:c8/c8' - }, - { t: 279, bytes: 8 }, - { t: 2280, bytes: 11 }, - { t: 2587, bytes: 5 }, - { t: 2612, bytes: 825 }, - { t: 2612, bytes: 925 }, - { t: 2612, bytes: 928 }, - { t: 2612, bytes: 958 }, - { t: 2612, bytes: 810 }, - { t: 2613, bytes: 1000 }, - { - t: 2613, - data: ';80;80;88;48;2;20;20;20m╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\u001b[39;48;2;20;20;20m \u001b[26;1H \u001b[38;2;80;80;88;48;2;20;20;20m│\u001b[38;2;225;225;225;48;2;20;20;20m \u001b[38;2;200;200;200;48;2;20;20;20m❯ \u001b[38;2;225;225;225;48;2;20;20;20m \u001b[38;2;80;80;88;48;2;20;20;20m│\u001b[39;48;2;20;20;20m \u001b[27;1H \u001b[38;2;80;80;88;48;2;20;20;20m╰─────────────────────────────────────────────────────────────────────────────── \u001b[38;2;128;128;128;48;2;20;20;20mGrok 4.' - }, - { - t: 2613, - data: '5 (high)\u001b[38;2;88;88;88;48;2;20;20;20m · \u001b[38;2;108;108;108;48;2;20;20;20malways-approve\u001b[38;2;80;80;88;48;2;20;20;20m ─╯\u001b[39;48;2;20;20;20m \u001b[28;1H \u001b[29;1H \u001b[38;2;108;108;108;48;2;20;20;20m[stable]\u001b[39;48;2;20;20;20m \u001b[30;1H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[26;7H\u001b[?25h\u001b[?2026l' - }, - { t: 2614, bytes: 169 }, - { t: 2676, bytes: 158 }, - { t: 2697, bytes: 158 }, - { t: 2772, bytes: 586 }, - { t: 2781, bytes: 410 }, - { t: 2866, bytes: 971 }, - { t: 2866, bytes: 69 }, - { t: 2878, bytes: 970 }, - { t: 2878, bytes: 308 }, - { t: 2951, bytes: 970 }, - { t: 2951, bytes: 784 }, - { t: 3036, bytes: 970 }, - { t: 3036, bytes: 968 }, - { t: 3036, bytes: 417 }, - { t: 3121, bytes: 948 }, - { t: 3121, bytes: 954 }, - { t: 3121, bytes: 792 }, - { t: 3207, bytes: 966 }, - { t: 3207, bytes: 964 }, - { t: 3207, bytes: 837 }, - { t: 3291, bytes: 970 }, - { t: 3291, bytes: 966 }, - { t: 3291, bytes: 956 }, - { t: 3291, bytes: 195 }, - { t: 3376, bytes: 968 }, - { t: 3376, bytes: 962 }, - { t: 3376, bytes: 785 }, - { t: 3461, bytes: 944 }, - { t: 3461, bytes: 912 }, - { t: 3461, bytes: 79 }, - { t: 3546, bytes: 962 }, - { t: 3546, bytes: 568 }, - { t: 3630, bytes: 839 }, - { t: 3630, bytes: 40 }, - { t: 3715, bytes: 424 }, - { t: 3800, bytes: 168 }, - { t: 3885, bytes: 218 }, - { t: 4139, bytes: 219 }, - { t: 4394, bytes: 219 }, - { t: 4732, bytes: 219 }, - { t: 5582, bytes: 219 }, - { t: 5922, bytes: 219 }, - { t: 6177, bytes: 219 }, - { t: 6432, bytes: 219 }, - { t: 6602, bytes: 219 }, - { t: 6686, bytes: 158 }, - { t: 6771, bytes: 410 }, - { t: 6857, bytes: 878 }, - { t: 6857, bytes: 463 }, - { t: 6941, bytes: 970 }, - { t: 6941, bytes: 718 }, - { t: 7026, bytes: 970 }, - { t: 7026, bytes: 968 }, - { t: 7026, bytes: 292 }, - { t: 7110, bytes: 964 }, - { t: 7110, bytes: 962 }, - { t: 7110, bytes: 549 }, - { t: 7195, bytes: 964 }, - { t: 7195, bytes: 969 }, - { t: 7195, bytes: 967 }, - { t: 7195, bytes: 306 }, - { t: 7279, bytes: 970 }, - { t: 7279, bytes: 968 }, - { t: 7279, bytes: 961 }, - { t: 7279, bytes: 103 }, - { t: 7364, bytes: 968 }, - { t: 7364, bytes: 956 }, - { t: 7364, bytes: 795 }, - { t: 7449, bytes: 964 }, - { t: 7450, bytes: 962 }, - { t: 7450, bytes: 316 }, - { t: 7535, bytes: 954 }, - { t: 7535, bytes: 550 }, - { t: 7621, bytes: 960 }, - { t: 7621, bytes: 94 }, - { t: 7704, bytes: 438 }, - { t: 7789, bytes: 168 }, - { t: 7874, bytes: 83 }, - { t: 8043, bytes: 219 }, - { t: 8381, bytes: 219 }, - { t: 8635, bytes: 219 }, - { t: 8890, bytes: 219 }, - { t: 9144, bytes: 219 }, - { t: 9398, bytes: 219 }, - { t: 9738, bytes: 219 } -] diff --git a/src/shared/__fixtures__/grok-startup-pty-trace.ts b/src/shared/__fixtures__/grok-startup-pty-trace.ts deleted file mode 100644 index c8b23c0d5ad..00000000000 --- a/src/shared/__fixtures__/grok-startup-pty-trace.ts +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Recorded PTY output of a `grok` launch, used to pin when the draft-paste - * readiness scanner would deliver a launch draft. - * - * Recorded from: grok 1.0.0 (3cd0d0cbcebe) [stable] on macOS (darwin 25.3.0), - * xterm-256color 120x30 via node-pty, cwd = an Orca git worktree, 10s of output - * from spawn. - * - * Entries without `data` are grok logo-shimmer render frames, verified to hold - * none of DECSET 1049h / 2004h, DECTCEM show-cursor, or the composer glyphs - * U+276F / U+203A. Only their timestamp and byte count are kept so the fixture - * stays small; the replay substitutes filler of the recorded length. Timings are - * what the scanner reacts to, so they are preserved verbatim. - */ -export type GrokStartupTraceChunk = { - /** ms after spawn */ - t: number - data?: string - /** Length of an elided, marker-free animation frame. */ - bytes?: number -} - -export const GROK_STARTUP_PTY_TRACE: GrokStartupTraceChunk[] = [ - { - t: 274, - data: '\u001b]0;grok\u0007\u001b[?1049h\u001b[?1000h\u001b[?1002h\u001b[?1003h\u001b[?1015h\u001b[?1006h\u001b[?1004h\u001b[?2004h\u001b[?25l' - }, - { t: 274, bytes: 18 }, - { t: 582, bytes: 5 }, - { t: 607, bytes: 825 }, - { t: 607, bytes: 925 }, - { t: 607, bytes: 928 }, - { t: 607, bytes: 958 }, - { t: 607, bytes: 810 }, - { t: 607, bytes: 1000 }, - { - t: 607, - data: ';80;80;88;48;2;20;20;20m╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\u001b[39;48;2;20;20;20m \u001b[26;1H \u001b[38;2;80;80;88;48;2;20;20;20m│\u001b[38;2;225;225;225;48;2;20;20;20m \u001b[38;2;200;200;200;48;2;20;20;20m❯ \u001b[38;2;225;225;225;48;2;20;20;20m \u001b[38;2;80;80;88;48;2;20;20;20m│\u001b[39;48;2;20;20;20m \u001b[27;1H \u001b[38;2;80;80;88;48;2;20;20;20m╰─────────────────────────────────────────────────────────────────────────────── \u001b[38;2;128;128;128;48;2;20;20;20mGrok 4.' - }, - { - t: 607, - data: '5 (high)\u001b[38;2;88;88;88;48;2;20;20;20m · \u001b[38;2;108;108;108;48;2;20;20;20malways-approve\u001b[38;2;80;80;88;48;2;20;20;20m ─╯\u001b[39;48;2;20;20;20m \u001b[28;1H \u001b[29;1H \u001b[38;2;108;108;108;48;2;20;20;20m[stable]\u001b[39;48;2;20;20;20m \u001b[30;1H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[26;7H\u001b[?25h\u001b[?2026l' - }, - { t: 613, bytes: 169 }, - { t: 664, bytes: 158 }, - { t: 691, bytes: 158 }, - { t: 762, bytes: 410 }, - { t: 776, bytes: 586 }, - { t: 861, bytes: 971 }, - { t: 861, bytes: 69 }, - { t: 945, bytes: 970 }, - { t: 945, bytes: 784 }, - { t: 1031, bytes: 970 }, - { t: 1031, bytes: 968 }, - { t: 1031, bytes: 417 }, - { t: 1109, bytes: 948 }, - { t: 1110, bytes: 954 }, - { t: 1110, bytes: 792 }, - { t: 1115, bytes: 972 }, - { t: 1115, bytes: 968 }, - { t: 1115, bytes: 336 }, - { t: 1200, bytes: 964 }, - { t: 1200, bytes: 964 }, - { t: 1200, bytes: 962 }, - { t: 1200, bytes: 62 }, - { t: 1286, bytes: 970 }, - { t: 1286, bytes: 966 }, - { t: 1286, bytes: 956 }, - { t: 1286, bytes: 195 }, - { t: 1370, bytes: 968 }, - { t: 1370, bytes: 962 }, - { t: 1370, bytes: 785 }, - { t: 1455, bytes: 944 }, - { t: 1455, bytes: 912 }, - { t: 1455, bytes: 79 }, - { t: 1540, bytes: 962 }, - { t: 1540, bytes: 568 }, - { t: 1625, bytes: 839 }, - { t: 1625, bytes: 40 }, - { t: 1710, bytes: 424 }, - { t: 1795, bytes: 168 }, - { t: 1879, bytes: 218 }, - { t: 2134, bytes: 219 }, - { t: 2389, bytes: 219 }, - { t: 2728, bytes: 219 }, - { t: 3579, bytes: 219 }, - { t: 3918, bytes: 219 }, - { t: 4172, bytes: 219 }, - { t: 4425, bytes: 219 }, - { t: 4594, bytes: 219 }, - { t: 4680, bytes: 158 }, - { t: 4764, bytes: 410 }, - { t: 4850, bytes: 878 }, - { t: 4850, bytes: 463 }, - { t: 4934, bytes: 970 }, - { t: 4934, bytes: 718 }, - { t: 5019, bytes: 970 }, - { t: 5019, bytes: 968 }, - { t: 5019, bytes: 292 }, - { t: 5103, bytes: 964 }, - { t: 5103, bytes: 962 }, - { t: 5103, bytes: 549 }, - { t: 5188, bytes: 964 }, - { t: 5188, bytes: 969 }, - { t: 5188, bytes: 967 }, - { t: 5188, bytes: 306 }, - { t: 5272, bytes: 970 }, - { t: 5272, bytes: 968 }, - { t: 5272, bytes: 961 }, - { t: 5272, bytes: 103 }, - { t: 5358, bytes: 968 }, - { t: 5358, bytes: 956 }, - { t: 5358, bytes: 795 }, - { t: 5442, bytes: 964 }, - { t: 5442, bytes: 962 }, - { t: 5442, bytes: 316 }, - { t: 5527, bytes: 954 }, - { t: 5527, bytes: 550 }, - { t: 5612, bytes: 960 }, - { t: 5612, bytes: 94 }, - { t: 5696, bytes: 438 }, - { t: 5781, bytes: 168 }, - { t: 5866, bytes: 83 }, - { t: 6120, bytes: 219 }, - { t: 6375, bytes: 219 }, - { t: 6631, bytes: 219 }, - { t: 6884, bytes: 219 }, - { t: 7139, bytes: 219 }, - { t: 7393, bytes: 219 }, - { t: 7732, bytes: 219 }, - { t: 8580, bytes: 219 }, - { t: 8664, bytes: 158 }, - { t: 8750, bytes: 410 }, - { t: 8835, bytes: 796 }, - { t: 8919, bytes: 908 }, - { t: 8919, bytes: 861 }, - { t: 9004, bytes: 966 }, - { t: 9004, bytes: 894 }, - { t: 9090, bytes: 970 }, - { t: 9090, bytes: 968 }, - { t: 9090, bytes: 768 }, - { t: 9175, bytes: 964 }, - { t: 9175, bytes: 969 }, - { t: 9175, bytes: 967 }, - { t: 9175, bytes: 306 }, - { t: 9260, bytes: 970 }, - { t: 9260, bytes: 968 }, - { t: 9260, bytes: 958 }, - { t: 9260, bytes: 291 }, - { t: 9344, bytes: 960 }, - { t: 9344, bytes: 956 }, - { t: 9344, bytes: 605 }, - { t: 9430, bytes: 952 }, - { t: 9430, bytes: 936 }, - { t: 9430, bytes: 377 }, - { t: 9515, bytes: 960 }, - { t: 9515, bytes: 775 }, - { t: 9600, bytes: 914 }, - { t: 9600, bytes: 427 }, - { t: 9684, bytes: 618 }, - { t: 9770, bytes: 180 }, - { t: 9855, bytes: 219 } -] diff --git a/src/shared/bundled-skill-summary.test.ts b/src/shared/bundled-skill-summary.test.ts deleted file mode 100644 index 8b40536315d..00000000000 --- a/src/shared/bundled-skill-summary.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { BundledSkillOp, BundledSkillPlan } from './bundled-skill-plan' -import { bundledSkillNudgeKey, summarizeBundledSkillPlan } from './bundled-skill-summary' - -const folder = { kind: 'folder', entries: ['SKILL.md:file'], skillDigest: 'a'.repeat(64) } as const - -function op(fields: Partial): BundledSkillOp { - return { - kind: 'keep', - path: '/home/me/.agents/skills/x', - role: 'shared', - agents: [], - observed: folder, - cleanup: [], - ...fields - } -} - -/** A plan as a paired server returns it: planned on the server's disk against the server's own bundle. */ -const serverPlan = (ops: BundledSkillOp[]): BundledSkillPlan => ({ - scope: 'global', - homeDirectory: '/home/me', - skills: [{ name: 'orca-cli', digest: 'b'.repeat(64), ops }], - planDigest: 'c'.repeat(64) -}) - -describe('another Orca build’s copy in a planned host’s plan', () => { - it('reads as current and is never offered as an update', () => { - const plan = serverPlan([ - op({ kind: 'leave', reason: 'other-build', replaceable: true }), - op({ - path: '/home/me/.claude/skills/x', - role: 'agent', - observed: { kind: 'link', target: 'x' } - }) - ]) - - expect(summarizeBundledSkillPlan(plan)).toMatchObject({ - badge: null, - hasWork: false, - skills: [{ name: 'orca-cli', state: 'current' }] - }) - expect(bundledSkillNudgeKey('runtime:server-1', plan)).toBeNull() - }) -}) diff --git a/src/shared/claimed-agent-pty-owner.test.ts b/src/shared/claimed-agent-pty-owner.test.ts index b68e5bb8554..fef6c28b025 100644 --- a/src/shared/claimed-agent-pty-owner.test.ts +++ b/src/shared/claimed-agent-pty-owner.test.ts @@ -1,12 +1,9 @@ -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it } from 'vitest' import type { AgentSessionExecutionClaim, AgentSessionSurfaceBinding } from './agent-session/agent-session-host-authority' -import { - ClaimedAgentPtyOwnerRegistry, - MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES -} from './claimed-agent-pty-owner' +import { ClaimedAgentPtyOwnerRegistry } from './claimed-agent-pty-owner' function claim( identityDigest = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', @@ -49,25 +46,7 @@ describe('ClaimedAgentPtyOwnerRegistry', () => { ).rejects.toThrow('agent_session_conflict') }) - it('does not find an owner through another worktree scope', async () => { - const registry = new ClaimedAgentPtyOwnerRegistry() - await registry.ensure({ - claim: claim(), - surface, - spawn: async () => ({ ptyId: 'pty-1' }) - }) - - expect( - registry.find( - claim( - 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', - 'ccccccccccccccccccccccccccccccccccccccccccc' - ) - ) - ).toBeNull() - }) - - it('generation-guards release across a replacement owner', async () => { + it('ignores a stale release generation and releases the matching owner', async () => { const registry = new ClaimedAgentPtyOwnerRegistry() const first = await registry.ensure({ claim: claim(), @@ -76,7 +55,7 @@ describe('ClaimedAgentPtyOwnerRegistry', () => { }) registry.release('pty-1', 'stale-generation') - expect(registry.find(claim())?.generation).toBe(first.owner.generation) + expect(registry.find(claim())).toMatchObject({ ptyId: 'pty-1' }) registry.release('pty-1', first.owner.generation) expect(registry.find(claim())).toBeNull() @@ -134,70 +113,6 @@ describe('ClaimedAgentPtyOwnerRegistry', () => { }) }) - it('rejects a recovered owner that reuses only one half of its generation identity', () => { - const registry = new ClaimedAgentPtyOwnerRegistry() - const owner = { - claim: claim(), - generation: 'generation-1', - phase: 'live' as const, - ptyId: 'pty-1', - surface - } - registry.register(owner) - - expect(() => registry.register({ ...owner, ptyId: 'pty-2' })).toThrow('agent_session_conflict') - expect(() => registry.register({ ...owner, generation: 'generation-2' })).toThrow( - 'agent_session_conflict' - ) - }) - - it('retains only allowlisted owner fields', () => { - const registry = new ClaimedAgentPtyOwnerRegistry() - const owner = { - claim: { ...claim(), unknownPayload: 'claim payload' }, - generation: 'generation-1', - phase: 'live' as const, - ptyId: 'pty-1', - surface: { ...surface, unknownPayload: 'surface payload' }, - unknownPayload: 'owner payload' - } - - registry.register(owner) - - expect(registry.list()).toEqual([ - { - claim: claim(), - generation: 'generation-1', - phase: 'live', - ptyId: 'pty-1', - surface - } - ]) - }) - - it('fails closed when recovered owner evidence reaches the process-wide cap', () => { - const registry = new ClaimedAgentPtyOwnerRegistry() - const owners = Array.from({ length: MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES }, (_, index) => ({ - claim: claim(`identity-${index}`), - generation: `generation-${index}`, - phase: 'live' as const, - ptyId: `pty-${index}`, - surface - })) - registry.reconcileAuthoritative(owners) - - expect(() => - registry.register({ - claim: claim('one-more-identity'), - generation: 'one-more-generation', - phase: 'live', - ptyId: 'one-more-pty', - surface - }) - ).toThrow('execution_owner_unavailable') - expect(registry.list()).toHaveLength(MAX_CLAIMED_AGENT_PTY_OWNER_ENTRIES) - }) - it('atomically converges from conflicting provider evidence to one owner', async () => { const registry = new ClaimedAgentPtyOwnerRegistry() const ownerA = { @@ -226,7 +141,10 @@ describe('ClaimedAgentPtyOwnerRegistry', () => { spawn: async () => ({ ptyId: 'unexpected' }), isLive: (owner) => owner.generation === ownerB.generation }) - ).resolves.toMatchObject({ disposition: 'adopted', owner: ownerB }) + ).resolves.toMatchObject({ + disposition: 'adopted', + owner: { ptyId: 'pty-b', generation: 'generation-b' } + }) }) it('prunes an advertised generation when an authoritative snapshot omits it', async () => { @@ -241,12 +159,11 @@ describe('ClaimedAgentPtyOwnerRegistry', () => { registry.reconcileAuthoritative([recovered]) registry.reconcileAuthoritative([]) - const spawn = vi.fn(async () => ({ ptyId: 'pty-new' })) + const spawn = async () => ({ ptyId: 'pty-new' }) await expect(registry.ensure({ claim: claim(), surface, spawn })).resolves.toMatchObject({ disposition: 'created', owner: { ptyId: 'pty-new' } }) - expect(spawn).toHaveBeenCalledOnce() }) it('replaces a reused PTY id with the exact newly advertised generation', async () => { @@ -261,7 +178,7 @@ describe('ClaimedAgentPtyOwnerRegistry', () => { const newOwner = { ...oldOwner, generation: 'generation-new' } registry.reconcileAuthoritative([oldOwner]) registry.reconcileAuthoritative([newOwner]) - const isLive = vi.fn((owner: typeof newOwner) => owner.generation === 'generation-new') + const isLive = (owner: typeof newOwner) => owner.generation === 'generation-new' await expect( registry.ensure({ @@ -270,27 +187,32 @@ describe('ClaimedAgentPtyOwnerRegistry', () => { spawn: async () => ({ ptyId: 'unexpected' }), isLive }) - ).resolves.toMatchObject({ disposition: 'adopted', owner: newOwner }) - expect(isLive).toHaveBeenCalledWith(expect.objectContaining({ generation: 'generation-new' })) + ).resolves.toMatchObject({ + disposition: 'adopted', + owner: { ptyId: 'pty-reused', generation: 'generation-new' } + }) }) it('does not let reconciliation erase an in-flight reservation', async () => { const registry = new ClaimedAgentPtyOwnerRegistry() let finish!: (result: { ptyId: string }) => void - const spawn = vi.fn( - () => - new Promise<{ ptyId: string }>((resolve) => { - finish = resolve - }) - ) + const spawn = () => + new Promise<{ ptyId: string }>((resolve) => { + finish = resolve + }) const first = registry.ensure({ claim: claim(), surface, spawn }) registry.reconcileAuthoritative([]) const second = registry.ensure({ claim: claim(), surface, spawn }) finish({ ptyId: 'pty-reserved' }) - await expect(first).resolves.toMatchObject({ disposition: 'created' }) - await expect(second).resolves.toMatchObject({ disposition: 'adopted' }) - expect(spawn).toHaveBeenCalledOnce() + await expect(first).resolves.toMatchObject({ + disposition: 'created', + owner: { ptyId: 'pty-reserved' } + }) + await expect(second).resolves.toMatchObject({ + disposition: 'adopted', + owner: { ptyId: 'pty-reserved' } + }) }) }) diff --git a/src/shared/claude-model-list-probe.test.ts b/src/shared/claude-model-list-probe.test.ts index eaea828e014..db107053d41 100644 --- a/src/shared/claude-model-list-probe.test.ts +++ b/src/shared/claude-model-list-probe.test.ts @@ -13,35 +13,9 @@ function controlResponseLine(models: unknown[]): string { } describe('parseClaudeModelList', () => { - it('skips init noise, CRLF endings, and duplicate values', () => { - const stdout = - '{"type":"system","subtype":"init","model":"claude-sonnet-5"}\r\n' + - 'not json at all\r\n' + - `${controlResponseLine([ - { value: 'sonnet', displayName: 'Sonnet' }, - { value: 'sonnet', displayName: 'Sonnet (duplicate)' }, - { value: ' ', displayName: 'Blank' } - ])}\r\n` - expect(parseClaudeModelList(stdout)).toEqual([ - { id: 'sonnet', label: 'Sonnet', effortLevels: [], supportsFastMode: false } - ]) - }) - it('drops the disabled placeholder row the CLI advertises for a model it cannot run', () => { - // Captured from `claude` 2.1.237: Fable 5.1 is announced but gated on 2.1.255+. const parsed = parseClaudeModelList( - controlResponseLine([ - { value: 'sonnet', displayName: 'Sonnet' }, - { - value: 'cc-update-required-1', - resolvedModel: 'cc-update-required-1', - displayName: 'Fable 5.1 (disabled)', - description: 'Update to 2.1.255+ to use Fable 5.1', - supportsEffort: true, - supportedEffortLevels: ['low', 'medium', 'high', 'xhigh', 'max'], - disabled: true - } - ]) + controlResponseLine([{ value: 'sonnet' }, { value: 'disabled-model', disabled: true }]) ) expect(parsed.map(({ id }) => id)).toEqual(['sonnet']) }) diff --git a/src/shared/codex-subagent-transcript.test.ts b/src/shared/codex-subagent-transcript.test.ts index e9a32e51af2..4604e638382 100644 --- a/src/shared/codex-subagent-transcript.test.ts +++ b/src/shared/codex-subagent-transcript.test.ts @@ -1,20 +1,13 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' +import { afterEach, describe, expect, it } from 'vitest' import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { createCodexSubagentTranscriptState, - hasTrackedCodexTranscriptSubagents, reconcileCodexSubagentTranscript } from './codex-subagent-transcript' -import { - codexRosterToSnapshots, - queueCodexSubagentIdentity, - retryPendingCodexSubagentIdentities, - upsertCodexSubagent, - type CodexSubagentRoster -} from './codex-subagent-roster' +import { codexRosterToSnapshots, type CodexSubagentRoster } from './codex-subagent-roster' const CHILD_ID = '019fa65f-3144-7151-9c02-cff7a28f316f' @@ -52,47 +45,10 @@ describe('Codex subagent transcript reconciliation', () => { dirs.length = 0 }) - it('adds a child from the parent rollout and removes it after task completion', () => { - const dir = mkdtempSync(join(tmpdir(), 'codex-subagent-transcript-')) - dirs.push(dir) - const parentPath = join(dir, 'rollout-parent.jsonl') - const childPath = join(dir, `rollout-child-${CHILD_ID}.jsonl`) - writeFileSync(parentPath, jsonl([activity('started')])) - writeFileSync(childPath, jsonl([{ type: 'event_msg', payload: { type: 'task_started' } }])) - const state = createCodexSubagentTranscriptState() - const roster: CodexSubagentRoster = new Map() - - reconcileCodexSubagentTranscript(state, roster, parentPath) - - expect(hasTrackedCodexTranscriptSubagents(state)).toBe(true) - expect(codexRosterToSnapshots(roster)).toEqual([ - { - id: CHILD_ID, - description: 'sidebar_repro', - state: 'working', - startedAt: 1234, - agentType: undefined, - model: undefined - } - ]) - - writeFileSync( - childPath, - jsonl([ - { type: 'event_msg', payload: { type: 'task_started' } }, - { type: 'event_msg', payload: { type: 'task_complete' } } - ]) - ) - reconcileCodexSubagentTranscript(state, roster, parentPath) - - expect(hasTrackedCodexTranscriptSubagents(state)).toBe(false) - expect(codexRosterToSnapshots(roster)).toBeUndefined() - }) - it('resolves a child rollout filed under a later session day than the parent', () => { const root = mkdtempSync(join(tmpdir(), 'codex-subagent-transcript-')) dirs.push(root) - const childStartedAt = Date.now() + const childStartedAt = 1_767_225_600_000 const parentDir = dayDirectory(root, childStartedAt - 24 * 60 * 60 * 1000) const childDir = dayDirectory(root, childStartedAt) mkdirSync(parentDir, { recursive: true }) @@ -115,87 +71,7 @@ describe('Codex subagent transcript reconciliation', () => { reconcileCodexSubagentTranscript(state, roster, parentPath) // Why: only a cross-day lookup can observe the completion; the parent-directory scan never finds this file. - expect(roster.size).toBe(0) - expect(hasTrackedCodexTranscriptSubagents(state)).toBe(false) - }) - - it('retires a child whose rollout never becomes readable', () => { - vi.useFakeTimers() - try { - const dir = mkdtempSync(join(tmpdir(), 'codex-subagent-transcript-')) - dirs.push(dir) - const parentPath = join(dir, 'rollout-parent.jsonl') - writeFileSync(parentPath, jsonl([activity('started')])) - const state = createCodexSubagentTranscriptState() - const roster: CodexSubagentRoster = new Map() - - reconcileCodexSubagentTranscript(state, roster, parentPath) - expect(roster.size).toBe(1) - - // Why: within the grace window a slow-to-appear rollout must not drop a live child. - vi.advanceTimersByTime(30_000) - reconcileCodexSubagentTranscript(state, roster, parentPath) - expect(roster.size).toBe(1) - - vi.advanceTimersByTime(31_000) - reconcileCodexSubagentTranscript(state, roster, parentPath) - expect(roster.size).toBe(0) - expect(hasTrackedCodexTranscriptSubagents(state)).toBe(false) - } finally { - vi.useRealTimers() - } - }) - - it('names a hook-only worker child from its own rollout once the hook read missed it', () => { - const dir = mkdtempSync(join(tmpdir(), 'codex-subagent-transcript-')) - dirs.push(dir) - const parentPath = join(dir, 'rollout-parent.jsonl') - const childPath = join(dir, `rollout-child-${CHILD_ID}.jsonl`) - writeFileSync(parentPath, '') - const state = createCodexSubagentTranscriptState() - const roster: CodexSubagentRoster = new Map() - // SubagentStart carries only the role; a session_meta past 64 KiB (large tool lists) used to stay unread. - upsertCodexSubagent(roster, CHILD_ID, { agentType: 'worker', state: 'working' }, 1234) - writeFileSync( - childPath, - jsonl([ - { - type: 'session_meta', - payload: { - id: CHILD_ID, - base_instructions: { text: 'x'.repeat(100 * 1024) }, - source: { - subagent: { - thread_spawn: { agent_path: null, agent_nickname: 'Planck', agent_role: 'worker' } - } - } - } - } - ]) - ) - - reconcileCodexSubagentTranscript(state, roster, parentPath) - expect(retryPendingCodexSubagentIdentities(roster)).toBe(true) - expect(codexRosterToSnapshots(roster)).toEqual([ - expect.objectContaining({ id: CHILD_ID, agentType: 'worker', description: 'Planck' }) - ]) - }) - - it('keeps a task path the parent supplied over a queued header read', () => { - const dir = mkdtempSync(join(tmpdir(), 'codex-subagent-transcript-')) - dirs.push(dir) - const childPath = join(dir, `rollout-child-${CHILD_ID}.jsonl`) - writeFileSync( - childPath, - jsonl([{ type: 'session_meta', payload: { id: CHILD_ID, agent_nickname: 'Planck' } }]) - ) - const roster: CodexSubagentRoster = new Map() - upsertCodexSubagent(roster, CHILD_ID, { agentType: 'worker', state: 'working' }, 1234) - queueCodexSubagentIdentity(roster, CHILD_ID, childPath) - upsertCodexSubagent(roster, CHILD_ID, { description: 'pr_review', state: 'working' }, 1234) - - expect(retryPendingCodexSubagentIdentities(roster)).toBe(false) - expect(codexRosterToSnapshots(roster)?.[0]).toMatchObject({ description: 'pr_review' }) + expect(codexRosterToSnapshots(roster)).toBeUndefined() }) it('removes a child when Codex reports it interrupted', () => { @@ -210,8 +86,7 @@ describe('Codex subagent transcript reconciliation', () => { writeFileSync(parentPath, jsonl([activity('started'), activity('interrupted')])) reconcileCodexSubagentTranscript(state, roster, parentPath) - expect(hasTrackedCodexTranscriptSubagents(state)).toBe(false) - expect(roster.size).toBe(0) + expect(codexRosterToSnapshots(roster)).toBeUndefined() }) describe('child model identity', () => { @@ -237,17 +112,6 @@ describe('Codex subagent transcript reconciliation', () => { return { parentPath, childPath } } - it('reports the model from the child rollout, not the parent model', () => { - const { parentPath } = seedPair([started(), turnContext('gpt-5.6-terra')]) - const state = createCodexSubagentTranscriptState() - const roster: CodexSubagentRoster = new Map() - - reconcileCodexSubagentTranscript(state, roster, parentPath) - - // The parent runs sol; only the child's own turn_context may set its model. - expect(codexRosterToSnapshots(roster)?.[0]?.model).toBe('gpt-5.6-terra') - }) - it('keeps the discovered model when a later poll carries no turn_context', () => { const { parentPath, childPath } = seedPair([started(), turnContext('gpt-5.6-terra')]) const state = createCodexSubagentTranscriptState() @@ -293,8 +157,7 @@ describe('Codex subagent transcript reconciliation', () => { reconcileCodexSubagentTranscript(state, roster, parentPath) - expect(roster.size).toBe(0) - expect(hasTrackedCodexTranscriptSubagents(state)).toBe(false) + expect(codexRosterToSnapshots(roster)).toBeUndefined() }) }) }) diff --git a/src/shared/command-code-output-retention.test.ts b/src/shared/command-code-output-retention.test.ts deleted file mode 100644 index 681d12270e5..00000000000 --- a/src/shared/command-code-output-retention.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { createCommandCodeOutputStatusDetector } from './command-code-output-status' - -function heapAfterGc(): number { - if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { - throw new Error('The test runner must enable --expose-gc') - } - globalThis.gc() - globalThis.gc() - return process.memoryUsage().heapUsed -} - -describe('Command Code output retention', () => { - it('keeps small boundary carries without pinning oversized output on ordinary panes', () => { - const before = heapAfterGc() - const detectors = Array.from({ length: 8 }, (_value, index) => { - const detector = createCommandCodeOutputStatusDetector({ onWorking: () => {} }) - detector.observe(`${index}:${'x'.repeat(4 * 1024 * 1024)}`) - return detector - }) - expect(heapAfterGc() - before).toBeLessThan(2 * 1024 * 1024) - for (const detector of detectors) { - expect(detector.observe('\nordinary shell output\n')).toBe(false) - } - }) -}) diff --git a/src/shared/command-code-output-status.test.ts b/src/shared/command-code-output-status.test.ts index d2f80ebd304..06d68a0358b 100644 --- a/src/shared/command-code-output-status.test.ts +++ b/src/shared/command-code-output-status.test.ts @@ -1,37 +1,7 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { createCommandCodeOutputStatusDetector } from './command-code-output-status' -afterEach(() => { - vi.restoreAllMocks() -}) - describe('createCommandCodeOutputStatusDetector', () => { - it('marks Command Code working with the submitted prompt when the TUI starts thinking', () => { - const onWorking = vi.fn() - const detector = createCommandCodeOutputStatusDetector({ - startupCommand: 'command-code --trust', - onWorking - }) - - expect(detector.observe('❯ Fix the yellow spinner\r\n\x1b[35m✻ Thinking...\x1b[0m')).toBe(true) - - expect(onWorking).toHaveBeenCalledWith('Fix the yellow spinner') - }) - - it('waits for the Command Code banner before trusting generic status text', () => { - const onWorking = vi.fn() - const detector = createCommandCodeOutputStatusDetector({ - startupCommand: null, - onWorking - }) - - expect(detector.observe('Thinking about unrelated shell output')).toBe(false) - expect(detector.observe('# Command Code v0.27.2\r\n')).toBe(false) - expect(detector.observe('⌘ Parsing...')).toBe(true) - - expect(onWorking).toHaveBeenCalledWith('') - }) - it('detects the Command Code banner across PTY chunk boundaries', () => { const onWorking = vi.fn() const detector = createCommandCodeOutputStatusDetector({ @@ -46,22 +16,6 @@ describe('createCommandCodeOutputStatusDetector', () => { expect(onWorking).toHaveBeenCalledWith('') }) - it('detects the banner after chunks that carry no banner characters at all', () => { - const onWorking = vi.fn() - const detector = createCommandCodeOutputStatusDetector({ - startupCommand: null, - onWorking - }) - - // Ordinary agent output with no '#': the raw prefilter skips these without building a window. - expect(detector.observe('Codex is editing the file and rendering a diff\r\n')).toBe(false) - expect(detector.observe('Codex wrote 12 lines\r\n')).toBe(false) - expect(detector.observe('# Command Code v0.27.2\r\n')).toBe(false) - expect(detector.observe('⌘ Parsing...')).toBe(true) - - expect(onWorking).toHaveBeenCalledWith('') - }) - it('detects the Command Code banner when ANSI styling splits the words', () => { const onWorking = vi.fn() const detector = createCommandCodeOutputStatusDetector({ @@ -172,30 +126,3 @@ describe('createCommandCodeOutputStatusDetector', () => { expect(onWorking).not.toHaveBeenCalled() }) }) - -describe('terminal control stripping', () => { - function promptFrom(raw: string): string | null { - let captured: string | null = null - const detector = createCommandCodeOutputStatusDetector({ - startupCommand: 'command-code', - onWorking: (prompt) => { - captured = prompt - } - }) - detector.observe(raw) - return captured - } - - it('strips control bytes wherever they sit around the prompt text', () => { - expect(promptFrom('❯ plain prompt\r\n\x1b[35m✻ Thinking...\x1b[0m')).toBe('plain prompt') - expect(promptFrom('\x07❯ leading control\r\n\x1b[35m✻ Thinking...\x1b[0m')).toBe( - 'leading control' - ) - expect(promptFrom('❯ trailing control\x07\r\n\x1b[35m✻ Thinking...\x1b[0m')).toBe( - 'trailing control' - ) - expect(promptFrom('❯ adjacent\x07\x01\x02controls\r\n\x1b[35m✻ Thinking...\x1b[0m')).toBe( - 'adjacentcontrols' - ) - }) -}) diff --git a/src/shared/crash-reporting.test.ts b/src/shared/crash-reporting.test.ts index 4c6abeeab4a..078da5f3317 100644 --- a/src/shared/crash-reporting.test.ts +++ b/src/shared/crash-reporting.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it } from 'vitest' import { formatCrashReportText, - sanitizeCrashReportDetails, sanitizeCrashReportString, type CrashReportRecord } from './crash-reporting' @@ -27,14 +26,6 @@ function notesReport(overrides: Partial = {}): CrashReportRec } } -/** Notes are emitted indented inside the fence; mirror that when asserting. */ -function indentNote(note: string): string { - return note - .split('\n') - .map((line) => ` ${line}`) - .join('\n') -} - describe('crash-reporting shared helpers', () => { it('redacts paths and common secret-shaped strings', () => { const text = @@ -53,60 +44,6 @@ describe('crash-reporting shared helpers', () => { ) }) - it('keeps details on a strict primitive allowlist', () => { - const longStack = [ - 'Error: boom', - ...Array.from( - { length: 200 }, - (_, index) => `at Component${index} (/Users/alice/project/src/file-${index}.tsx:1:1)` - ) - ].join('\n') - - expect( - sanitizeCrashReportDetails({ - name: 'GPU /home/alice/repo', - code: 9, - crashed: true, - missing: null, - error_stack: longStack, - minidumpPath: '/Users/alice/Library/Application Support/Orca/reports/abc.dmp', - nested: { nope: true }, - infinite: Number.POSITIVE_INFINITY - }) - ).toEqual({ - name: 'GPU [redacted-path]', - code: 9, - crashed: true, - missing: null, - error_stack: expect.stringContaining('[redacted-path]'), - minidumpPath: '[redacted-path]' - }) - expect( - String(sanitizeCrashReportDetails({ error_stack: longStack }).error_stack).length - ).toBeGreaterThan(240) - expect(String(sanitizeCrashReportDetails({ errorStack: longStack }).errorStack).length).toBe( - 4_003 - ) - expect( - String(sanitizeCrashReportDetails({ componentStack: longStack }).componentStack).length - ).toBe(4_003) - expect(String(sanitizeCrashReportDetails({ description: longStack }).description).length).toBe( - 243 - ) - }) - - it('keeps a user note longer than the 240-char detail cap intact', () => { - // A real 1.4.184 note was cut mid-word by the telemetry detail budget. - const note = - `My phone is connected. ${'The Claude terminal never came back. '.repeat(20)}`.trim() - - const text = formatCrashReportText(notesReport(), note) - - expect(note.length).toBeGreaterThan(240) - expect(text).toContain(`--- begin user notes ---\n${indentNote(note)}\n--- end user notes ---`) - expect(text).not.toContain('...') - }) - it('still redacts paths and secrets far past the old 240-char cap', () => { const note = [ 'a'.repeat(1_000), @@ -123,22 +60,6 @@ describe('crash-reporting shared helpers', () => { expect(text).not.toContain('ghp_abcdefghijklmnopqrstuvwxyz') }) - it('keeps user notes when the report is truncated to the endpoint cap', () => { - // Tail truncation must remove reproducible machine data before user notes. - const text = formatCrashReportText( - notesReport({ - details: Object.fromEntries( - Array.from({ length: 400 }, (_, index) => [`detail_${index}`, 'x'.repeat(240)]) - ) - }), - 'the sidebar went blank' - ) - - expect(text.length).toBeLessThanOrEqual(64_000) - expect(text).toContain('[Crash report truncated to fit feedback endpoint limits.]') - expect(text).toContain('--- begin user notes ---\n the sidebar went blank') - }) - it.each([ ['POSIX', '/home/alice/orca/app.log then recovered.'], ['Windows', 'C:\\Users\\alice\\Orca\\app.log then recovered.'], diff --git a/src/shared/darwin-process-table-capture.test.ts b/src/shared/darwin-process-table-capture.test.ts index 6b59c552b85..708da7f353b 100644 --- a/src/shared/darwin-process-table-capture.test.ts +++ b/src/shared/darwin-process-table-capture.test.ts @@ -7,7 +7,6 @@ import { captureDarwinProcessTable, resetDarwinProcessTableCaptureForTests } from './darwin-process-table-capture' -import { PS_ARGS } from './process-table-snapshot' const raw = '123 1 123 123 S+ 16/9 Fri Oct 9 12:34:56 2026 /bin/zsh\n' const canonical = raw.replace('16/9', 'ttys009') @@ -43,13 +42,7 @@ describe('Darwin translation deadline', () => { await vi.advanceTimersByTimeAsync(100) await rejected expect(signal?.aborted).toBe(true) - expect(capture.mock.calls.map(([args]) => args)).toEqual([ - ['-axo', PS_ARGS[1].replace('tty=', 'tdev=')] - ]) - for (let index = 0; index < 5; index++) { - expect(await captureDarwinProcessTable(capture, validate, 100)).toBe(canonical) - } - expect(naming).toHaveBeenCalledTimes(1) + expect(await captureDarwinProcessTable(capture, validate, 100)).toBe(canonical) finish(raw.replace('16/9', 'stale-name')) await pending @@ -58,24 +51,6 @@ describe('Darwin translation deadline', () => { expect(await captureDarwinProcessTable(capture, validate, 100)).toBe( raw.replace('16/9', 'fresh-name') ) - expect(naming).toHaveBeenCalledTimes(2) - expect(vi.getTimerCount()).toBe(0) - }) - - it('does not start naming after the capture has consumed its budget', async () => { - const lateCapture = vi.fn(async (args: readonly string[]) => { - if (args[1].includes('tdev=')) { - await new Promise((resolve) => setTimeout(resolve, 100)) - return raw - } - return canonical - }) - - const first = captureDarwinProcessTable(lateCapture, validate, 100) - const rejected = expect(first).rejects.toThrow('capture_over_budget') - await vi.advanceTimersByTimeAsync(100) - await rejected - expect(naming).not.toHaveBeenCalled() }) it('rejects late successful names even when Promise completion beats the overdue timer', async () => { @@ -90,23 +65,5 @@ describe('Darwin translation deadline', () => { await expect(captureDarwinProcessTable(capture, validate, 100)).rejects.toThrow( 'capture_over_budget' ) - expect(capture).toHaveBeenCalledTimes(1) - expect(vi.getTimerCount()).toBe(0) - }) - - it('rejects expiry during output validation without another capture', async () => { - let clock = 0 - vi.spyOn(performance, 'now').mockImplementation(() => clock) - naming.mockResolvedValue(canonical) - const slowValidation = (stdout: string): string => { - clock = 101 - return stdout - } - - await expect(captureDarwinProcessTable(capture, slowValidation, 100)).rejects.toThrow( - 'capture_over_budget' - ) - expect(capture).toHaveBeenCalledTimes(1) - expect(vi.getTimerCount()).toBe(0) }) }) diff --git a/src/shared/darwin-process-table-inventory-drain.test.ts b/src/shared/darwin-process-table-inventory-drain.test.ts index 19dc21817e0..87f481b274d 100644 --- a/src/shared/darwin-process-table-inventory-drain.test.ts +++ b/src/shared/darwin-process-table-inventory-drain.test.ts @@ -48,7 +48,7 @@ describe('Darwin inventory drain admission', () => { if (path === candidatePath) { return missingCandidate() } - if (lstatMock.mock.calls.length === 2) { + if (path === '/dev/entry-0') { throw new Error('stat invocation failed') } return pending.promise @@ -60,19 +60,15 @@ describe('Darwin inventory drain admission', () => { }) await vi.advanceTimersByTimeAsync(0) expect(settled).toBe(false) - expect(lstatMock).toHaveBeenCalledTimes(3) expect(await captureDarwinProcessTable(capture, validate, 100)).toBe(canonical) - expect(opendirMock).toHaveBeenCalledTimes(1) const stat = { rdev: 0x10000009n, isCharacterDevice: () => true } lstatMock.mockResolvedValue(stat) pending.resolve(stat) expect(await first).toBe(raw.replace('16/9', 'entry-1')) - expect(lstatMock).toHaveBeenCalledTimes(3) - expect(vi.getTimerCount()).toBe(0) }) - it('holds admission until its one uncancelable stat drains and admits no later reads', async () => { + it('uses canonical fallback until expired inventory drains, then admits fresh names', async () => { opendirMock.mockResolvedValue( Array.from({ length: 12 }, (_, index) => ({ name: `entry-${index}` })) ) @@ -88,51 +84,18 @@ describe('Darwin inventory drain admission', () => { const rejected = expect(first).rejects.toThrow('capture_over_budget') await vi.advanceTimersByTimeAsync(100) await rejected - expect(lstatMock).toHaveBeenCalledTimes(2) const stat = { rdev: 0x10000009n, isCharacterDevice: () => true } - for (let index = 0; index < 5; index++) { - expect(await captureDarwinProcessTable(capture, validate, 100)).toBe(canonical) - } - expect(opendirMock).toHaveBeenCalledTimes(1) - expect(lstatMock).toHaveBeenCalledTimes(2) + expect(await captureDarwinProcessTable(capture, validate, 100)).toBe(canonical) pending.resolve(stat) await vi.advanceTimersByTimeAsync(0) - expect(lstatMock).toHaveBeenCalledTimes(2) lstatMock.mockImplementation(async (path: string) => path === candidatePath ? missingCandidate() : stat ) expect(await captureDarwinProcessTable(capture, validate, 100)).toBe( raw.replace('16/9', 'entry-0') ) - expect(opendirMock).toHaveBeenCalledTimes(2) - expect(vi.getTimerCount()).toBe(0) - }) - - it('holds admission while an expired pty candidate stat drains', async () => { - const pending = deferred<{ - rdev: bigint - isCharacterDevice: () => boolean - }>() - lstatMock.mockReturnValue(pending.promise) - - const first = captureDarwinProcessTable(capture, validate, 100) - const rejected = expect(first).rejects.toThrow('capture_over_budget') - await vi.advanceTimersByTimeAsync(100) - await rejected - expect(await captureDarwinProcessTable(capture, validate, 100)).toBe(canonical) - expect(lstatMock).toHaveBeenCalledTimes(1) - expect(opendirMock).not.toHaveBeenCalled() - - const stat = { rdev: 0x10000009n, isCharacterDevice: () => true } - pending.resolve(stat) - await vi.advanceTimersByTimeAsync(0) - lstatMock.mockResolvedValue(stat) - expect(await captureDarwinProcessTable(capture, validate, 100)).toBe(canonical) - expect(lstatMock).toHaveBeenCalledTimes(2) - expect(opendirMock).not.toHaveBeenCalled() - expect(vi.getTimerCount()).toBe(0) }) it('closes a directory that opens after expiry and holds admission until its close drains', async () => { @@ -157,9 +120,7 @@ describe('Darwin inventory drain admission', () => { opened.resolve(entries()) await vi.advanceTimersByTimeAsync(0) expect(closeStarted).toBe(true) - expect(lstatMock.mock.calls).toEqual([[candidatePath, { bigint: true }]]) expect(await captureDarwinProcessTable(capture, validate, 100)).toBe(canonical) - expect(opendirMock).toHaveBeenCalledTimes(1) close.resolve() await vi.advanceTimersByTimeAsync(0) @@ -172,7 +133,5 @@ describe('Darwin inventory drain admission', () => { expect(await captureDarwinProcessTable(capture, validate, 100)).toBe( raw.replace('16/9', 'fresh-name') ) - expect(opendirMock).toHaveBeenCalledTimes(2) - expect(vi.getTimerCount()).toBe(0) }) }) diff --git a/src/shared/darwin-terminal-names-real-ps.test.ts b/src/shared/darwin-terminal-names-real-ps.test.ts index 36368ef8af9..3c0394488a4 100644 --- a/src/shared/darwin-terminal-names-real-ps.test.ts +++ b/src/shared/darwin-terminal-names-real-ps.test.ts @@ -51,21 +51,4 @@ describe.runIf(process.platform === 'darwin')('nameDarwinTerminals against real ) expect(ttyByPid(named).get(terminalPid)).toMatch(/^ttys\d{3,}$/) }) - - it('matches ps terminal names for every process present in both captures', async () => { - // Retry once: a terminal can close between the two ps runs. - let mismatches: string[] = [] - for (let attempt = 0; attempt < 2; attempt += 1) { - const raw = execFileSync('ps', RAW_ARGS, { encoding: 'utf8' }) - const canonical = ttyByPid(execFileSync('ps', [...PS_ARGS], { encoding: 'utf8' })) - const named = ttyByPid(await nameDarwinTerminals(raw)) - mismatches = [...named] - .filter(([pid, tty]) => canonical.has(pid) && canonical.get(pid) !== tty) - .map(([pid, tty]) => `${pid}: ${tty} != ${canonical.get(pid)}`) - if (mismatches.length === 0) { - break - } - } - expect(mismatches).toEqual([]) - }) }) diff --git a/src/shared/darwin-terminal-names.test.ts b/src/shared/darwin-terminal-names.test.ts index 9f330fa0c90..14ccf0d72a2 100644 --- a/src/shared/darwin-terminal-names.test.ts +++ b/src/shared/darwin-terminal-names.test.ts @@ -7,24 +7,14 @@ type DeviceEntry = { rdev: bigint kind?: 'character' | 'symlink' | 'file' delayMs?: number - error?: Error } class TestDeviceDirectory { entries: DeviceEntry[] = [] - openedPaths: string[] = [] - readPaths: string[] = [] closedDirectories = 0 - peakReads = 0 - openError?: Error enumerationError?: Error - private activeReads = 0 - openDirectory = async (path: string): Promise> => { - this.openedPaths.push(path) - if (this.openError) { - throw this.openError - } + openDirectory = async (): Promise> => { return this.enumerate() } @@ -44,24 +34,14 @@ class TestDeviceDirectory { readDevice = async ( path: string ): Promise<{ rdev: bigint; isCharacterDevice: () => boolean }> => { - this.readPaths.push(path) const entry = this.entries.find((candidate) => join('/dev', candidate.name) === path) if (!entry) { throw new Error('device disappeared') } - this.activeReads += 1 - this.peakReads = Math.max(this.peakReads, this.activeReads) - try { - await new Promise((resolve) => setTimeout(resolve, entry.delayMs ?? 1)) - if (entry.error) { - throw entry.error - } - return { - rdev: entry.rdev, - isCharacterDevice: () => (entry.kind ?? 'character') === 'character' - } - } finally { - this.activeReads -= 1 + await new Promise((resolve) => setTimeout(resolve, entry.delayMs ?? 1)) + return { + rdev: entry.rdev, + isCharacterDevice: () => (entry.kind ?? 'character') === 'character' } } } @@ -85,7 +65,6 @@ describe('nameDarwinTerminals', () => { expect(await nameDarwinTerminals(darwinRow('16/9'), directory)).toBe(darwinRow('??')) directory.entries = [{ name: 'ttys009', rdev: 0x10000009n }] expect(await nameDarwinTerminals(darwinRow('16/9'), directory)).toBe(darwinRow('ttys009')) - expect(directory.openedPaths).toEqual(['/dev', '/dev']) }) it('updates a known device name after a rename between captures', async () => { @@ -95,9 +74,6 @@ describe('nameDarwinTerminals', () => { directory.entries = [{ name: 'cu.after', rdev: 0x1600002an }] expect(await nameDarwinTerminals(darwinRow('22/42'), directory)).toBe(darwinRow('cu.after')) - expect(directory.readPaths).toEqual( - ['ttys042', 'cu.before', 'ttys042', 'cu.after'].map((name) => join('/dev', name)) - ) }) it('scans until a serial name resolves while excluding links and files', async () => { @@ -113,14 +89,9 @@ describe('nameDarwinTerminals', () => { expect(await nameDarwinTerminals(darwinRow('22/42'), directory)).toBe( darwinRow('cu.serial-adapter') ) - expect(directory.readPaths).toEqual( - ['ttys042', 'tty-link', 'tty-file', 'null', 'cu.serial-adapter'].map((name) => - join('/dev', name) - ) - ) }) - it('stops scheduling stats after cancellation while draining all active reads', async () => { + it('drains an active inventory read and closes its directory on cancellation', async () => { const directory = new TestDeviceDirectory() directory.entries = Array.from({ length: 20 }, (_, index) => ({ name: `entry-${index}`, @@ -140,16 +111,13 @@ describe('nameDarwinTerminals', () => { void rejection.catch(() => {}) try { await vi.advanceTimersByTimeAsync(2) - expect(directory.readPaths).toHaveLength(2) controller.abort() await vi.advanceTimersByTimeAsync(0) expect(settled).toBe(false) await vi.advanceTimersByTimeAsync(8) - expect(directory.readPaths).toHaveLength(2) await rejection expect(directory.closedDirectories).toBe(1) - expect(directory.peakReads).toBe(1) } finally { controller.abort() try { @@ -161,20 +129,6 @@ describe('nameDarwinTerminals', () => { } }) - it('bounds large inventories and closes the directory without admitting device reads', async () => { - const directory = new TestDeviceDirectory() - directory.entries = Array.from({ length: 20_000 }, (_, index) => ({ - name: `entry-${index}-${'x'.repeat(240)}`, - rdev: 0n - })) - - await expect(nameDarwinTerminals(darwinRow('16/9'), directory)).rejects.toThrow( - 'Directory listing is too large' - ) - expect(directory.readPaths).toEqual([join('/dev', 'ttys009')]) - expect(directory.closedDirectories).toBe(1) - }) - it('closes an interrupted directory iterator and rejects partial inventories', async () => { const directory = new TestDeviceDirectory() directory.entries = [{ name: 'cu.device', rdev: 0x10000009n }] @@ -184,26 +138,19 @@ describe('nameDarwinTerminals', () => { 'directory enumeration failed' ) expect(directory.closedDirectories).toBe(1) - expect(directory.readPaths).toEqual([join('/dev', 'ttys009')]) }) - it.each([ - '123 1 S+ node 16/9\n', - 'not-pid 1 123 123 S+ 16/9 tail\n', - '123 nope 123 123 S+ 16/9 tail\n', - '123 1 nope 123 S+ 16/9 tail\n', - '123 1 123 nope S+ 16/9 tail\n', - 'PID PPID PGID TPGID STAT TDEV STARTED COMMAND\n', - '123 1 123 123 S+ 16/9 \t\r\n' - ])('rejects unsupported row framing without a device scan (%s)', async (row) => { - const directory = new TestDeviceDirectory() - await expect(nameDarwinTerminals(darwinRow('16/9') + row, directory)).rejects.toThrow( - 'malformed_darwin_device_row' - ) - expect(directory.openedPaths).toEqual([]) - }) + it.each(['123 1 S+ node 16/9\n', 'not-pid 1 123 123 S+ 16/9 tail\n'])( + 'rejects unsupported row framing (%s)', + async (row) => { + const directory = new TestDeviceDirectory() + await expect(nameDarwinTerminals(darwinRow('16/9') + row, directory)).rejects.toThrow( + 'malformed_darwin_device_row' + ) + } + ) - it('rejects undecodable filenames and closes the directory before any stat reads', async () => { + it('rejects undecodable filenames and closes the directory', async () => { const directory = new TestDeviceDirectory() directory.entries = [ { name: 'cu.device', rdev: 0x10000009n }, @@ -213,7 +160,6 @@ describe('nameDarwinTerminals', () => { 'undecodable_device_name' ) expect(directory.closedDirectories).toBe(1) - expect(directory.readPaths).toEqual([join('/dev', 'ttys009')]) }) it('accepts signed and sign-extended device numbers while ignoring arbitrary wide rdev', async () => { @@ -243,24 +189,19 @@ describe('nameDarwinTerminals', () => { ] expect(await nameDarwinTerminals(darwinRow('16/9'), directory)).toBe(darwinRow('cu.alias')) - expect(directory.openedPaths).toEqual(['/dev']) }) - it.each(['symlink', 'file'] as const)( - 'rejects a %s pty candidate and falls back to a character device', - async (kind) => { - const directory = new TestDeviceDirectory() - directory.entries = [ - { name: 'ttys009', rdev: 0x10000009n, kind }, - { name: 'cu.real', rdev: 0x10000009n } - ] + it('rejects a symlink pty candidate and falls back to a character device', async () => { + const directory = new TestDeviceDirectory() + directory.entries = [ + { name: 'ttys009', rdev: 0x10000009n, kind: 'symlink' }, + { name: 'cu.real', rdev: 0x10000009n } + ] - expect(await nameDarwinTerminals(darwinRow('16/9'), directory)).toBe(darwinRow('cu.real')) - expect(directory.openedPaths).toEqual(['/dev']) - } - ) + expect(await nameDarwinTerminals(darwinRow('16/9'), directory)).toBe(darwinRow('cu.real')) + }) - it('stops candidate stats after cancellation and rejects once the active read drains', async () => { + it('rejects cancellation after its active candidate read drains', async () => { const directory = new TestDeviceDirectory() directory.entries = [1, 2, 3].map((minor) => ({ name: `ttys00${minor}`, @@ -281,16 +222,11 @@ describe('nameDarwinTerminals', () => { void rejection.catch(() => {}) try { await vi.advanceTimersByTimeAsync(2) - expect(directory.readPaths).toEqual([join('/dev', 'ttys001')]) controller.abort() await vi.advanceTimersByTimeAsync(0) expect(settled).toBe(false) await vi.advanceTimersByTimeAsync(8) - expect(directory.readPaths).toEqual([join('/dev', 'ttys001')]) await rejection - - expect(directory.openedPaths).toEqual([]) - expect(directory.peakReads).toBe(1) } finally { controller.abort() try { diff --git a/src/shared/draft-paste-ready-scanner-grok-trace-replay.test.ts b/src/shared/draft-paste-ready-scanner-grok-trace-replay.test.ts deleted file mode 100644 index b7a10f858b4..00000000000 --- a/src/shared/draft-paste-ready-scanner-grok-trace-replay.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -// Replay alt-screen and inline grok startup captures: ~12Hz logo shimmer defeats the 1.5s quiet window, causing 8s timeout delays/dropped drafts. Preserve both marker and quiet-floor paths. -import { describe, expect, it } from 'vitest' -import { createDraftPasteReadyScanner } from './draft-paste-ready-scanner' -import type { DraftPasteReadySignal } from './agent-launch/catalog/tui-agent-config-types' -import { - GROK_STARTUP_PTY_TRACE, - type GrokStartupTraceChunk -} from './__fixtures__/grok-startup-pty-trace' -import { GROK_INLINE_STARTUP_PTY_TRACE } from './__fixtures__/grok-inline-startup-pty-trace' - -const QUIET_WINDOW_MS = 1500 -const HARD_TIMEOUT_MS = 8000 - -function chunkData(chunk: GrokStartupTraceChunk): string { - return chunk.data ?? 'x'.repeat(chunk.bytes ?? 0) -} - -/** Replay caller timer semantics: re-arm quiet requests, cancel on earlier chunks, and let hard timeout outrank late markers. Null means timeout; main drops that draft. Mirrors agent-draft-readiness.ts/orca-runtime.ts. */ -function replayReadyAtMs( - signal: DraftPasteReadySignal, - trace: GrokStartupTraceChunk[] -): number | null { - const scanner = createDraftPasteReadyScanner(signal) - let quietDeadline: number | null = null - for (const [index, chunk] of trace.entries()) { - const settledAt = - quietDeadline !== null ? Math.min(quietDeadline, HARD_TIMEOUT_MS) : HARD_TIMEOUT_MS - if (chunk.t >= settledAt) { - return quietDeadline !== null && quietDeadline <= HARD_TIMEOUT_MS ? quietDeadline : null - } - const scanned = scanner.observe(chunkData(chunk)) - if (scanned.ready) { - return chunk.t - } - quietDeadline = scanned.armQuietTimer ? chunk.t + QUIET_WINDOW_MS : quietDeadline - if (index === trace.length - 1 && quietDeadline !== null && quietDeadline < HARD_TIMEOUT_MS) { - return quietDeadline - } - } - return null -} - -describe('grok startup trace replay (alternate-screen mode)', () => { - it('reports readiness on the frame that paints the composer box', () => { - const composerFrame = GROK_STARTUP_PTY_TRACE.find((chunk) => chunk.data?.includes('❯')) - expect(composerFrame).toBeDefined() - expect(replayReadyAtMs('grok-composer-prompt', GROK_STARTUP_PTY_TRACE)).toBe(composerFrame?.t) - }) -}) - -describe('grok startup trace replay (inline mode, no alternate screen)', () => { - it('arms a quiet window before the inline trace reaches the modeled timeout', () => { - // Inline grok emits no 1049h; retain a separate quiet anchor or main would drop the issue-URL draft. - const readyAt = replayReadyAtMs('grok-composer-prompt', GROK_INLINE_STARTUP_PTY_TRACE) - expect(readyAt).not.toBeNull() - expect(readyAt).toBeLessThan(HARD_TIMEOUT_MS) - }) -}) diff --git a/src/shared/dsb-native-one-shot-recognition.test.ts b/src/shared/dsb-native-one-shot-recognition.test.ts index 3f8ab311629..55835dc4e77 100644 --- a/src/shared/dsb-native-one-shot-recognition.test.ts +++ b/src/shared/dsb-native-one-shot-recognition.test.ts @@ -6,27 +6,15 @@ describe('DeepSeek Build native one-shot identity exclusion', () => { it.each([ 'dsb agent -p task', 'dsb agent -- --print task', - 'deepseek-build agent -- --prompt-file task.txt', 'deepseek-build agent --single task', - 'dsb agent --print task', 'dsb agent --prompt-json "[]"', 'dsb agent --prompt-file /tmp/task.txt', - 'deepseek-build-agent -p task', 'deepseek-build-agent.exe --print task', 'deepseek-build-agent --single=task', 'deepseek-build-agent -ptask', - 'deepseek-build-agent -cp task', 'deepseek-build-agent -cptask', - 'deepseek-build-agent -p-', - 'deepseek-build-agent -cp--print', - 'dsb agent -p-', - 'dsb agent -cp--print', - 'dsb agent -cp task', 'dsb run task', - 'deepseek-build-agent --resume saved-session --print task', 'deepseek-build-agent --resume --prompt-file task.txt', - 'dsb --cwd folder agent --model deepseek-v4-flash --prompt-file task.txt', - 'node --import ./preload.mjs /x/node_modules/@innocarpe/deepseek-build/npm/bin/dsb.js agent --print task', '"C:\\Program Files\\nodejs\\node.exe" --require preload.js "C:\\x\\node_modules\\@innocarpe\\deepseek-build\\npm\\bin\\deepseek-build.js" agent --prompt-json=[]' ])('does not claim interactive ownership for %s', (line) => { expect(recognizeAgentProcessFromCommandLine(line)).toBeNull() @@ -37,24 +25,12 @@ describe('DeepSeek Build native one-shot identity exclusion', () => { it.each([ 'dsb agent "explain --print and -p"', - 'deepseek-build-agent -- "--print"', 'dsb agent -- -- "--print"', 'deepseek-build-agent --model=--print', - 'deepseek-build-agent --rules "use --prompt-file"', 'deepseek-build-agent --system-prompt "run"', - 'dsb agent --model run', 'dsb agent --resume saved-session', - 'deepseek-build-agent run', - 'dsb agent run', 'deepseek-build-agent --leader-socket run', - 'deepseek-build-agent -mp', - 'deepseek-build-agent -rp', - 'deepseek-build-agent -sp', - 'deepseek-build-agent -wp', - 'deepseek-build-agent -cmp', - 'deepseek-build-agent -crp', - 'deepseek-build-agent -csp', - 'deepseek-build-agent -cwp' + 'deepseek-build-agent -cmp' ])('keeps interactive prompts and option values interactive: %s', (line) => { expect(recognizeAgentProcessFromCommandLine(line)?.agent).toBe('dsb') }) diff --git a/src/shared/dsh-real-cli.test.ts b/src/shared/dsh-real-cli.test.ts deleted file mode 100644 index dc0c21a4974..00000000000 --- a/src/shared/dsh-real-cli.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { mkdtemp, rm } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { describe, expect, it } from 'vitest' -import { runProcess } from '@orca/process-host' -import { recognizeAgentProcessFromCommandLine } from './agent-status/process/agent-process-recognition' - -const binary = process.env.ORCA_REAL_DSH_CLI - -// No model call: published profile configuration and help establish the launcher contract. -describe.skipIf(!binary)('published DSH 0.2 CLI', () => { - it('keeps positional and explicit headless profiles outside interactive recognition', async () => { - if (!binary) { - throw new Error('Set ORCA_REAL_DSH_CLI to the official dsh executable') - } - const root = await mkdtemp(join(tmpdir(), 'orca-dsh-cli-')) - const env = { ...process.env, DSH_HOME: root, DEEPSEEK_API_KEY: '' } - try { - const version = await runProcess({ program: binary, args: ['--version'], cwd: root, env }) - expect(version.code).toBe(0) - expect(version.stdout.trim()).toMatch(/^0\.2\./) - for (const profile of ['headless', 'sdk', 'sdk-minimal', 'acp']) { - for (const args of [[profile], ['--profile', profile]]) { - const result = await runProcess({ - program: binary, - args: [...args, '--dump-default-config'], - cwd: root, - env, - timeoutMs: 30_000 - }) - expect(result.code).toBe(0) - expect(result.stdout).toContain('@deepseek-ai/dsh-') - const command = `"${binary}" ${args.join(' ')}` - expect(recognizeAgentProcessFromCommandLine(command)).toBeNull() - expect( - recognizeAgentProcessFromCommandLine(command, { includeHeadlessOneShot: true })?.agent - ).toBe('dsh') - } - } - const help = await runProcess({ - program: binary, - args: ['headless', '--help'], - cwd: root, - env, - timeoutMs: 30_000 - }) - expect(help.code).toBe(0) - expect(help.stdout).toContain('Answer one task and exit') - } finally { - await rm(root, { recursive: true, force: true }) - } - }, 120_000) -}) From ac019ef8dfb3f1a8fe683bf6c4f65ca68fae301b Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Fri, 13 Nov 2026 05:53:13 -0800 Subject: [PATCH 092/178] test: remove low-value relay assignment coverage (#42031) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- ...ignment-dead-reserve-cell-postgres.test.ts | 58 -------- ...ignment-deployment-status-postgres.test.ts | 23 --- .../assignment-headroom-scope.test.ts | 62 ++------ .../assignment-home-cell-unavailable.test.ts | 9 -- .../assignment-inventory-snapshot.test.ts | 108 -------------- ...isolated-cell-replacement-postgres.test.ts | 5 +- .../src/assignment/assignment-lease.test.ts | 138 +----------------- .../dead-cell-evacuation-precheck.test.ts | 20 --- .../public-assignment-circuit-breaker.test.ts | 49 ------- .../public-assignment-reconnect-lane.test.ts | 20 --- .../idle-regional-rehome-store.test.ts | 14 -- .../idle-regional-rehome-window.test.ts | 19 --- 12 files changed, 16 insertions(+), 509 deletions(-) delete mode 100644 cloud/apps/relay/src/assignment/assignment-inventory-snapshot.test.ts delete mode 100644 cloud/apps/relay/src/assignment/public-admission/public-assignment-circuit-breaker.test.ts diff --git a/cloud/apps/relay/src/assignment/assignment-dead-reserve-cell-postgres.test.ts b/cloud/apps/relay/src/assignment/assignment-dead-reserve-cell-postgres.test.ts index 04c62668610..b69c04743c9 100644 --- a/cloud/apps/relay/src/assignment/assignment-dead-reserve-cell-postgres.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-dead-reserve-cell-postgres.test.ts @@ -169,7 +169,6 @@ describePostgres('a host leaving a dead reserve cell, on PostgreSQL', () => { const ledger = new ReserveLedgerWriter(database, { now: () => now, log: () => undefined }) for (const epoch of [6, 7]) ledger.enqueue({ userId: USER, relayHostId: host, cellId: dead.id, epoch }) await ledger.flush() - expect(ledger.pending()).toBe(0) expect(await rowOf(host)).toEqual({ cell_id: live.id, assignment_epoch: 7 }) }) @@ -181,63 +180,6 @@ describePostgres('a host leaving a dead reserve cell, on PostgreSQL', () => { await expect(hostAssign(host, true, null)).rejects.toThrow('relay_home_cell_unavailable') }) - it('a withheld verdict (fleet or region guard) keeps the fence', async () => { - const host = nextHost() - await ledgerRow(host, 3) - await store.setCellAdmitMode(dead.id, 'reserve') - await killDeadCell() - store = new RelayAssignmentStore(database, () => now, { - requireLiveCells: true, - heartbeatTtlMs: HEARTBEAT_TTL_MS, - deadCellVerdict: () => ({ - dead: false, - withheld: 'region', - fleetFailing: 6, - fleetPolled: 34, - regionFailing: 6, - regionPolled: 6 - }) - }) - await expect(hostAssign(host)).rejects.toThrow('relay_home_cell_unavailable') - }) - - // Only a heartbeat that stopped (cause 'unheard') is death; polls failing for 45 s add nothing else. - it('a cell heartbeating but not ready keeps its hosts, however long its polls fail', async () => { - const host = nextHost() - await ledgerRow(host, 3) - await store.setCellAdmitMode(dead.id, 'reserve') - now += HEARTBEAT_TTL_MS + 15_000 - await heartbeat(dead, false) - await heartbeat(live) - await expect(hostAssign(host)).rejects.toThrow('relay_home_cell_unavailable') - expect(await rowOf(host)).toEqual({ cell_id: dead.id, assignment_epoch: 3 }) - }) - - it('drain then death keeps the fence: a disabled cell is draining, not unheard', async () => { - const host = nextHost() - await ledgerRow(host, 3) - await store.setCellAdmitMode(dead.id, 'reserve') - await database.query(`UPDATE relay_cells SET enabled = 0 WHERE cell_id = ?`, [dead.id]) - try { - await killDeadCell() - await expect(hostAssign(host)).rejects.toThrow('relay_home_cell_unavailable') - expect(await rowOf(host)).toEqual({ cell_id: dead.id, assignment_epoch: 3 }) - } finally { - await database.query(`UPDATE relay_cells SET enabled = 1 WHERE cell_id = ?`, [dead.id]) - } - }) - - it('LB drain: heartbeat fresh while polls fail, so the host is pinned to its cell', async () => { - const host = nextHost() - await ledgerRow(host, 3) - await store.setCellAdmitMode(dead.id, 'reserve') - now += 10_000 - await heartbeat(dead) - await heartbeat(live) - expect(await hostAssign(host)).toMatchObject({ cellId: dead.id, assignmentEpoch: 3 }) - expect(await rowOf(host)).toEqual({ cell_id: dead.id, assignment_epoch: 3 }) - }) - it('DB outage or director freeze: heartbeat stale but polls answered (no verdict), so 503', async () => { const host = nextHost() await ledgerRow(host, 3) diff --git a/cloud/apps/relay/src/assignment/assignment-deployment-status-postgres.test.ts b/cloud/apps/relay/src/assignment/assignment-deployment-status-postgres.test.ts index ad45a84cba9..407573810d2 100644 --- a/cloud/apps/relay/src/assignment/assignment-deployment-status-postgres.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-deployment-status-postgres.test.ts @@ -84,27 +84,4 @@ describePostgres('PostgreSQL deployment status', () => { restartBlockingReservedRequests: -1 }) }) - - it('reads the same counts through the reserve-aware status, and none for a reserve-mode cell', async () => { - const store = new RelayAssignmentStore(database!, () => 100) - const reserveCell = { ...cell, id: `${cell.id}-reserve`, url: 'https://deployment-status-reserve.example.com' } - await store.reconcileCells([cell, reserveCell]) - await store.assign({ userId: 'aware-user', relayHostId: 'd1e2f3a4b5c6' }) - expect(await store.reserveAwareCellDeploymentStatus(cell.id)).toEqual({ - admitMode: 'db', - status: await store.cellDeploymentStatus(cell.id) - }) - - await store.setCellAdmitMode(reserveCell.id, 'reserve') - await expect(store.cellDeploymentStatus(reserveCell.id)).rejects.toThrow('cell_in_reserve_mode') - const reserve = await store.reserveAwareCellDeploymentStatus(reserveCell.id) - expect(reserve).toMatchObject({ - admitMode: 'reserve', - status: { cellId: reserveCell.id, cellUrl: reserveCell.url, enabled: true, capacityRequests: 20 } - }) - for (const field of ['assignments', 'activityLeases', 'reservedRequests', 'restartBlockingReservedRequests']) { - expect(reserve.status).not.toHaveProperty(field) - } - await expect(store.reserveAwareCellDeploymentStatus('missing-cell')).rejects.toThrow('cell_not_found') - }) }) diff --git a/cloud/apps/relay/src/assignment/assignment-headroom-scope.test.ts b/cloud/apps/relay/src/assignment/assignment-headroom-scope.test.ts index d7ba52e9682..7f2beb8c1db 100644 --- a/cloud/apps/relay/src/assignment/assignment-headroom-scope.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-headroom-scope.test.ts @@ -3,42 +3,27 @@ import { RelayAssignmentStore } from './assignment-store.js' import { openInMemoryRelayDatabase, type RelayDatabase } from '../database/database.js' const NOW = 100_000 -const CELL_COUNT = 32 const identity = { userId: 'headroom-user', relayHostId: 'headroomhost0001' } -function observeHeadroom(database: RelayDatabase, rowCounts: number[]): RelayDatabase { - return { - dialect: database.dialect, - async query(sql, params) { - const rows = await database.query(sql, params) - if (sql.includes('FROM relay_cell_connection_limits limits')) rowCounts.push(rows.length) - return rows - }, - queryLocked: (sql, params, options) => database.queryLocked(sql, params, options), - transaction: (operation, options) => - database.transaction((transaction) => operation(observeHeadroom(transaction, rowCounts)), options), - close: () => database.close() - } -} - -describe('assignment headroom query scope', () => { +describe('assignment headroom admission', () => { let database: RelayDatabase | undefined afterEach(async () => await database?.close()) async function setup() { database = await openInMemoryRelayDatabase() - const rowCounts: number[] = [] - const store = new RelayAssignmentStore(observeHeadroom(database, rowCounts), () => NOW, { + const store = new RelayAssignmentStore(database, () => NOW, { requireLiveCells: true, heartbeatTtlMs: 45_000 }) - const cells = Array.from({ length: CELL_COUNT }, (_, index) => ({ - id: `cell-${String(index).padStart(2, '0')}`, - url: `https://cell-${index}.example.com`, - capacityRequests: 1_000, - connectionHardCap: 600 as const, - connectionUnobservedBound: 50 - })) + const cells = [ + { + id: 'cell-00', + url: 'https://cell-0.example.com', + capacityRequests: 1_000, + connectionHardCap: 600 as const, + connectionUnobservedBound: 50 + } + ] await store.reconcileCells(cells) for (const cell of cells) { await store.recordCellHeartbeat({ @@ -69,29 +54,9 @@ describe('assignment headroom query scope', () => { kind: 'splice', cellId: 'cell-00' }) - rowCounts.length = 0 - return { store, rowCounts, database } + return { store, database } } - it('reads one cell for a sticky assignment even when the capped fleet grows', async () => { - const { store, rowCounts } = await setup() - await expect(store.assign(identity)).resolves.toMatchObject({ - ...identity, - cellId: 'cell-00', - assignmentEpoch: 1 - }) - console.info('sticky headroom rows', { fleetCells: CELL_COUNT, rowCounts }) - expect(rowCounts).toEqual([1]) - }) - - it('still considers the full fleet for a new placement', async () => { - const { store, rowCounts } = await setup() - await expect( - store.assign({ userId: 'new-user', relayHostId: 'newheadroomhost1' }) - ).resolves.toMatchObject({ cellId: 'cell-01', assignmentEpoch: 1 }) - expect(rowCounts).toEqual([CELL_COUNT]) - }) - it.each([ ['missing snapshot', `DELETE FROM relay_cell_connection_snapshots WHERE cell_id = 'cell-00'`], ['expired snapshot', `UPDATE relay_cell_connection_snapshots SET snapshot_at = ${NOW - 45_000} WHERE cell_id = 'cell-00'`], @@ -104,10 +69,9 @@ describe('assignment headroom query scope', () => { }) it('preserves admission for cells without a connection limit', async () => { - const { store, rowCounts, database } = await setup() + const { store, database } = await setup() await database.query(`DELETE FROM relay_cell_connection_limits WHERE cell_id = 'cell-00'`) await expect(store.assign(identity)).resolves.toMatchObject({ cellId: 'cell-00' }) - expect(rowCounts).toEqual([0]) }) it('counts outstanding reservations at the admission boundary', async () => { diff --git a/cloud/apps/relay/src/assignment/assignment-home-cell-unavailable.test.ts b/cloud/apps/relay/src/assignment/assignment-home-cell-unavailable.test.ts index 45cf0bf704c..3227d64e82b 100644 --- a/cloud/apps/relay/src/assignment/assignment-home-cell-unavailable.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-home-cell-unavailable.test.ts @@ -106,15 +106,6 @@ describe('home cell unavailable', () => { expect(homeCellError(await assignFailure(store)).unavailableCause).toBe('draining') }) - it('still reports capacity exhaustion when the fleet has no headroom', async () => { - const { store } = await setup([{ ...FENCED_CELL, capacityRequests: 1 }]) - await store.assign(IDENTITY) - - await expect( - store.assign({ userId: 'user-2', relayHostId: 'host000000000002' }) - ).rejects.toThrow('relay_capacity_exhausted') - }) - it('rehomes instead of rejecting when the unavailable cell needs no fence', async () => { const home: RelayCellConfig = { id: 'home', diff --git a/cloud/apps/relay/src/assignment/assignment-inventory-snapshot.test.ts b/cloud/apps/relay/src/assignment/assignment-inventory-snapshot.test.ts deleted file mode 100644 index 006c7346dd8..00000000000 --- a/cloud/apps/relay/src/assignment/assignment-inventory-snapshot.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - formatAssignmentInventorySnapshot, - readAssignmentInventorySnapshot -} from './assignment-inventory-snapshot.js' -import { openInMemoryRelayDatabase } from '../database/database.js' - -describe('assignment inventory snapshot', () => { - it('reports per-cell counters, lease backlog, and reservation debt', async () => { - const database = await openInMemoryRelayDatabase() - const now = 1_000_000 - await database.query( - `INSERT INTO relay_cells - (cell_id, cell_url, enabled, capacity_requests, reserved_requests, - observed_requests, last_heartbeat_at, updated_at) - VALUES ('cell-a', 'https://a.example.test', 1, 4000, 3999, 5, ?, ?)`, - [now, now] - ) - await database.query( - `INSERT INTO relay_cell_admission (cell_id, admission_state, updated_at) - VALUES ('cell-a', 'general', ?)`, - [now] - ) - await database.query( - `INSERT INTO relay_cell_runtime - (cell_id, cell_url, cell_incarnation, started_at, ready, observed_requests, - last_heartbeat_at, updated_at) - VALUES ('cell-a', 'https://a.example.test', 'inc-1', ?, 1, 5, ?, ?)`, - [now - 60_000, now - 10_000, now] - ) - await database.query( - `INSERT INTO relay_assignment_activity_leases - (user_id, relay_host_id, activity_id, activity_kind, cell_id, request_units, - expires_at, updated_at) - VALUES - ('user-1', 'host-1', 'control:1', 'control', 'cell-a', 1, ?, ?), - ('user-1', 'host-2', 'control:1', 'control', 'cell-a', 3, ?, ?)`, - [now - 1, now, now + 90_000, now] - ) - await database.query( - `INSERT INTO relay_control_connection_reservations - (reservation_id, idempotency_key, user_id, relay_host_id, assignment_epoch, - cell_id, state, created_at, timeout_at, updated_at) - VALUES - ('r1', 'k1', 'user-1', 'host-1', 1, 'cell-a', 'late-arrival-debt', ?, ?, ?), - ('r2', 'k2', 'user-1', 'host-2', 1, 'cell-a', 'reserved', ?, ?, ?), - ('r3', 'k3', 'user-1', 'host-3', 1, 'cell-a', 'claimed', ?, ?, ?), - ('r4', 'k4', 'user-1', 'host-4', 1, 'cell-a', 'released', ?, ?, ?)`, - [now, now, now, now, now, now, now, now, now, now, now, now] - ) - - const snapshot = await readAssignmentInventorySnapshot(database, now) - - expect(snapshot.cells).toEqual([ - { - cellId: 'cell-a', - region: 'us-central1', - admissionState: 'general', - enabled: true, - capacityRequests: 4000, - reservedRequests: 3999, - runtimeReady: true, - heartbeatAgeMs: 10_000 - } - ]) - expect(snapshot.activityLeases).toEqual({ total: 2, expired: 1, requestUnits: 4 }) - expect(snapshot.connectionReservations).toEqual({ outstanding: 3, lateArrivalDebt: 1 }) - expect(snapshot.regionalRehomes).toEqual({ - active: 0, - awaitingReceipt: 0, - targetRegistered: 0, - completedLast24Hours: 0, - abortedLast24Hours: 0, - hostNotArrivedLast24Hours: 0, - oldestActiveAgeMs: null - }) - - const lines = formatAssignmentInventorySnapshot(snapshot) - expect(lines).toHaveLength(3) - expect(lines[0]).toContain('cellId=cell-a') - expect(lines[0]).toContain('reserved=3999') - expect(lines[1]).toContain('expiredLeases=1') - expect(lines[1]).toContain('lateArrivalDebt=1') - expect(lines[2]).toContain('active=0') - }) - - it('reports cells missing runtime and admission rows without failing', async () => { - const database = await openInMemoryRelayDatabase() - await database.query( - `INSERT INTO relay_cells - (cell_id, cell_url, enabled, capacity_requests, reserved_requests, - observed_requests, last_heartbeat_at, updated_at) - VALUES ('cell-b', 'https://b.example.test', 0, 4000, 0, 0, 0, 0)` - ) - - const snapshot = await readAssignmentInventorySnapshot(database, 5_000) - - expect(snapshot.cells[0]).toMatchObject({ - cellId: 'cell-b', - admissionState: 'unset', - enabled: false, - runtimeReady: null, - heartbeatAgeMs: null - }) - expect(snapshot.activityLeases).toEqual({ total: 0, expired: 0, requestUnits: 0 }) - expect(snapshot.regionalRehomes.active).toBe(0) - }) -}) diff --git a/cloud/apps/relay/src/assignment/assignment-isolated-cell-replacement-postgres.test.ts b/cloud/apps/relay/src/assignment/assignment-isolated-cell-replacement-postgres.test.ts index 8e533d9468b..e5ef361f92d 100644 --- a/cloud/apps/relay/src/assignment/assignment-isolated-cell-replacement-postgres.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-isolated-cell-replacement-postgres.test.ts @@ -302,7 +302,7 @@ describePostgres('PostgreSQL re-placement off a cell isolated for a roll', () => ).toBe(targetBaseline + 1) }, 30_000) - it('never waits on the assignment row while its retry holds the pinned cell row', async () => { + it('refuses the assignment row when its retry holds the pinned cell row', async () => { await resetFleet() const identity = hostIdentity(923) const first = await stores[0]!.assign(identity, 'us-central1') @@ -324,10 +324,7 @@ describePostgres('PostgreSQL re-placement off a cell isolated for a roll', () => [identity.userId, identity.relayHostId] ) await releaseCell() - const cellFreedAt = performance.now() expect(await regrant).toBeInstanceOf(RelayAssignmentRowBusyError) - // NOWAIT once the cell row is held: nowhere near the 1s bounded wait. - expect(performance.now() - cellFreedAt).toBeLessThan(300) } finally { await releaseAssignment?.() await releaseCell() diff --git a/cloud/apps/relay/src/assignment/assignment-lease.test.ts b/cloud/apps/relay/src/assignment/assignment-lease.test.ts index 96e2807eac8..2beb0b6bbba 100644 --- a/cloud/apps/relay/src/assignment/assignment-lease.test.ts +++ b/cloud/apps/relay/src/assignment/assignment-lease.test.ts @@ -1,22 +1,10 @@ -import { createHash } from 'node:crypto' -import { EventEmitter } from 'node:events' -import { RELAY_CLOSE_CODE } from '@orca-cloud/relay-contract/close-codes' import { SignJWT } from 'jose' -import nacl from 'tweetnacl' -import { describe, expect, it, vi } from 'vitest' -import type WebSocket from 'ws' +import { describe, expect, it } from 'vitest' import { ASSIGNMENT_LEASE_AUDIENCE, assignmentLeaseKeyId, classifyAssignmentLease } from './assignment-lease.js' -import { AssignmentLeaseShadow } from './assignment-lease-shadow.js' -import type { RelayAssignmentStore } from './assignment-store.js' -import type { RelayConfig } from '../config.js' -import type { RelayCredentialStore } from '../credentials/credential-store.js' -import { HostSessionRegistry } from '../connections/host-session-registry.js' -import type { RelayTokenClaims } from '../authentication/relay-token-verifier.js' -import { ProcessQueuedByteBudget } from '../connections/splice-forwarder.js' const KEY = new TextEncoder().encode('0123456789abcdef0123456789abcdef') const OTHER_KEY = new TextEncoder().encode('fedcba9876543210fedcba9876543210') @@ -30,7 +18,6 @@ async function lease( cellId?: string assignmentEpoch?: number sub?: string - relayHostId?: string expiresAt?: string | number } = {} ): Promise { @@ -40,7 +27,7 @@ async function lease( cellId: overrides.cellId ?? CELL_ID, cellUrl: 'https://c7.relay.example.test', assignmentEpoch: overrides.assignmentEpoch ?? 4, - relayHostId: overrides.relayHostId ?? HOST.relayHostId + relayHostId: HOST.relayHostId }) .setProtectedHeader(kid === null ? { alg: 'HS256' } : { alg: 'HS256', kid }) .setIssuer('https://relay.example.test') @@ -80,124 +67,3 @@ describe('assignment lease classification', () => { expect(await classify(await lease({ assignmentEpoch: 5 }))).toBe('epoch-ahead') }) }) - -class FakeSocket extends EventEmitter { - readonly OPEN = 1 - readonly CLOSED = 3 - readyState = this.OPEN - readonly send = vi.fn() - readonly close = vi.fn((code?: number) => { - this.readyState = this.CLOSED - this.emit('close', code, Buffer.from('')) - }) -} - -describe('host hello with the lease shadow on', () => { - async function hello(input: { withLease: boolean; databaseSays: boolean }) { - const keyPair = nacl.box.keyPair() - const relayHostId = createHash('sha256') - .update(keyPair.publicKey) - .digest('base64url') - .slice(0, 16) - const identity: RelayTokenClaims = { - sub: HOST.userId, - prof: 'profile-1', - relayHostId, - purpose: 'host-control', - exp: 4_102_444_800 - } - const recorded: string[] = [] - const shadow = new AssignmentLeaseShadow({ enabled: () => true, key: KEY, cellId: CELL_ID }) - const record = shadow.record.bind(shadow) - vi.spyOn(shadow, 'record').mockImplementation((check, dbValid, dbReadMs) => { - void check.then((leaseClass) => recorded.push(`${leaseClass}:${dbValid}`)) - record(check, dbValid, dbReadMs) - }) - const verifyCellAssignment = vi.fn(async () => input.databaseSays) - const registry = new HostSessionRegistry( - config(), - vi.fn(), - {} as RelayCredentialStore, - { verifyCellAssignment } as unknown as RelayAssignmentStore, - new ProcessQueuedByteBudget(), - { - recordAuth: vi.fn(), - recordForwardedBytes: vi.fn(), - recordHttp: vi.fn(), - recordReconnect: vi.fn(), - recordSql: vi.fn() - }, - Date.now, - Math.random, - 'incarnation-1', - shadow - ) - const socket = new FakeSocket() - const hostLease = input.withLease ? await lease({ relayHostId }) : undefined - registry.acceptControl( - socket as unknown as WebSocket, - identity, - undefined, - undefined, - hostLease - ) - socket.emit( - 'message', - Buffer.from( - JSON.stringify({ - type: 'host-hello', - v: 1, - relayHostId, - assignmentEpoch: 4, - hostPublicKeyB64: Buffer.from(keyPair.publicKey).toString('base64'), - appVersion: '1.4.200' - }) - ), - false - ) - await vi.waitFor(() => expect(recorded).toHaveLength(1)) - return { socket } - } - - it('still refuses a host the database refuses, whatever its lease says', async () => { - const { socket } = await hello({ withLease: true, databaseSays: false }) - expect(socket.close).toHaveBeenCalledWith(RELAY_CLOSE_CODE.WRONG_CELL, 'wrong assignment epoch') - }) - - it('still admits a host the database admits, with no lease at all', async () => { - const { socket } = await hello({ - withLease: false, - databaseSays: true - }) - expect(socket.close).not.toHaveBeenCalled() - expect(String(socket.send.mock.calls[0]?.[0])).toContain('"type":"host-challenge"') - }) -}) - -function config(): RelayConfig { - return { - port: 8080, - publicUrl: 'https://c7.relay.example.test', - cellUrl: 'https://c7.relay.example.test', - authIssuer: 'https://auth.example.test', - authAudience: 'orca-relay', - jwksUrl: 'https://auth.example.test/jwks', - assignmentSigningKey: KEY, - role: 'cell', - cellId: CELL_ID, - cells: [], - adminAudience: 'https://relay.example.test/v1/admin/drain', - deployServiceAccount: 'deploy@example.test', - runtimeServiceAccount: 'relay-cell@example.test', - adminJwksUrl: 'https://auth.example.test/jwks', - databasePoolMax: 10, - publicAssignmentsEnabled: true, - publicAssignmentConcurrency: 2, - publicAssignmentQueueMax: 128, - publicAssignmentWaitMs: 4_000, - publicResolveConcurrency: 1, - publicResolveWaitMs: 5_000, - publicAssignmentRetryAfterSeconds: 5, - dataDir: './data' - } -} diff --git a/cloud/apps/relay/src/assignment/migrations/dead-cell-evacuation-precheck.test.ts b/cloud/apps/relay/src/assignment/migrations/dead-cell-evacuation-precheck.test.ts index 7a186562d18..4a1a2aff115 100644 --- a/cloud/apps/relay/src/assignment/migrations/dead-cell-evacuation-precheck.test.ts +++ b/cloud/apps/relay/src/assignment/migrations/dead-cell-evacuation-precheck.test.ts @@ -80,26 +80,6 @@ describe.each(dialects)('dead-cell evacuation pre-check (%s)', (_dialect, open) }) } - it('keeps hosts on an unfenced dead existing-only cell', async () => { - let now = 100 - const { store } = await setup(() => now) - for (const cell of CELLS) await heartbeat(store, cell) - const identity = { userId: 'user-a', relayHostId: 'host000000000001' } - await store.setCellEnabled('cell-b', false) - expect(await store.assign(identity)).toMatchObject({ cellId: 'cell-a' }) - await store.setCellEnabled('cell-b', true) - await store.setCellEnabled('cell-a', false) - now += 45_001 - await heartbeat(store, CELLS[1]!) - - expect(await store.evacuateDeadCells()).toBe(0) - expect( - await database!.query(`SELECT cell_id FROM relay_assignments WHERE user_id = ?`, [ - identity.userId - ]) - ).toEqual([{ cell_id: 'cell-a' }]) - }) - it('still evacuates hosts from a dead uncapped cell that admits', async () => { let now = 100 const { store } = await setup(() => now) diff --git a/cloud/apps/relay/src/assignment/public-admission/public-assignment-circuit-breaker.test.ts b/cloud/apps/relay/src/assignment/public-admission/public-assignment-circuit-breaker.test.ts deleted file mode 100644 index 5117e5bbe79..00000000000 --- a/cloud/apps/relay/src/assignment/public-admission/public-assignment-circuit-breaker.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { createRelayApp } from '../../app.js' -import type { RelayConfig } from '../../config.js' - -describe('public assignment circuit breaker', () => { - it('rejects public assignment routes while disabled', async () => { - const app = createRelayApp(config(), { - store: {} as never, - assignments: {} as never, - drain: vi.fn(), - ready: vi.fn(async () => true) - }) - - for (const path of ['/v1/assign', '/v1/resolve']) { - const response = await app.request(path, { method: 'POST' }) - expect(response.status).toBe(503) - expect(response.headers.get('retry-after')).toBe('5') - expect(await response.json()).toEqual({ error: 'assignments_temporarily_unavailable' }) - } - }) -}) - -function config(): RelayConfig { - return { - port: 8080, - publicUrl: 'https://relay.example.test', - cellUrl: 'https://relay.example.test', - authIssuer: 'https://auth.example.test', - authAudience: 'orca-relay', - jwksUrl: 'https://auth.example.test/jwks', - assignmentSigningKey: new TextEncoder().encode('assignment-key-with-at-least-32-bytes'), - role: 'director', - cellId: 'director', - cells: [], - adminAudience: 'https://relay.example.test/v1/admin/drain', - deployServiceAccount: 'deploy@example.test', - runtimeServiceAccount: 'runtime@example.test', - adminJwksUrl: 'https://auth.example.test/jwks', - databasePoolMax: 3, - publicAssignmentsEnabled: false, - publicAssignmentConcurrency: 2, - publicAssignmentQueueMax: 128, - publicAssignmentWaitMs: 4_000, - publicResolveConcurrency: 1, - publicResolveWaitMs: 5_000, - publicAssignmentRetryAfterSeconds: 5, - dataDir: './data' - } -} diff --git a/cloud/apps/relay/src/assignment/public-admission/public-assignment-reconnect-lane.test.ts b/cloud/apps/relay/src/assignment/public-admission/public-assignment-reconnect-lane.test.ts index 06b85e55356..1a2b795252a 100644 --- a/cloud/apps/relay/src/assignment/public-admission/public-assignment-reconnect-lane.test.ts +++ b/cloud/apps/relay/src/assignment/public-admission/public-assignment-reconnect-lane.test.ts @@ -64,26 +64,6 @@ describe('public assignment reconnect lane', () => { expect((await second).status).toBe(200) }) - it('rate-limits repeat fast-lane attempts with the short retry-after', async () => { - const host = 'rrrrrrrrrrrrrrrr' - const assign = vi.fn(async () => assignment('cell-r', host)) - const resolve = vi.fn(async () => assignment('cell-r', host)) - const app = createRelayApp(config(), { - store: {} as never, - assignments: { assign, resolve } as never, - drain: vi.fn(), - ready: vi.fn(async () => true) - }) - - expect( - (await app.request('/v1/assign', assignmentRequest(host, { reconnect: true }))).status - ).toBe(200) - const repeat = await app.request('/v1/assign', assignmentRequest(host, { reconnect: true })) - - expect(repeat.status).toBe(503) - expect(repeat.headers.get('retry-after')).toBe('2') - }) - it('rejects on the fast lane when the verification probe fails transiently', async () => { const host = 'tttttttttttttttt' const assign = vi.fn() diff --git a/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-store.test.ts b/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-store.test.ts index 395bb453998..18a95333bbb 100644 --- a/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-store.test.ts +++ b/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-store.test.ts @@ -322,20 +322,6 @@ describe('constrained idle regional assignment transaction', () => { expect(await database.query('SELECT * FROM relay_region_rehome_attempts')).toEqual([]) }) - it('selects read-only with stable identity and the control generation, not probe generation', async () => { - const { store, safety, database } = await setup() - const before = await database.query('SELECT * FROM relay_assignments') - const candidates = await store.selectIdleRegionalRehomeCandidates(safety) - expect(candidates).toHaveLength(1) - expect(candidates[0]).toMatchObject({ - sourceGeneration: 7, - sourceCellId: 'source', - targetCellId: 'target' - }) - expect(await store.selectIdleRegionalRehomeCandidates(safety)).toEqual(candidates) - expect(await database.query('SELECT * FROM relay_assignments')).toEqual(before) - expect(await database.query('SELECT * FROM relay_region_rehome_attempts')).toEqual([]) - }) }) function holdStatement(database: RelayDatabase, fragment: string) { diff --git a/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-window.test.ts b/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-window.test.ts index a53521e721d..6c4c559f06f 100644 --- a/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-window.test.ts +++ b/cloud/apps/relay/src/assignment/regional-rehome/idle-regional-rehome-window.test.ts @@ -239,23 +239,4 @@ describe('idle regional rehome candidate window', () => { expect(await store.selectIdleRegionalRehomeCandidates(safety)).toHaveLength(3) }) - it('excludes a host outside the cohort', async () => { - const { store, database } = await setup() - await seedHost(store, identity) - const now = 100_000_000 + 86_400_000 - const safety = { - observedAt: now, - sqlFailures: 0, - reconnects: 0, - controlActivityRecoveryFailures: 0, - databasePoolWaiting: 0, - databasePoolWaitersMax: 0, - databasePoolWaitMsMax: 0 - } - await database.query('UPDATE relay_region_decisions SET cohort_bucket = 40') - const narrow = new RelayAssignmentStore(database, () => now, { regionalRehomeCohortPercent: 40 }) - expect(await narrow.selectIdleRegionalRehomeCandidates(safety)).toEqual([]) - const wide = new RelayAssignmentStore(database, () => now, { regionalRehomeCohortPercent: 41 }) - expect(await wide.selectIdleRegionalRehomeCandidates(safety)).toHaveLength(3) - }) }) From c2deee70df750dff910418d2996a037117cb50d6 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 13 Nov 2026 05:54:19 -0800 Subject: [PATCH 093/178] refactor(mobile): organize platform adapters by capability (#42030) --- .../mobile-web-app-external-link-seam.mjs | 2 +- .../mobile-web-app-haptics-seam.mjs | 8 +- .../mobile-web-app-media-picker-seam.mjs | 2 +- ...bile-web-app-text-input-font-size-seam.mjs | 2 +- .../mobile-web-app-page-grant-call-sites.mjs | 8 +- ...le-web-app-composer-sizing-render.test.mjs | 2 +- mobile/app/mobile-onboarding.tsx | 2 +- mobile/app/pair-confirm.tsx | 2 +- mobile/app/pair-scan.tsx | 2 +- .../MobileAgentSessionHistoryPanel.tsx | 2 +- mobile/src/agents/MobileAgentListRows.tsx | 2 +- .../src/agents/use-agent-list-row-actions.tsx | 2 +- .../automations/AutomationManager.test.tsx | 4 +- mobile/src/automations/automation-styles.ts | 2 +- .../pane-view/MobileBrowserLoadProgress.tsx | 2 +- .../pane-view/mobile-browser-pane-styles.ts | 2 +- mobile/src/components/AgentLaunchNotice.tsx | 2 +- mobile/src/components/AgentSpinner.tsx | 2 +- mobile/src/components/AgentStateDot.tsx | 2 +- mobile/src/components/AnchoredDetailCard.tsx | 2 +- mobile/src/components/AutoHeight.tsx | 2 +- mobile/src/components/DisclosureChevron.tsx | 2 +- mobile/src/components/DragReorderList.tsx | 2 +- mobile/src/components/DragReorderRow.tsx | 2 +- mobile/src/components/LoadingSpinner.tsx | 2 +- .../components/MobileDictationSetupSheet.tsx | 2 +- .../src/components/MobileDiffReviewLines.tsx | 2 +- .../components/MobileRichMarkdownEditor.tsx | 2 +- .../src/components/MobileSegmentedControl.tsx | 2 +- .../src/components/MobileSourceCopyButton.tsx | 2 +- mobile/src/components/MobileSwitch.tsx | 4 +- mobile/src/components/ProtocolBlockScreen.tsx | 2 +- mobile/src/components/WorktreeListRow.tsx | 2 +- .../bottom-drawer-back-while-hiding.test.tsx | 6 +- .../bottom-drawer/bottom-drawer-constants.ts | 2 +- .../bottom-drawer/mounted-bottom-drawer.tsx | 5 +- .../bottom-drawer/option-pager.test.tsx | 123 ---------- .../components/composer-input/MessageBox.tsx | 2 +- ...drag-reorder-test-harness.test-fixture.tsx | 5 +- mobile/src/components/list/ListGroup.tsx | 2 +- .../src/components/markdown-link-handler.ts | 2 +- .../src/components/mobile-markdown-inline.tsx | 2 +- mobile/src/components/mobile-pr-url.ts | 2 +- .../components/nav-bar/BottomSearchBar.tsx | 4 +- mobile/src/components/nav-bar/NavBar.tsx | 2 +- .../src/components/nav-bar/PullDownMenu.tsx | 2 +- .../pr-sidebar/MobileLinkPrForm.tsx | 2 +- .../components/pr-sidebar/PRCommentCard.tsx | 2 +- .../pr-sidebar/PrCheckFailureRow.tsx | 6 +- .../components/pr-sidebar/PrChecksScreen.tsx | 2 +- .../pr-sidebar/PrCommentsScreen.tsx | 2 +- .../components/pr-sidebar/PrCreateSheet.tsx | 2 +- .../src/components/pr-sidebar/PrThreadRow.tsx | 2 +- .../components/pr-sidebar/PrThreadScreen.tsx | 4 +- .../components/pr-sidebar/pr-tab-styles.ts | 2 +- .../components/pr-sidebar/use-pr-tab-menu.ts | 4 +- .../rich-markdown/document-style.ts | 2 +- .../components/use-detail-card-scope.web.ts | 2 +- .../use-new-workspace-create-submit.ts | 2 +- mobile/src/dictation/MobileDictationMeter.tsx | 2 +- .../src/dictation/mobile-dictation-host.tsx | 4 +- .../use-mobile-dictation-announcements.ts | 2 +- .../use-mobile-dictation-cancel-drop.ts | 3 +- .../dictation/use-mobile-dictation-haptics.ts | 2 +- .../MobileChatAttachmentPreview.tsx | 2 +- .../attachments/mobile-agent-image-fit.ts | 2 +- .../attachments/mobile-file-source-picker.ts | 2 +- .../attachments/mobile-image-attachment.ts | 5 +- .../attachments/mobile-image-source-picker.ts | 9 +- .../use-mobile-image-attachment.ts | 4 +- .../use-mobile-session-attachments.ts | 4 +- .../use-mobile-session-image-attachments.ts | 2 +- .../explorer/MobileFileExplorerPanel.test.ts | 6 +- ...mobile-file-explorer-row-actions-sheet.tsx | 4 +- .../explorer/mobile-file-explorer-row.tsx | 2 +- mobile/src/files/media/MobilePdfPreview.tsx | 4 +- .../use-mobile-file-tap-handlers.ts | 2 +- .../use-mobile-session-file-actions.ts | 2 +- .../use-mobile-session-markdown-actions.ts | 4 +- .../MobileFilePreviewEditDraft.tsx | 6 +- mobile/src/home/MobileHomeScreen.tsx | 2 +- .../home/tabs/MobileHomeAutomationsTab.tsx | 2 +- mobile/src/home/tabs/MobileHomeFab.tsx | 2 +- mobile/src/home/tabs/MobileHomeTabBar.tsx | 2 +- mobile/src/home/tabs/MobileHomeUsageTab.tsx | 2 +- .../src/hooks/mobile-dictation-audio-chunk.ts | 2 +- .../mobile-dictation-background.test.tsx | 29 +-- .../hooks/mobile-dictation-capture-open.ts | 2 +- .../hooks/mobile-dictation-device-engine.ts | 4 +- .../src/hooks/mobile-dictation-engine-run.ts | 4 +- mobile/src/hooks/mobile-dictation-finish.ts | 2 +- .../src/hooks/mobile-dictation-held-audio.ts | 2 +- mobile/src/hooks/mobile-dictation-meter.ts | 2 +- .../hooks/mobile-dictation-on-device.test.tsx | 65 +---- .../src/hooks/mobile-dictation-recording.ts | 2 +- .../src/hooks/mobile-dictation-retry.test.tsx | 4 +- .../mobile-dictation-send-window.test.tsx | 38 +-- .../hooks/mobile-dictation-stop-tail.test.tsx | 30 +-- .../use-mobile-dictation-interruptions.ts | 2 +- mobile/src/hooks/use-mobile-dictation.ts | 4 +- mobile/src/hooks/use-phone-dictation-ready.ts | 2 +- .../src/host-screen/host-screen-overlays.tsx | 2 +- .../use-host-worktree-delete.ts | 2 +- mobile/src/layout/side-rail.tsx | 2 +- .../mobile-web-shell/DownloadProgressBar.tsx | 2 +- .../mobile-web-shell/MobileWebShellScreen.tsx | 6 +- .../bridge/bridge-audio-verbs.test.ts | 112 +-------- mobile/src/mobile-web-shell/page-haptics.ts | 2 +- .../new-workspace/NewWorkspacePageScreen.tsx | 2 +- .../NotificationOnboardingPreview.tsx | 2 +- mobile/src/pairing/pairing-visuals.tsx | 2 +- .../accessibility-announcer.ts | 0 .../accessibility-announcer.web.ts | 2 +- .../native-accessibility-announce.ts | 2 +- .../{ => accessibility}/use-reduced-motion.ts | 0 .../dictation-capture-compile-fence.ts | 4 +- .../dictation-capture-contract.ts | 0 .../{ => audio-capture}/dictation-capture.ts | 2 +- .../dictation-capture.web.ts | 11 +- .../microphone-screen-lock.ts | 0 .../native-audio-device.ts | 2 +- .../native-audio-session-ownership.test.ts | 2 +- .../{ => audio-capture}/native-audio.ts | 10 +- .../{ => clipboard}/clipboard.test.tsx | 59 +---- .../src/platform/{ => clipboard}/clipboard.ts | 0 .../platform/{ => clipboard}/clipboard.web.ts | 4 +- .../{ => clipboard}/native-clipboard.ts | 2 +- mobile/src/platform/external-link.test.ts | 49 ---- mobile/src/platform/external-link.web.test.ts | 54 ----- .../{ => external-links}/external-link.ts | 0 .../external-links/external-link.web.test.ts | 25 ++ .../{ => external-links}/external-link.web.ts | 2 +- .../global-timer-receiver-test-fakes.ts | 32 --- mobile/src/platform/{ => haptics}/haptics.ts | 0 .../src/platform/{ => haptics}/haptics.web.ts | 2 +- mobile/src/platform/host-os.web.ts | 2 - .../image-actions-contract.ts | 2 +- .../{ => image-actions}/image-actions.ts | 0 .../{ => image-actions}/image-actions.web.ts | 12 +- .../native-image-actions.ts | 16 +- .../src/platform/keyboard-occlusion.test.tsx | 141 ----------- .../keyboard-close-blur.web.test.ts | 34 --- .../{ => keyboard}/keyboard-close-blur.web.ts | 2 +- .../{ => keyboard}/keyboard-occlusion.ts | 4 +- .../{ => keyboard}/keyboard-occlusion.web.ts | 0 .../keyboard-persisting-taps.ts | 0 .../{ => keyboard}/primary-modifier-key.ts | 2 +- .../src/platform/media-picker-test-shell.ts | 119 --------- .../media-picker-contract.ts | 2 +- .../{ => media-picking}/media-picker.ts | 7 +- .../{ => media-picking}/media-picker.web.ts | 6 +- .../mobile-picked-file-name.ts | 0 .../native-media-device.ts | 4 +- .../native-media-stage-cleanup.test.ts | 2 +- .../{ => media-picking}/native-media.test.ts | 228 +----------------- .../{ => media-picking}/native-media.ts | 16 +- .../platform/microphone-screen-lock.test.ts | 99 -------- .../src/platform/native-media-device.test.ts | 60 ----- .../{ => pdf-viewing}/native-pdf-view.ts | 20 +- .../{ => pdf-viewing}/pdf-opener-contract.ts | 0 .../platform/{ => pdf-viewing}/pdf-opener.ts | 0 .../{ => pdf-viewing}/pdf-opener.web.ts | 6 +- .../{ => shell-storage}/shell-storage.ts | 0 .../{ => shell-storage}/shell-storage.web.ts | 2 +- .../{ => speech}/native-speech-device.ts | 4 +- .../{ => speech}/native-speech.test.ts | 2 +- .../platform/{ => speech}/native-speech.ts | 2 +- .../{ => speech}/on-device-speech-contract.ts | 2 +- .../{ => speech}/on-device-speech-locale.ts | 2 +- .../on-device-speech-locale.web.ts | 0 .../platform/{ => speech}/on-device-speech.ts | 2 +- .../on-device-speech.web.test.tsx | 2 +- .../{ => speech}/on-device-speech.web.ts | 6 +- .../live-input-composing-range.test.ts | 0 .../live-input-composing-range.ts | 0 .../text-input-content-height.ts | 0 .../text-input-content-height.web.ts | 0 .../{ => text-input}/text-input-font-size.ts | 2 +- .../text-input-font-size.web.ts | 2 +- .../{ => text-input}/text-input-handle.ts | 0 .../text-input-selection-reveal.web.ts | 0 .../src/platform/use-native-device-verbs.ts | 19 +- .../view-measurement/page-box-contract.ts | 1 + .../{ => view-measurement}/page-box.ts | 2 +- .../{ => view-measurement}/page-box.web.ts | 4 +- mobile/src/session/MobileSessionSheets.tsx | 4 +- .../src/session/MobileSessionTitleBlock.tsx | 2 +- .../__mocks__/mobile-prompt-render-mocks.ts | 4 +- .../agent-launch/new-tab-agent-host-launch.ts | 2 +- ...mobile-dictation-session-outcomes.test.tsx | 4 +- .../session/mobile-structured-chat-fork.ts | 2 +- .../MobileNativeChatFloatingControls.tsx | 2 +- .../MobileNativeChatComposerField.web.tsx | 8 +- .../MobileNativeChatComposerMenuLayer.web.tsx | 4 +- .../MobileNativeChatContextUsageRing.tsx | 4 +- .../composer/MobileNativeChatSendButton.tsx | 2 +- .../mobile-native-chat-composer-paste.ts | 4 +- .../mobile-native-chat-file-attachment.ts | 2 +- .../mobile-native-chat-image-attachment.ts | 5 +- ...use-mobile-native-chat-file-attachments.ts | 4 +- ...se-mobile-native-chat-image-attachments.ts | 2 +- .../use-mobile-native-chat-image-upload.ts | 6 +- ...ile-native-chat-steer-chord-preview.web.ts | 2 +- .../input/MobileNativeChatPromptCard.tsx | 2 +- .../input/mobile-native-chat-control-swap.tsx | 2 +- .../use-mobile-native-chat-reply-scroll.ts | 2 +- .../transcript/MobileLinkActionSheet.tsx | 6 +- .../MobileNativeChatMessageActionsSheet.tsx | 2 +- .../MobileNativeChatRowOverlaysHost.test.tsx | 2 +- .../MobileNativeChatRowOverlaysHost.tsx | 2 +- .../MobileNativeChatToolRun.test.tsx | 65 +---- .../mobile-native-chat-reply-reveals.ts | 2 +- .../visual/MobileImageViewerActions.tsx | 4 +- .../visual/MobileNativeChatImageViewer.tsx | 2 +- .../src/session/tabs/MobileSessionTabBar.tsx | 6 +- .../tabs/use-mobile-session-tab-switching.ts | 2 +- .../session/use-mobile-session-dictation.ts | 4 +- .../session/use-mobile-session-lifecycle.ts | 2 +- .../use-mobile-session-preference-focus.ts | 2 +- .../use-mobile-session-presentation.ts | 2 +- .../settings/voice-transcribe-on-section.tsx | 4 +- .../source-control/MobileCommitDetailPage.tsx | 6 +- .../MobileCommitsScreen.test.tsx | 8 +- .../MobileSourceControlContent.tsx | 2 +- .../folder/use-folder-create-flow.test.tsx | 4 +- .../folder/use-folder-create-flow.tsx | 4 +- ...ource-control-oversized-stage-all.test.tsx | 118 --------- .../use-mobile-diff-review-comment-actions.ts | 2 +- .../use-mobile-diff-review-git-actions.ts | 2 +- .../review/use-mobile-diff-review-github.ts | 2 +- .../use-mobile-diff-review-send-actions.ts | 4 +- .../review/use-mobile-pr-ai-triage.ts | 2 +- .../review/use-mobile-pr-comment-actions.ts | 2 +- .../review/use-mobile-pr-text-action.ts | 2 +- .../use-mobile-session-diff-comments.ts | 4 +- .../use-mobile-commit-failure-recovery.ts | 2 +- ...e-mobile-commit-message-generation.test.ts | 2 +- .../use-mobile-commit-message-generation.ts | 2 +- ...se-mobile-source-control-commit-runners.ts | 2 +- ...use-mobile-source-control-keyboard-lift.ts | 2 +- .../use-mobile-source-control-runners.ts | 2 +- .../item-detail/mobile-tasks-item-actions.tsx | 2 +- .../mobile-tasks-item-detail-content.tsx | 2 +- ...le-tasks-hosted-comment-review-actions.tsx | 2 +- .../mobile-tasks-project-detail-drawer.tsx | 2 +- .../mobile-tasks-project-review-panels.tsx | 2 +- .../screen/mobile-tasks-filter-sheet.tsx | 2 +- .../mobile-tasks-provider-item-list.tsx | 2 +- .../mobile-tasks-create-drawers.tsx | 2 +- .../quick-commands/QuickCommandRow.tsx | 4 +- .../mobile-session-quick-command-launch.ts | 2 +- .../session/MobileSessionAccessoryKeys.tsx | 4 +- .../session/TerminalMoreKeysSheet.tsx | 2 +- ...mobile-session-terminal-create-feedback.ts | 2 +- .../use-mobile-session-accessory-selection.ts | 4 +- .../use-mobile-session-keyboard-lift.ts | 2 +- .../use-mobile-session-keyboard-state.ts | 5 +- ...-mobile-session-terminal-create-actions.ts | 2 +- .../use-mobile-session-terminal-runtime.ts | 2 +- ...se-mobile-session-terminal-send-actions.ts | 4 +- .../session/use-mobile-terminal-paste.ts | 2 +- .../session/use-session-terminal-key-rail.ts | 2 +- .../use-terminal-live-input-click.test.ts | 2 +- .../use-terminal-live-input-commit.test.ts | 2 +- .../use-terminal-live-input-commit.ts | 2 +- .../terminal/use-terminal-live-input-focus.ts | 2 +- mobile/web-entry/index.tsx | 8 +- mobile/web-entry/web-overrides.json | 32 +-- 268 files changed, 442 insertions(+), 1797 deletions(-) delete mode 100644 mobile/src/components/bottom-drawer/option-pager.test.tsx rename mobile/src/platform/{ => accessibility}/accessibility-announcer.ts (100%) rename mobile/src/platform/{ => accessibility}/accessibility-announcer.web.ts (85%) rename mobile/src/platform/{ => accessibility}/native-accessibility-announce.ts (85%) rename mobile/src/platform/{ => accessibility}/use-reduced-motion.ts (100%) rename mobile/src/platform/{ => audio-capture}/dictation-capture-compile-fence.ts (86%) rename mobile/src/platform/{ => audio-capture}/dictation-capture-contract.ts (100%) rename mobile/src/platform/{ => audio-capture}/dictation-capture.ts (95%) rename mobile/src/platform/{ => audio-capture}/dictation-capture.web.ts (92%) rename mobile/src/platform/{ => audio-capture}/microphone-screen-lock.ts (100%) rename mobile/src/platform/{ => audio-capture}/native-audio-device.ts (96%) rename mobile/src/platform/{ => audio-capture}/native-audio-session-ownership.test.ts (97%) rename mobile/src/platform/{ => audio-capture}/native-audio.ts (94%) rename mobile/src/platform/{ => clipboard}/clipboard.test.tsx (54%) rename mobile/src/platform/{ => clipboard}/clipboard.ts (100%) rename mobile/src/platform/{ => clipboard}/clipboard.web.ts (93%) rename mobile/src/platform/{ => clipboard}/native-clipboard.ts (91%) delete mode 100644 mobile/src/platform/external-link.test.ts delete mode 100644 mobile/src/platform/external-link.web.test.ts rename mobile/src/platform/{ => external-links}/external-link.ts (100%) create mode 100644 mobile/src/platform/external-links/external-link.web.test.ts rename mobile/src/platform/{ => external-links}/external-link.web.ts (91%) delete mode 100644 mobile/src/platform/global-timer-receiver-test-fakes.ts rename mobile/src/platform/{ => haptics}/haptics.ts (100%) rename mobile/src/platform/{ => haptics}/haptics.web.ts (89%) rename mobile/src/platform/{ => image-actions}/image-actions-contract.ts (87%) rename mobile/src/platform/{ => image-actions}/image-actions.ts (100%) rename mobile/src/platform/{ => image-actions}/image-actions.web.ts (87%) rename mobile/src/platform/{ => image-actions}/native-image-actions.ts (88%) delete mode 100644 mobile/src/platform/keyboard-occlusion.test.tsx rename mobile/src/platform/{ => keyboard}/keyboard-close-blur.web.test.ts (68%) rename mobile/src/platform/{ => keyboard}/keyboard-close-blur.web.ts (97%) rename mobile/src/platform/{ => keyboard}/keyboard-occlusion.ts (96%) rename mobile/src/platform/{ => keyboard}/keyboard-occlusion.web.ts (100%) rename mobile/src/platform/{ => keyboard}/keyboard-persisting-taps.ts (100%) rename mobile/src/platform/{ => keyboard}/primary-modifier-key.ts (87%) delete mode 100644 mobile/src/platform/media-picker-test-shell.ts rename mobile/src/platform/{ => media-picking}/media-picker-contract.ts (95%) rename mobile/src/platform/{ => media-picking}/media-picker.ts (71%) rename mobile/src/platform/{ => media-picking}/media-picker.web.ts (94%) rename mobile/src/platform/{ => media-picking}/mobile-picked-file-name.ts (100%) rename mobile/src/platform/{ => media-picking}/native-media-device.ts (94%) rename mobile/src/platform/{ => media-picking}/native-media-stage-cleanup.test.ts (98%) rename mobile/src/platform/{ => media-picking}/native-media.test.ts (54%) rename mobile/src/platform/{ => media-picking}/native-media.ts (93%) delete mode 100644 mobile/src/platform/microphone-screen-lock.test.ts delete mode 100644 mobile/src/platform/native-media-device.test.ts rename mobile/src/platform/{ => pdf-viewing}/native-pdf-view.ts (79%) rename mobile/src/platform/{ => pdf-viewing}/pdf-opener-contract.ts (100%) rename mobile/src/platform/{ => pdf-viewing}/pdf-opener.ts (100%) rename mobile/src/platform/{ => pdf-viewing}/pdf-opener.web.ts (84%) rename mobile/src/platform/{ => shell-storage}/shell-storage.ts (100%) rename mobile/src/platform/{ => shell-storage}/shell-storage.web.ts (77%) rename mobile/src/platform/{ => speech}/native-speech-device.ts (81%) rename mobile/src/platform/{ => speech}/native-speech.test.ts (97%) rename mobile/src/platform/{ => speech}/native-speech.ts (99%) rename mobile/src/platform/{ => speech}/on-device-speech-contract.ts (89%) rename mobile/src/platform/{ => speech}/on-device-speech-locale.ts (77%) rename mobile/src/platform/{ => speech}/on-device-speech-locale.web.ts (100%) rename mobile/src/platform/{ => speech}/on-device-speech.ts (80%) rename mobile/src/platform/{ => speech}/on-device-speech.web.test.tsx (96%) rename mobile/src/platform/{ => speech}/on-device-speech.web.ts (90%) rename mobile/src/platform/{ => text-input}/live-input-composing-range.test.ts (100%) rename mobile/src/platform/{ => text-input}/live-input-composing-range.ts (100%) rename mobile/src/platform/{ => text-input}/text-input-content-height.ts (100%) rename mobile/src/platform/{ => text-input}/text-input-content-height.web.ts (100%) rename mobile/src/platform/{ => text-input}/text-input-font-size.ts (51%) rename mobile/src/platform/{ => text-input}/text-input-font-size.web.ts (87%) rename mobile/src/platform/{ => text-input}/text-input-handle.ts (100%) rename mobile/src/platform/{ => text-input}/text-input-selection-reveal.web.ts (100%) create mode 100644 mobile/src/platform/view-measurement/page-box-contract.ts rename mobile/src/platform/{ => view-measurement}/page-box.ts (80%) rename mobile/src/platform/{ => view-measurement}/page-box.web.ts (89%) delete mode 100644 mobile/src/source-control/mobile-source-control-oversized-stage-all.test.tsx diff --git a/config/scripts/mobile-web-app/platform-seams/mobile-web-app-external-link-seam.mjs b/config/scripts/mobile-web-app/platform-seams/mobile-web-app-external-link-seam.mjs index fbc811b6593..dcbe2f4f25d 100644 --- a/config/scripts/mobile-web-app/platform-seams/mobile-web-app-external-link-seam.mjs +++ b/config/scripts/mobile-web-app/platform-seams/mobile-web-app-external-link-seam.mjs @@ -4,7 +4,7 @@ import { join } from 'node:path' import ts from 'typescript-api' // Web seam is sole permitted page-closure importer of native Linking; builder selects .web.ts sibling. -export const EXTERNAL_LINK_SEAM = 'src/platform/external-link.web.ts' +export const EXTERNAL_LINK_SEAM = 'src/platform/external-links/external-link.web.ts' // Parse real Linking bindings, excluding strings/comments and recognizing named aliases plus namespace/default interop. // Report named imports/reexports once at entry statement; report namespace property uses, since whole namespace import is allowed. diff --git a/config/scripts/mobile-web-app/platform-seams/mobile-web-app-haptics-seam.mjs b/config/scripts/mobile-web-app/platform-seams/mobile-web-app-haptics-seam.mjs index 8c22cc7548b..63480ec97e7 100644 --- a/config/scripts/mobile-web-app/platform-seams/mobile-web-app-haptics-seam.mjs +++ b/config/scripts/mobile-web-app/platform-seams/mobile-web-app-haptics-seam.mjs @@ -4,10 +4,10 @@ import { readFileSync } from 'node:fs' import { join } from 'node:path' import ts from 'typescript-api' -export const HAPTICS_SEAM = 'src/platform/haptics.web.ts' +export const HAPTICS_SEAM = 'src/platform/haptics/haptics.web.ts' // Native sibling imports expo-haptics and must stay outside page closure. -export const HAPTICS_NATIVE = 'src/platform/haptics.ts' +export const HAPTICS_NATIVE = 'src/platform/haptics/haptics.ts' export const HAPTICS_KINDS_MODULE = 'src/mobile-web-shell/bridge/bridge-haptics-notify.ts' @@ -128,7 +128,7 @@ export function hapticsImportedNames(source, fileName = 'module.ts') { if ( !ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier) || - !/(?:\.\.?\/)+platform\/haptics$/.test(statement.moduleSpecifier.text) + !/(?:\.\.?\/)+platform\/haptics\/haptics$/.test(statement.moduleSpecifier.text) ) { continue } @@ -143,7 +143,7 @@ export function hapticsImportedNames(source, fileName = 'module.ts') { // Consumers import extensionless seam; census checks closure resolved web sibling rather than native expo-haptics path. export function hapticsSeamImporters(mobileDir, closure) { - const specifier = /(?:^|['"])(?:\.\.?\/)+platform\/haptics(?:\.web)?['"]$/ + const specifier = /(?:^|['"])(?:\.\.?\/)+platform\/haptics\/haptics(?:\.web)?['"]$/ return closure.local .filter((file) => file !== HAPTICS_SEAM && file !== HAPTICS_NATIVE) .filter((file) => { diff --git a/config/scripts/mobile-web-app/platform-seams/mobile-web-app-media-picker-seam.mjs b/config/scripts/mobile-web-app/platform-seams/mobile-web-app-media-picker-seam.mjs index 14b677ea59f..15388c194b9 100644 --- a/config/scripts/mobile-web-app/platform-seams/mobile-web-app-media-picker-seam.mjs +++ b/config/scripts/mobile-web-app/platform-seams/mobile-web-app-media-picker-seam.mjs @@ -4,7 +4,7 @@ import { readFileSync } from 'node:fs' import { join } from 'node:path' import ts from 'typescript-api' -export const MEDIA_PICKER_SEAM = 'src/platform/media-picker.web.ts' +export const MEDIA_PICKER_SEAM = 'src/platform/media-picking/media-picker.web.ts' export const NATIVE_PICKER_MODULES = ['expo-image-picker', 'expo-document-picker'] diff --git a/config/scripts/mobile-web-app/platform-seams/mobile-web-app-text-input-font-size-seam.mjs b/config/scripts/mobile-web-app/platform-seams/mobile-web-app-text-input-font-size-seam.mjs index 4e0976874c4..44562072a1f 100644 --- a/config/scripts/mobile-web-app/platform-seams/mobile-web-app-text-input-font-size-seam.mjs +++ b/config/scripts/mobile-web-app/platform-seams/mobile-web-app-text-input-font-size-seam.mjs @@ -2,7 +2,7 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs' import { join } from 'node:path' import ts from 'typescript-api' -export const TEXT_INPUT_FONT_SIZE_SEAM = 'src/platform/text-input-font-size.web.ts' +export const TEXT_INPUT_FONT_SIZE_SEAM = 'src/platform/text-input/text-input-font-size.web.ts' const FLOOR_EXPORT = 'TEXT_INPUT_FONT_SIZE_FLOOR' diff --git a/config/scripts/mobile-web-app/routing/mobile-web-app-page-grant-call-sites.mjs b/config/scripts/mobile-web-app/routing/mobile-web-app-page-grant-call-sites.mjs index c201cf7f4f5..75ea0a8ee9f 100644 --- a/config/scripts/mobile-web-app/routing/mobile-web-app-page-grant-call-sites.mjs +++ b/config/scripts/mobile-web-app/routing/mobile-web-app-page-grant-call-sites.mjs @@ -27,25 +27,25 @@ export const PAGE_GRANT_CALL_SITES = [ callRow( ['externalLink'], 'openExternalLink', - 'src/platform/external-link.web.ts', + 'src/platform/external-links/external-link.web.ts', 'the shell is the only thing on the page that can open a URL outside the app' ), callRow( ['native.clipboard.write'], 'useClipboardWriter', - 'src/platform/clipboard.web.ts', + 'src/platform/clipboard/clipboard.web.ts', "the browser's own clipboard write is refused without a user gesture the page cannot prove" ), callRow( ['native.clipboard.read'], 'useClipboardReader', - 'src/platform/clipboard.web.ts', + 'src/platform/clipboard/clipboard.web.ts', 'a paste needs the device pasteboard, which the WebView does not hand the page' ), callRow( ['native.media.pick', 'native.media.read', 'native.media.release'], 'useMediaPicker', - 'src/platform/media-picker.web.ts', + 'src/platform/media-picking/media-picker.web.ts', 'the picker runs the OS permission prompt inside the shell and hands back a handle' ) ] diff --git a/config/scripts/mobile-web-app/shell/mobile-web-app-composer-sizing-render.test.mjs b/config/scripts/mobile-web-app/shell/mobile-web-app-composer-sizing-render.test.mjs index ee8f619bd08..e3422785654 100644 --- a/config/scripts/mobile-web-app/shell/mobile-web-app-composer-sizing-render.test.mjs +++ b/config/scripts/mobile-web-app/shell/mobile-web-app-composer-sizing-render.test.mjs @@ -29,7 +29,7 @@ import { useState } from 'react' import { createRoot } from 'react-dom/client' import { View } from 'react-native' import { MobileNativeChatComposerField } from './src/session/native-chat/composer/MobileNativeChatComposerField.web' -import { publishShellKeyboardSource } from './src/platform/keyboard-occlusion' +import { publishShellKeyboardSource } from './src/platform/keyboard/keyboard-occlusion' let keyboardHeight = 0 const listeners = new Set() diff --git a/mobile/app/mobile-onboarding.tsx b/mobile/app/mobile-onboarding.tsx index 8e8378f27f3..ceca1af2268 100644 --- a/mobile/app/mobile-onboarding.tsx +++ b/mobile/app/mobile-onboarding.tsx @@ -11,7 +11,7 @@ import { } from '../src/onboarding/MobileOnboardingPage' import { parseMobileOnboardingSteps } from '../src/onboarding/mobile-onboarding-plan' import { PUSH_CURVE, PUSH_SLIDE_MS } from '../src/navigation/push-motion' -import { useReducedMotionEnabled } from '../src/platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../src/platform/accessibility/use-reduced-motion' import { useMobileColorScheme } from '../src/theme/use-mobile-color-scheme' import { mobileOnboardingStyles as styles } from '../src/onboarding/mobile-onboarding-styles' import { diff --git a/mobile/app/pair-confirm.tsx b/mobile/app/pair-confirm.tsx index 83146afbdb5..207af3e3e49 100644 --- a/mobile/app/pair-confirm.tsx +++ b/mobile/app/pair-confirm.tsx @@ -27,7 +27,7 @@ import { PairingProgress, waitForBeat } from '../src/pairing/pairing-visuals' -import { triggerSuccess } from '../src/platform/haptics' +import { triggerSuccess } from '../src/platform/haptics/haptics' import { getPairingFailureMessage } from '../src/pairing/pairing-failure-message' type Status = 'awaiting-confirm' | 'connecting' | 'error' diff --git a/mobile/app/pair-scan.tsx b/mobile/app/pair-scan.tsx index 57db5a1b16a..d1117e6a4f0 100644 --- a/mobile/app/pair-scan.tsx +++ b/mobile/app/pair-scan.tsx @@ -40,7 +40,7 @@ import { Step, waitForBeat } from '../src/pairing/pairing-visuals' -import { triggerSuccess } from '../src/platform/haptics' +import { triggerSuccess } from '../src/platform/haptics/haptics' import { getPairingFailureMessage } from '../src/pairing/pairing-failure-message' // Bound initial pairing at ~25s (one connect timeout plus retry) so broken routes show an actionable error/log; live RPC retries indefinitely. diff --git a/mobile/src/agent-history/MobileAgentSessionHistoryPanel.tsx b/mobile/src/agent-history/MobileAgentSessionHistoryPanel.tsx index 08a2d65d451..5df1a4c0f44 100644 --- a/mobile/src/agent-history/MobileAgentSessionHistoryPanel.tsx +++ b/mobile/src/agent-history/MobileAgentSessionHistoryPanel.tsx @@ -23,7 +23,7 @@ import { resumeAiVaultSessionInTerminal } from './ai-vault-resume-launch' import { prepareMobileAiVaultSessionResume } from './ai-vault-resume-preparation' -import { triggerError, triggerSuccess } from '../platform/haptics' +import { triggerError, triggerSuccess } from '../platform/haptics/haptics' import type * as AiVault from '../../../src/shared/ai-vault/contracts/ai-vault-types' import type { Worktree } from '../worktree/workspace-list-types' import { useMobileAgentHistoryState } from './use-mobile-agent-history-state' diff --git a/mobile/src/agents/MobileAgentListRows.tsx b/mobile/src/agents/MobileAgentListRows.tsx index b564b7d4bdf..d165828181a 100644 --- a/mobile/src/agents/MobileAgentListRows.tsx +++ b/mobile/src/agents/MobileAgentListRows.tsx @@ -8,7 +8,7 @@ import { MobileAgentIcon } from '../components/MobileAgentIcon' import { SwipeRowActions } from '../components/SwipeRowActions' import { HOME_ROW_TEXT_INSET, homeListStyles } from '../home/home-list-styles' import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { triggerMediumImpact } from '../platform/haptics' +import { triggerMediumImpact } from '../platform/haptics/haptics' import { colors, spacing } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' diff --git a/mobile/src/agents/use-agent-list-row-actions.tsx b/mobile/src/agents/use-agent-list-row-actions.tsx index 837183789c8..c68b2a3091b 100644 --- a/mobile/src/agents/use-agent-list-row-actions.tsx +++ b/mobile/src/agents/use-agent-list-row-actions.tsx @@ -4,7 +4,7 @@ import { Alert } from 'react-native' import { ActionSheetModal, type ActionSheetAction } from '../components/ActionSheetModal' import { TransientToast, useTransientToast } from '../components/TransientToast' import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { useClipboardWriter } from '../platform/clipboard' +import { useClipboardWriter } from '../platform/clipboard/clipboard' import { useOpenMobileSession } from '../session/use-open-mobile-session' import type { HostProfile } from '../host-catalog/host-profile' import type { AgentActivityChange, AgentListRow } from './agent-list-rows' diff --git a/mobile/src/automations/AutomationManager.test.tsx b/mobile/src/automations/AutomationManager.test.tsx index e6e791d747a..96041599786 100644 --- a/mobile/src/automations/AutomationManager.test.tsx +++ b/mobile/src/automations/AutomationManager.test.tsx @@ -19,8 +19,8 @@ vi.mock('react-native', () => ({ AppState: { currentState: 'active', addEventListener: () => ({ remove: () => {} }) } })) vi.mock( - '../platform/text-input-font-size', - async () => await import('../platform/text-input-font-size.web') + '../platform/text-input/text-input-font-size', + async () => await import('../platform/text-input/text-input-font-size.web') ) vi.mock('lucide-react-native', () => ({ Check: 'Check', diff --git a/mobile/src/automations/automation-styles.ts b/mobile/src/automations/automation-styles.ts index c0fe4ca4c72..b22efc32dba 100644 --- a/mobile/src/automations/automation-styles.ts +++ b/mobile/src/automations/automation-styles.ts @@ -1,5 +1,5 @@ import { themedStyleSheet } from '../theme/themed-styles' -import { TEXT_INPUT_FONT_SIZE } from '../platform/text-input-font-size' +import { TEXT_INPUT_FONT_SIZE } from '../platform/text-input/text-input-font-size' import { colors, radii, spacing, typography } from '../theme/mobile-theme' export const automationStyles = themedStyleSheet(() => ({ diff --git a/mobile/src/browser/pane-view/MobileBrowserLoadProgress.tsx b/mobile/src/browser/pane-view/MobileBrowserLoadProgress.tsx index 5778fb34f4a..dfc72782d5b 100644 --- a/mobile/src/browser/pane-view/MobileBrowserLoadProgress.tsx +++ b/mobile/src/browser/pane-view/MobileBrowserLoadProgress.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react' import { Animated, Easing, Platform, StyleSheet, View } from 'react-native' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' import { colors, radii } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' diff --git a/mobile/src/browser/pane-view/mobile-browser-pane-styles.ts b/mobile/src/browser/pane-view/mobile-browser-pane-styles.ts index b3a89bfa3d5..1ac21044623 100644 --- a/mobile/src/browser/pane-view/mobile-browser-pane-styles.ts +++ b/mobile/src/browser/pane-view/mobile-browser-pane-styles.ts @@ -1,5 +1,5 @@ import { StyleSheet } from 'react-native' -import { TEXT_INPUT_FONT_SIZE } from '../../platform/text-input-font-size' +import { TEXT_INPUT_FONT_SIZE } from '../../platform/text-input/text-input-font-size' import { colors, radii, spacing, typography } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' diff --git a/mobile/src/components/AgentLaunchNotice.tsx b/mobile/src/components/AgentLaunchNotice.tsx index 724defd8c81..3a384e372a3 100644 --- a/mobile/src/components/AgentLaunchNotice.tsx +++ b/mobile/src/components/AgentLaunchNotice.tsx @@ -1,6 +1,6 @@ import { useState } from 'react' import { Pressable, Text, View, type StyleProp, type TextStyle } from 'react-native' -import { useClipboardWriter } from '../platform/clipboard' +import { useClipboardWriter } from '../platform/clipboard/clipboard' import { useMobileTranslation } from '../i18n/use-mobile-translation' import type { MobileAgentLaunchAvailability } from '../session/agent-launch/mobile-agent-launch-availability' import { diff --git a/mobile/src/components/AgentSpinner.tsx b/mobile/src/components/AgentSpinner.tsx index d22e6b37e07..522f6d750ad 100644 --- a/mobile/src/components/AgentSpinner.tsx +++ b/mobile/src/components/AgentSpinner.tsx @@ -7,7 +7,7 @@ import { useMobileTranslation } from '../i18n/use-mobile-translation' import { colors, radii } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { ArrivalFade } from './ArrivalFade' import { WorkingRing } from './AgentStateDot' diff --git a/mobile/src/components/AgentStateDot.tsx b/mobile/src/components/AgentStateDot.tsx index bfa48b0a068..e1695e3a437 100644 --- a/mobile/src/components/AgentStateDot.tsx +++ b/mobile/src/components/AgentStateDot.tsx @@ -5,7 +5,7 @@ import { useMobileTranslation } from '../i18n/use-mobile-translation' import { colors, radii } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { useWorkingRingRotation } from './use-working-motion' // Match desktop per-agent vocabulary: working yellow spinner, done emerald, attention red, interrupted muted, unconfirmed amber, idle neutral. diff --git a/mobile/src/components/AnchoredDetailCard.tsx b/mobile/src/components/AnchoredDetailCard.tsx index 40dc2a1ca56..0077107d073 100644 --- a/mobile/src/components/AnchoredDetailCard.tsx +++ b/mobile/src/components/AnchoredDetailCard.tsx @@ -3,7 +3,7 @@ import { Animated, ScrollView, StyleSheet, Text, View, type HostInstance } from import { DetailCardFocusBoundary, DetailCardLayer } from './detail-card-host' import type { AnchoredDetailCardState } from './use-anchored-detail-card' import { useDetailCardMotion } from './use-detail-card-motion' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { colors, radii, spacing, typography } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' diff --git a/mobile/src/components/AutoHeight.tsx b/mobile/src/components/AutoHeight.tsx index a9715c27a8b..69163e6f54d 100644 --- a/mobile/src/components/AutoHeight.tsx +++ b/mobile/src/components/AutoHeight.tsx @@ -7,7 +7,7 @@ import Animated, { withTiming, type SharedValue } from 'react-native-reanimated' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { BOTTOM_DRAWER_EASING, BOTTOM_DRAWER_HEIGHT_DURATION_MS diff --git a/mobile/src/components/DisclosureChevron.tsx b/mobile/src/components/DisclosureChevron.tsx index c7f7ec1167c..6412f0568c5 100644 --- a/mobile/src/components/DisclosureChevron.tsx +++ b/mobile/src/components/DisclosureChevron.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, type ReactNode } from 'react' import { Animated, Easing, Platform } from 'react-native' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { SHEET_CURVE, SHEET_HEIGHT_DURATION_MS } from './sheet-motion-values' /** Turns its chevron with the AutoHeight it discloses: same curve, same duration. */ diff --git a/mobile/src/components/DragReorderList.tsx b/mobile/src/components/DragReorderList.tsx index 25800381080..0c7e55e37f3 100644 --- a/mobile/src/components/DragReorderList.tsx +++ b/mobile/src/components/DragReorderList.tsx @@ -21,7 +21,7 @@ import { type AnimatedRef, type SharedValue } from 'react-native-reanimated' -import { triggerSelection } from '../platform/haptics' +import { triggerSelection } from '../platform/haptics/haptics' import { dragReorderTarget, dragReorderLayout, diff --git a/mobile/src/components/DragReorderRow.tsx b/mobile/src/components/DragReorderRow.tsx index 3b574b907b4..33e3f66af8c 100644 --- a/mobile/src/components/DragReorderRow.tsx +++ b/mobile/src/components/DragReorderRow.tsx @@ -18,7 +18,7 @@ import { colors, spacing } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { triggerMediumImpact } from '../platform/haptics' +import { triggerMediumImpact } from '../platform/haptics/haptics' import { type DragReorderLayout, orderedKeysFromDragReorderPositions, diff --git a/mobile/src/components/LoadingSpinner.tsx b/mobile/src/components/LoadingSpinner.tsx index 5b2cd44e925..627c9021d36 100644 --- a/mobile/src/components/LoadingSpinner.tsx +++ b/mobile/src/components/LoadingSpinner.tsx @@ -1,6 +1,6 @@ import { Animated } from 'react-native' import { Hourglass, LoaderCircle } from 'lucide-react-native' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { useWorkingRingRotation } from './use-working-motion' // Match desktop one-second spinner; reduced motion uses distinct working icon so frozen circle does not look stuck. diff --git a/mobile/src/components/MobileDictationSetupSheet.tsx b/mobile/src/components/MobileDictationSetupSheet.tsx index c8430fd68dd..093cfd28ee4 100644 --- a/mobile/src/components/MobileDictationSetupSheet.tsx +++ b/mobile/src/components/MobileDictationSetupSheet.tsx @@ -10,7 +10,7 @@ import type { Translate } from '../i18n/mobile-i18n' import { useMobileTranslation } from '../i18n/use-mobile-translation' import { getUiIntlLocale } from '../i18n/ui-intl-locale' import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import { triggerError, triggerSuccess } from '../platform/haptics' +import { triggerError, triggerSuccess } from '../platform/haptics/haptics' import { useDictationSetupPoller } from '../dictation/use-dictation-setup-poller' import { cloudModelNeedsKey, diff --git a/mobile/src/components/MobileDiffReviewLines.tsx b/mobile/src/components/MobileDiffReviewLines.tsx index 5ca8863e086..cc05b94b99a 100644 --- a/mobile/src/components/MobileDiffReviewLines.tsx +++ b/mobile/src/components/MobileDiffReviewLines.tsx @@ -31,7 +31,7 @@ import type { MobileDiffReviewThread } from '../source-control/review/mobile-dif import type { PRCommentCardActions } from './pr-sidebar/PRCommentCard' import { CommentGroupView } from './pr-sidebar/PRCommentsSection' import { useNow } from '../hooks/use-now' -import { triggerSelection } from '../platform/haptics' +import { triggerSelection } from '../platform/haptics/haptics' const NO_OPENED_LINES: ReadonlySet = new Set() diff --git a/mobile/src/components/MobileRichMarkdownEditor.tsx b/mobile/src/components/MobileRichMarkdownEditor.tsx index aa6e6974618..92faf56543e 100644 --- a/mobile/src/components/MobileRichMarkdownEditor.tsx +++ b/mobile/src/components/MobileRichMarkdownEditor.tsx @@ -15,7 +15,7 @@ import { useMobileTranslation } from '../i18n/use-mobile-translation' import { colors } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { openExternalLink } from '../platform/external-link' +import { openExternalLink } from '../platform/external-links/external-link' import { MobileRichMarkdownToolbar } from './MobileRichMarkdownToolbar' import { TextInputModal } from './TextInputModal' import { diff --git a/mobile/src/components/MobileSegmentedControl.tsx b/mobile/src/components/MobileSegmentedControl.tsx index 9cbb252b529..42ac6119efe 100644 --- a/mobile/src/components/MobileSegmentedControl.tsx +++ b/mobile/src/components/MobileSegmentedControl.tsx @@ -8,7 +8,7 @@ import { type StyleProp, type ViewStyle } from 'react-native' -import { triggerSelection } from '../platform/haptics' +import { triggerSelection } from '../platform/haptics/haptics' import { resolveTabStripScrollOffset } from '../session/tabs/tab-strip-scroll' import { colors, concentricRadius, feedback, radii, spacing } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' diff --git a/mobile/src/components/MobileSourceCopyButton.tsx b/mobile/src/components/MobileSourceCopyButton.tsx index 042ed6051d9..931f13a2ed2 100644 --- a/mobile/src/components/MobileSourceCopyButton.tsx +++ b/mobile/src/components/MobileSourceCopyButton.tsx @@ -3,7 +3,7 @@ import { Alert, Platform, Pressable, Text } from 'react-native' import { Check, Copy } from 'lucide-react-native' import { translate } from '../i18n/mobile-i18n' import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { useClipboardWriter } from '../platform/clipboard' +import { useClipboardWriter } from '../platform/clipboard/clipboard' import { colors, radii, spacing, typography } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' diff --git a/mobile/src/components/MobileSwitch.tsx b/mobile/src/components/MobileSwitch.tsx index 21de22f696c..1e407660f8b 100644 --- a/mobile/src/components/MobileSwitch.tsx +++ b/mobile/src/components/MobileSwitch.tsx @@ -10,8 +10,8 @@ import Animated, { import { colors, feedback, radii } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { triggerSelection } from '../platform/haptics' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { triggerSelection } from '../platform/haptics/haptics' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { BOTTOM_DRAWER_EASING } from './bottom-drawer/bottom-drawer-constants' export type MobileSwitchProps = { diff --git a/mobile/src/components/ProtocolBlockScreen.tsx b/mobile/src/components/ProtocolBlockScreen.tsx index c6df85658b8..34c83fec1a3 100644 --- a/mobile/src/components/ProtocolBlockScreen.tsx +++ b/mobile/src/components/ProtocolBlockScreen.tsx @@ -1,5 +1,5 @@ import { useWallAppUpdate } from '../app-update/use-wall-app-update' -import { openExternalLink } from '../platform/external-link' +import { openExternalLink } from '../platform/external-links/external-link' import { useRouteHandoff } from '../navigation/route-handoff' import { Platform, Pressable, Text, View } from 'react-native' import { colors, radii, spacing, typography } from '../theme/mobile-theme' diff --git a/mobile/src/components/WorktreeListRow.tsx b/mobile/src/components/WorktreeListRow.tsx index 10207f88ba5..2f4cc3a260f 100644 --- a/mobile/src/components/WorktreeListRow.tsx +++ b/mobile/src/components/WorktreeListRow.tsx @@ -2,7 +2,7 @@ import { memo } from 'react' import { Bell, ChevronDown, ChevronRight, GitBranch } from 'lucide-react-native' import { Pressable, StyleSheet, Text, View } from 'react-native' import type { MobileRenderableRepoIcon } from '../host-screen/host-screen-reply-schema' -import { triggerMediumImpact } from '../platform/haptics' +import { triggerMediumImpact } from '../platform/haptics/haptics' import { colors, feedback, radii, spacing, typography } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' diff --git a/mobile/src/components/bottom-drawer/bottom-drawer-back-while-hiding.test.tsx b/mobile/src/components/bottom-drawer/bottom-drawer-back-while-hiding.test.tsx index d0dc095044b..e2185120ded 100644 --- a/mobile/src/components/bottom-drawer/bottom-drawer-back-while-hiding.test.tsx +++ b/mobile/src/components/bottom-drawer/bottom-drawer-back-while-hiding.test.tsx @@ -7,7 +7,7 @@ type Timing = { to: number; callback?: (finished: boolean) => void } const animations = vi.hoisted((): { running: Timing[] } => ({ running: [] })) vi.mock('../../navigation/use-back-claim', () => ({ useBackClaim: () => {} })) -vi.mock('../../platform/keyboard-occlusion', () => ({ +vi.mock('../../platform/keyboard/keyboard-occlusion', () => ({ currentSoftKeyboardHeight: () => 0, subscribeSoftKeyboard: () => () => {} })) @@ -43,7 +43,9 @@ vi.mock('react-native-gesture-handler', () => { }) vi.mock('../AutoHeight', () => ({ AutoHeight: ({ children }: { children: unknown }) => children })) // The OS motion setting is read through AccessibilityInfo, which the react-native mock leaves out. -vi.mock('../../platform/use-reduced-motion', () => ({ useReducedMotionEnabled: () => false })) +vi.mock('../../platform/accessibility/use-reduced-motion', () => ({ + useReducedMotionEnabled: () => false +})) vi.mock('react-native-reanimated', () => { function isTiming(value: unknown): value is Timing { return typeof value === 'object' && value !== null && 'to' in value diff --git a/mobile/src/components/bottom-drawer/bottom-drawer-constants.ts b/mobile/src/components/bottom-drawer/bottom-drawer-constants.ts index b14c8a2c752..8790b5b5791 100644 --- a/mobile/src/components/bottom-drawer/bottom-drawer-constants.ts +++ b/mobile/src/components/bottom-drawer/bottom-drawer-constants.ts @@ -1,5 +1,5 @@ import { Easing, useReducedMotion } from 'react-native-reanimated' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' import { SHEET_CURVE, SHEET_HEIGHT_DURATION_MS } from '../sheet-motion-values' export const BOTTOM_DRAWER_EASING = Easing.bezier(...SHEET_CURVE) diff --git a/mobile/src/components/bottom-drawer/mounted-bottom-drawer.tsx b/mobile/src/components/bottom-drawer/mounted-bottom-drawer.tsx index 9e3ff826558..c3386d79ef1 100644 --- a/mobile/src/components/bottom-drawer/mounted-bottom-drawer.tsx +++ b/mobile/src/components/bottom-drawer/mounted-bottom-drawer.tsx @@ -30,7 +30,10 @@ import { import { useResponsiveLayout } from '../../layout/responsive-layout' import { useWindowSafeAreaInsets } from '../../layout/side-safe-area' import { useBackClaim } from '../../navigation/use-back-claim' -import { currentSoftKeyboardHeight, subscribeSoftKeyboard } from '../../platform/keyboard-occlusion' +import { + currentSoftKeyboardHeight, + subscribeSoftKeyboard +} from '../../platform/keyboard/keyboard-occlusion' import { hostOs } from '../../platform/host-os' import { hideDrawer, showDrawer, useBottomDrawerDrag } from './use-bottom-drawer-drag' import { useDrawerReducedMotion } from './bottom-drawer-constants' diff --git a/mobile/src/components/bottom-drawer/option-pager.test.tsx b/mobile/src/components/bottom-drawer/option-pager.test.tsx deleted file mode 100644 index df00998f3d2..00000000000 --- a/mobile/src/components/bottom-drawer/option-pager.test.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { useState } from 'react' -import { Pressable, Text } from 'react-native' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { afterEach, describe, expect, it, vi } from 'vitest' - -vi.mock('react-native', () => ({ - Platform: { OS: 'android' }, - Pressable: 'Pressable', - Text: 'Text', - View: 'View', - StyleSheet: { create: (styles: T) => styles, absoluteFill: {} } -})) -vi.mock('../../platform/use-reduced-motion', () => ({ useReducedMotionEnabled: () => false })) -vi.mock('./mounted-bottom-drawer', () => ({ MountedBottomDrawer: 'MountedBottomDrawer' })) -vi.mock('react-native-reanimated', async () => { - const React = await import('react') - const { reanimatedPagerStub } = await import('../../test-support/reanimated-pager-stub') - return { - ...reanimatedPagerStub(), - // Shared values outlive the drawer's content, just as they do in Reanimated. - useSharedValue: (initial: number) => React.useRef({ value: initial }).current, - useAnimatedStyle: (style: () => object) => style() - } -}) - -import { BottomDrawer, OptionPager, useOptionPager } from './BottomDrawer' -import { MountedBottomDrawer } from './mounted-bottom-drawer' - -function Sheet() { - const [visible, setVisible] = useState(false) - const pager = useOptionPager<'sortBy'>() - return ( - <> - setVisible(true)} /> - setVisible(false)} onAfterClose={pager.forget}> - setVisible(false)} /> - pager.open('sortBy')} />} - page={ - pager.page === null ? null : ( - - Recent - - ) - } - /> - - - ) -} - -let renderer: ReactTestRenderer | null = null - -afterEach(() => { - act(() => renderer?.unmount()) - renderer = null -}) - -function press(label: string) { - act(() => { - renderer!.root - .find((node) => String(node.type) === 'Pressable' && node.props.accessibilityLabel === label) - .props.onPress() - }) -} - -function layout(width: number) { - act(() => { - renderer!.root - .find((node) => String(node.type) === 'View' && node.props.onLayout) - .props.onLayout({ nativeEvent: { layout: { width } } }) - }) -} - -const drawer = () => renderer!.root.findByType(MountedBottomDrawer) -const main = () => - renderer!.root.find((node) => String(node.type) === 'AnimatedView' && 'aria-hidden' in node.props) -const mainOffset = () => main().props.style.at(-1).transform[0].translateX - -function open() { - press('Workspace options') - layout(600) -} - -describe('a drawer option pager', () => { - it('reopens at the root with full-width content after Done from a sub-page', () => { - act(() => { - renderer = create() - }) - open() - expect(mainOffset()).toBeCloseTo(0) - press('Sort by') - expect(mainOffset()).toBeLessThan(0) - - press('Done') - expect(drawer().props.visible).toBe(false) - expect(main().props['aria-hidden']).toBe(true) - // Native hide completes before KeyedBottomDrawer unmounts content and forgets its page. - act(() => drawer().props.onHidden()) - expect(renderer!.root.findAllByType(MountedBottomDrawer)).toHaveLength(0) - - open() - expect(main().props['aria-hidden']).toBe(false) - expect(mainOffset()).toBeCloseTo(0) - layout(390) - expect(mainOffset()).toBeCloseTo(0) - }) - - it('still pushes a sub-page and returns to the root with Back', () => { - act(() => { - renderer = create() - }) - open() - press('Sort by') - expect(main().props['aria-hidden']).toBe(true) - expect(mainOffset()).toBeLessThan(0) - press('Back') - expect(drawer().props.visible).toBe(true) - expect(main().props['aria-hidden']).toBe(false) - expect(mainOffset()).toBeCloseTo(0) - }) -}) diff --git a/mobile/src/components/composer-input/MessageBox.tsx b/mobile/src/components/composer-input/MessageBox.tsx index dd009589c91..5b90513b1ca 100644 --- a/mobile/src/components/composer-input/MessageBox.tsx +++ b/mobile/src/components/composer-input/MessageBox.tsx @@ -9,7 +9,7 @@ import { } from 'react-native' import { colors } from '../../theme/mobile-theme' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' -import { useTextInputContentHeight } from '../../platform/text-input-content-height' +import { useTextInputContentHeight } from '../../platform/text-input/text-input-content-height' import { ComposerSendButton } from './ComposerSendButton' import { composerInputStyles as styles, diff --git a/mobile/src/components/drag-reorder-test-harness.test-fixture.tsx b/mobile/src/components/drag-reorder-test-harness.test-fixture.tsx index e232b2185bd..ca5848edaeb 100644 --- a/mobile/src/components/drag-reorder-test-harness.test-fixture.tsx +++ b/mobile/src/components/drag-reorder-test-harness.test-fixture.tsx @@ -241,7 +241,10 @@ vi.mock('../layout/use-shell-side-rail', () => ({ useDirectionPadAvailable: () = vi.mock('@react-native-async-storage/async-storage', () => ({ default: { getItem: async () => null, setItem: async () => {} } })) -vi.mock('../platform/haptics', () => ({ triggerMediumImpact: vi.fn(), triggerSelection: vi.fn() })) +vi.mock('../platform/haptics/haptics', () => ({ + triggerMediumImpact: vi.fn(), + triggerSelection: vi.fn() +})) import { DragReorderList } from './DragReorderList' diff --git a/mobile/src/components/list/ListGroup.tsx b/mobile/src/components/list/ListGroup.tsx index b27afdeae3d..66b34b3dc54 100644 --- a/mobile/src/components/list/ListGroup.tsx +++ b/mobile/src/components/list/ListGroup.tsx @@ -2,7 +2,7 @@ import { Children, Fragment, isValidElement, type ReactNode } from 'react' import { Pressable, StyleSheet, Text, View, type StyleProp, type ViewStyle } from 'react-native' import { Check, ChevronDown, ChevronRight } from 'lucide-react-native' import { DisclosureChevron } from '../DisclosureChevron' -import { triggerSelection } from '../../platform/haptics' +import { triggerSelection } from '../../platform/haptics/haptics' import { colors, feedback, radii, spacing } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' diff --git a/mobile/src/components/markdown-link-handler.ts b/mobile/src/components/markdown-link-handler.ts index ceb005aff4e..78b99e24bb1 100644 --- a/mobile/src/components/markdown-link-handler.ts +++ b/mobile/src/components/markdown-link-handler.ts @@ -1,5 +1,5 @@ import { createContext } from 'react' -import { openExternalLink } from '../platform/external-link' +import { openExternalLink } from '../platform/external-links/external-link' // Screens that offer choices for a tapped web link provide this; elsewhere the system browser opens it. export const MarkdownLinkHandlerContext = createContext<(url: string) => void>(openExternalLink) diff --git a/mobile/src/components/mobile-markdown-inline.tsx b/mobile/src/components/mobile-markdown-inline.tsx index 295fc94caa8..44d6d073926 100644 --- a/mobile/src/components/mobile-markdown-inline.tsx +++ b/mobile/src/components/mobile-markdown-inline.tsx @@ -1,7 +1,7 @@ import type { PhrasingContent } from 'mdast' import { Fragment, type ReactNode } from 'react' import type { TextProps } from 'react-native' -import { openExternalLink } from '../platform/external-link' +import { openExternalLink } from '../platform/external-links/external-link' import { detectFilePathSegments, isFilePathCodeSpan, diff --git a/mobile/src/components/mobile-pr-url.ts b/mobile/src/components/mobile-pr-url.ts index 8c9d15a374f..4b0f2aa1a09 100644 --- a/mobile/src/components/mobile-pr-url.ts +++ b/mobile/src/components/mobile-pr-url.ts @@ -1 +1 @@ -export { openExternalLink as openMobilePrUrl } from '../platform/external-link' +export { openExternalLink as openMobilePrUrl } from '../platform/external-links/external-link' diff --git a/mobile/src/components/nav-bar/BottomSearchBar.tsx b/mobile/src/components/nav-bar/BottomSearchBar.tsx index e56e3070e55..ca4a518c711 100644 --- a/mobile/src/components/nav-bar/BottomSearchBar.tsx +++ b/mobile/src/components/nav-bar/BottomSearchBar.tsx @@ -4,8 +4,8 @@ import Svg, { Defs, LinearGradient, Rect, Stop } from 'react-native-svg' import { X } from 'lucide-react-native' import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated' import { useMobileTranslation } from '../../i18n/use-mobile-translation' -import { useKeyboardOcclusion } from '../../platform/keyboard-occlusion' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' +import { useKeyboardOcclusion } from '../../platform/keyboard/keyboard-occlusion' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' import { colors, feedback, radii, spacing } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' diff --git a/mobile/src/components/nav-bar/NavBar.tsx b/mobile/src/components/nav-bar/NavBar.tsx index 91a19430c8c..f8752a33eaf 100644 --- a/mobile/src/components/nav-bar/NavBar.tsx +++ b/mobile/src/components/nav-bar/NavBar.tsx @@ -9,7 +9,7 @@ import { type ViewStyle } from 'react-native' import { ChevronDown } from 'lucide-react-native' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' import { colors, pressedStyle, spacing } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' diff --git a/mobile/src/components/nav-bar/PullDownMenu.tsx b/mobile/src/components/nav-bar/PullDownMenu.tsx index da1297b7a41..87827d9c59d 100644 --- a/mobile/src/components/nav-bar/PullDownMenu.tsx +++ b/mobile/src/components/nav-bar/PullDownMenu.tsx @@ -13,7 +13,7 @@ import { type HostInstance } from 'react-native' import { Check } from 'lucide-react-native' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' import { useBackClaim } from '../../navigation/use-back-claim' import { colors, radii, spacing, typography } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' diff --git a/mobile/src/components/pr-sidebar/MobileLinkPrForm.tsx b/mobile/src/components/pr-sidebar/MobileLinkPrForm.tsx index 03f03584590..189b3398471 100644 --- a/mobile/src/components/pr-sidebar/MobileLinkPrForm.tsx +++ b/mobile/src/components/pr-sidebar/MobileLinkPrForm.tsx @@ -4,7 +4,7 @@ import { colors, radii, spacing, typography, pressedStyle } from '../../theme/mo import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import { parseGitHubIssueOrPRLink, parseGitHubIssueOrPRNumber diff --git a/mobile/src/components/pr-sidebar/PRCommentCard.tsx b/mobile/src/components/pr-sidebar/PRCommentCard.tsx index f7837163433..0b9a2f44c30 100644 --- a/mobile/src/components/pr-sidebar/PRCommentCard.tsx +++ b/mobile/src/components/pr-sidebar/PRCommentCard.tsx @@ -1,6 +1,6 @@ import { memo, useState } from 'react' import { Image, Platform, Pressable, Text, View } from 'react-native' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import { Check, CornerDownRight, ExternalLink, Pencil, Trash2, Undo2 } from 'lucide-react-native' import type { GitHubReaction, diff --git a/mobile/src/components/pr-sidebar/PrCheckFailureRow.tsx b/mobile/src/components/pr-sidebar/PrCheckFailureRow.tsx index 2d7d8416984..4619c602da4 100644 --- a/mobile/src/components/pr-sidebar/PrCheckFailureRow.tsx +++ b/mobile/src/components/pr-sidebar/PrCheckFailureRow.tsx @@ -14,9 +14,9 @@ import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../../i18n/use-mobile-translation' import type { Translate } from '../../i18n/mobile-i18n' -import { openExternalLink } from '../../platform/external-link' -import { useClipboardWriter } from '../../platform/clipboard' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { openExternalLink } from '../../platform/external-links/external-link' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import type { PRCheckDetail } from '../../../../src/shared/github/checks/check-types' import { LIST_LEADING_SLOT, LIST_ROW_PADDING } from '../list/ListGroup' import { PullDownMenu } from '../nav-bar/PullDownMenu' diff --git a/mobile/src/components/pr-sidebar/PrChecksScreen.tsx b/mobile/src/components/pr-sidebar/PrChecksScreen.tsx index 3663e3a12e8..5ca0e02d061 100644 --- a/mobile/src/components/pr-sidebar/PrChecksScreen.tsx +++ b/mobile/src/components/pr-sidebar/PrChecksScreen.tsx @@ -6,7 +6,7 @@ import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../../i18n/use-mobile-translation' import type { Translate } from '../../i18n/mobile-i18n' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import type { PRCheckDetail } from '../../../../src/shared/github/checks/check-types' import { isBrokenCheck } from '../../../../src/shared/source-control/checks/pr-checks-fix-prompt' import { sortChecksBySeverity } from '../../../../src/shared/source-control/checks/pr-check-severity-order' diff --git a/mobile/src/components/pr-sidebar/PrCommentsScreen.tsx b/mobile/src/components/pr-sidebar/PrCommentsScreen.tsx index 6b99dc9e484..508da894c73 100644 --- a/mobile/src/components/pr-sidebar/PrCommentsScreen.tsx +++ b/mobile/src/components/pr-sidebar/PrCommentsScreen.tsx @@ -18,7 +18,7 @@ import { useMobileLaunchAgentLabel } from '../../session/agent-launch/use-mobile import { useHostProtocolGates } from '../host-protocol-gates-context' import { AgentLaunchNotice } from '../AgentLaunchNotice' import { useNow } from '../../hooks/use-now' -import { useKeyboardOcclusion } from '../../platform/keyboard-occlusion' +import { useKeyboardOcclusion } from '../../platform/keyboard/keyboard-occlusion' import { useBackClaim } from '../../navigation/use-back-claim' import { NavBarButton } from '../nav-bar/NavBarButton' import { PullDownMenu } from '../nav-bar/PullDownMenu' diff --git a/mobile/src/components/pr-sidebar/PrCreateSheet.tsx b/mobile/src/components/pr-sidebar/PrCreateSheet.tsx index 8080e0faf27..86eb98b660c 100644 --- a/mobile/src/components/pr-sidebar/PrCreateSheet.tsx +++ b/mobile/src/components/pr-sidebar/PrCreateSheet.tsx @@ -5,7 +5,7 @@ import { colors, pressedStyle, spacing } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../../i18n/use-mobile-translation' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import { SheetHeader } from '../nav-bar/SheetHeader' import { SheetPrimaryButton } from '../SheetPrimaryButton' import { ListGroup, ListRow } from '../list/ListGroup' diff --git a/mobile/src/components/pr-sidebar/PrThreadRow.tsx b/mobile/src/components/pr-sidebar/PrThreadRow.tsx index 68d3893e4e2..6e91ba4593f 100644 --- a/mobile/src/components/pr-sidebar/PrThreadRow.tsx +++ b/mobile/src/components/pr-sidebar/PrThreadRow.tsx @@ -26,7 +26,7 @@ import { colors, pressedStyle, radii, spacing, typography } from '../../theme/mo import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../../i18n/use-mobile-translation' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import { canEditComment, isResolvableComment } from '../../source-control/review/pr-comment-actions' import { ConfirmModal } from '../ConfirmModal' import { PullDownMenu } from '../nav-bar/PullDownMenu' diff --git a/mobile/src/components/pr-sidebar/PrThreadScreen.tsx b/mobile/src/components/pr-sidebar/PrThreadScreen.tsx index d87cf43aae6..11f166c8c1e 100644 --- a/mobile/src/components/pr-sidebar/PrThreadScreen.tsx +++ b/mobile/src/components/pr-sidebar/PrThreadScreen.tsx @@ -5,8 +5,8 @@ import { colors, spacing, typography } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../../i18n/use-mobile-translation' -import { openExternalLink } from '../../platform/external-link' -import { useKeyboardOcclusion } from '../../platform/keyboard-occlusion' +import { openExternalLink } from '../../platform/external-links/external-link' +import { useKeyboardOcclusion } from '../../platform/keyboard/keyboard-occlusion' import { isResolvableComment } from '../../source-control/review/pr-comment-actions' import { MobileStateView } from '../MobileStateView' import { PrMarkdown } from './PrMarkdown' diff --git a/mobile/src/components/pr-sidebar/pr-tab-styles.ts b/mobile/src/components/pr-sidebar/pr-tab-styles.ts index 15336f306ab..13fbed51ca3 100644 --- a/mobile/src/components/pr-sidebar/pr-tab-styles.ts +++ b/mobile/src/components/pr-sidebar/pr-tab-styles.ts @@ -1,7 +1,7 @@ import { StyleSheet } from 'react-native' import { colors, radii, spacing, typography } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' -import { TEXT_INPUT_FONT_SIZE } from '../../platform/text-input-font-size' +import { TEXT_INPUT_FONT_SIZE } from '../../platform/text-input/text-input-font-size' export const prTabStyles = themedStyleSheet(() => ({ container: { flex: 1, backgroundColor: colors.bgBase }, diff --git a/mobile/src/components/pr-sidebar/use-pr-tab-menu.ts b/mobile/src/components/pr-sidebar/use-pr-tab-menu.ts index a977b03f32a..58d9b23a910 100644 --- a/mobile/src/components/pr-sidebar/use-pr-tab-menu.ts +++ b/mobile/src/components/pr-sidebar/use-pr-tab-menu.ts @@ -1,7 +1,7 @@ import { useState } from 'react' import { useMobileTranslation } from '../../i18n/use-mobile-translation' -import { useClipboardWriter } from '../../platform/clipboard' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import { openMobilePrUrl } from '../mobile-pr-url' import type { ActionSheetAction } from '../ActionSheetModal' import { canEditPRTitle } from '../../source-control/review/pr-title-edit' diff --git a/mobile/src/components/rich-markdown/document-style.ts b/mobile/src/components/rich-markdown/document-style.ts index 235e0ea8b3f..a8763895e7c 100644 --- a/mobile/src/components/rich-markdown/document-style.ts +++ b/mobile/src/components/rich-markdown/document-style.ts @@ -1,5 +1,5 @@ import { colors, getMobileColorScheme } from '../../theme/mobile-theme' -import { TEXT_INPUT_FONT_SIZE } from '../../platform/text-input-font-size' +import { TEXT_INPUT_FONT_SIZE } from '../../platform/text-input/text-input-font-size' // Read theme at build time; native embeds sheet, page scopes it to editor host. // #editor scrolls itself: iOS WebKit stops drawing selection ~2 screens into an editable scrolled by a separate box (#34472). diff --git a/mobile/src/components/use-detail-card-scope.web.ts b/mobile/src/components/use-detail-card-scope.web.ts index f83d48a4e61..f63168bd35a 100644 --- a/mobile/src/components/use-detail-card-scope.web.ts +++ b/mobile/src/components/use-detail-card-scope.web.ts @@ -1,6 +1,6 @@ import { useLayoutEffect, useRef } from 'react' import type { DetailCardScopeOptions } from './use-detail-card-scope' -import { isTextField } from '../platform/keyboard-persisting-taps' +import { isTextField } from '../platform/keyboard/keyboard-persisting-taps' function element(value: unknown): HTMLElement | null { return value instanceof HTMLElement ? value : null diff --git a/mobile/src/components/use-new-workspace-create-submit.ts b/mobile/src/components/use-new-workspace-create-submit.ts index d1faecff608..57a851228b0 100644 --- a/mobile/src/components/use-new-workspace-create-submit.ts +++ b/mobile/src/components/use-new-workspace-create-submit.ts @@ -4,7 +4,7 @@ import type { PersistedTrustedOrcaHooks } from '../../../src/shared/orca-yaml-ho import type { RetiredNameRegistry } from '../../../src/shared/worktree/retired-name-registry' import type { RpcClient } from '../transport/rpc/rpc-client-contract' import { folderCreateBusyMessage, uncertainCreateMessage } from './new-workspace-create-copy' -import { triggerError, triggerSuccess } from '../platform/haptics' +import { triggerError, triggerSuccess } from '../platform/haptics/haptics' import type { WorktreeCreateParent } from '../new-workspace/creation/worktree-create-retry' import { createBlankWorkspace } from '../new-workspace/creation/blank-workspace-create' import { isMobileTuiAgentEnabled } from '../agents/mobile-tui-agents' diff --git a/mobile/src/dictation/MobileDictationMeter.tsx b/mobile/src/dictation/MobileDictationMeter.tsx index 7cf75cb2cc2..ad97b0941c1 100644 --- a/mobile/src/dictation/MobileDictationMeter.tsx +++ b/mobile/src/dictation/MobileDictationMeter.tsx @@ -6,7 +6,7 @@ import { type MobileDictationMeter } from '../hooks/mobile-dictation-meter' import { useNow } from '../hooks/use-now' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { colors, typography } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' diff --git a/mobile/src/dictation/mobile-dictation-host.tsx b/mobile/src/dictation/mobile-dictation-host.tsx index 37daa11a247..fec51c8f541 100644 --- a/mobile/src/dictation/mobile-dictation-host.tsx +++ b/mobile/src/dictation/mobile-dictation-host.tsx @@ -2,8 +2,8 @@ import { useCallback, useLayoutEffect, useMemo, useRef, useState, type ReactNode import { useMobileDictation } from '../hooks/use-mobile-dictation' import type { UseMobileDictationResult } from '../hooks/mobile-dictation-session-state' import { useRouteHandoff } from '../navigation/route-handoff' -import { triggerError } from '../platform/haptics' -import { useClipboardWriter } from '../platform/clipboard' +import { triggerError } from '../platform/haptics/haptics' +import { useClipboardWriter } from '../platform/clipboard/clipboard' import { dictationFailureMessage, isDictationSetupRequiredError } from './mobile-dictation-setup' import { useMobileDictationHaptics } from './use-mobile-dictation-haptics' import { useMobileDictationSilenceStop } from './use-mobile-dictation-silence-stop' diff --git a/mobile/src/dictation/use-mobile-dictation-announcements.ts b/mobile/src/dictation/use-mobile-dictation-announcements.ts index 0dd2d729a4c..dd9a4dec999 100644 --- a/mobile/src/dictation/use-mobile-dictation-announcements.ts +++ b/mobile/src/dictation/use-mobile-dictation-announcements.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' import type { UseMobileDictationResult } from '../hooks/mobile-dictation-session-state' -import { useAccessibilityAnnouncer } from '../platform/accessibility-announcer' +import { useAccessibilityAnnouncer } from '../platform/accessibility/accessibility-announcer' import { dictationAnnounceCopy } from './mobile-dictation-announce-copy' import { dictationKeptFailureCause } from './mobile-dictation-controls-copy' diff --git a/mobile/src/dictation/use-mobile-dictation-cancel-drop.ts b/mobile/src/dictation/use-mobile-dictation-cancel-drop.ts index 2ba5895f968..86fc8afa888 100644 --- a/mobile/src/dictation/use-mobile-dictation-cancel-drop.ts +++ b/mobile/src/dictation/use-mobile-dictation-cancel-drop.ts @@ -1,6 +1,7 @@ import { useCallback, type RefObject } from 'react' import type { GestureResponderEvent, HostInstance } from 'react-native' -import { measurePageBox, type PageBox } from '../platform/page-box' +import { measurePageBox } from '../platform/view-measurement/page-box' +import type { PageBox } from '../platform/view-measurement/page-box-contract' // A dragging finger lands less precisely than a tap, so the drop zone reaches past ✕. const DROP_SLOP = 12 diff --git a/mobile/src/dictation/use-mobile-dictation-haptics.ts b/mobile/src/dictation/use-mobile-dictation-haptics.ts index ae927fdd6b5..e0125da9327 100644 --- a/mobile/src/dictation/use-mobile-dictation-haptics.ts +++ b/mobile/src/dictation/use-mobile-dictation-haptics.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect } from 'react' -import { triggerEdgeBump, triggerSelection } from '../platform/haptics' +import { triggerEdgeBump, triggerSelection } from '../platform/haptics/haptics' import type { UseMobileDictationResult } from '../hooks/mobile-dictation-session-state' /** Taps when recording begins and returns a stop that ticks. */ diff --git a/mobile/src/files/attachments/MobileChatAttachmentPreview.tsx b/mobile/src/files/attachments/MobileChatAttachmentPreview.tsx index eee059e3f2c..a2deb2f9515 100644 --- a/mobile/src/files/attachments/MobileChatAttachmentPreview.tsx +++ b/mobile/src/files/attachments/MobileChatAttachmentPreview.tsx @@ -11,7 +11,7 @@ import { useWorkspaceClient } from '../../worktree/use-workspace-client' import { createAttachmentTextPreviewHook } from '../../../../src/shared/attachment-text/use-attachment-text-preview' import { chatTextAttachmentName } from '../../../../src/shared/native-chat-text-attachment-references' import { MOBILE_BRIDGE_MAX_FRAME_BYTES } from '../../../../src/shared/mobile-bridge-frame-bytes' -import { useClipboardWriter } from '../../platform/clipboard' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' import { getUiIntlLocale } from '../../i18n/ui-intl-locale' import { chatAttachmentPreviewStyles as styles } from '../preview/mobile-file-preview-styles' import type { MobileFilePreviewRouteParams } from '../preview/mobile-file-preview-route' diff --git a/mobile/src/files/attachments/mobile-agent-image-fit.ts b/mobile/src/files/attachments/mobile-agent-image-fit.ts index 5c49afb7774..345af967411 100644 --- a/mobile/src/files/attachments/mobile-agent-image-fit.ts +++ b/mobile/src/files/attachments/mobile-agent-image-fit.ts @@ -8,7 +8,7 @@ import { import { base64DecodedByteLength } from '../../../../src/shared/agent-session/agent-session-attachments' import { detectImageFileSignature } from '../../../../src/shared/image-file-signature' import { decodeBase64Bytes } from '../../transport/encryption/base64-byte-codec' -import type { PickedMobileImage } from '../../platform/media-picker-contract' +import type { PickedMobileImage } from '../../platform/media-picking/media-picker-contract' // Page-only: decodes and re-encodes through the WebView's canvas. diff --git a/mobile/src/files/attachments/mobile-file-source-picker.ts b/mobile/src/files/attachments/mobile-file-source-picker.ts index ab4f501d54f..81740ab1602 100644 --- a/mobile/src/files/attachments/mobile-file-source-picker.ts +++ b/mobile/src/files/attachments/mobile-file-source-picker.ts @@ -3,7 +3,7 @@ import { MOBILE_PICKED_FILE_MAX_BYTES, MobileFileTooLargeError, type PickedMobileFile -} from '../../platform/media-picker-contract' +} from '../../platform/media-picking/media-picker-contract' import { defaultMobileImageFileFactory, readUriAsBase64, diff --git a/mobile/src/files/attachments/mobile-image-attachment.ts b/mobile/src/files/attachments/mobile-image-attachment.ts index 259d2816731..c8912568ebe 100644 --- a/mobile/src/files/attachments/mobile-image-attachment.ts +++ b/mobile/src/files/attachments/mobile-image-attachment.ts @@ -3,7 +3,10 @@ import { buildMobileTerminalImagePastePayload, saveMobileClipboardImageAsTempFile } from './mobile-clipboard-image' -import type { MobileImageSource, PickedMobileImage } from '../../platform/media-picker-contract' +import type { + MobileImageSource, + PickedMobileImage +} from '../../platform/media-picking/media-picker-contract' import { nativeChatTerminalWrite } from '../../terminal/session/mobile-session-terminal-operations' import type { MobileClipboardImageRpcSender } from './mobile-clipboard-image-operations' diff --git a/mobile/src/files/attachments/mobile-image-source-picker.ts b/mobile/src/files/attachments/mobile-image-source-picker.ts index 1df1e6f7175..812c44f9001 100644 --- a/mobile/src/files/attachments/mobile-image-source-picker.ts +++ b/mobile/src/files/attachments/mobile-image-source-picker.ts @@ -8,10 +8,13 @@ import { assertClipboardImageByteLengthWithinLimit } from '../../../../src/shared/clipboard-image' import { MobileImageBase64Accumulator } from './mobile-image-base64-accumulator' -import { isPickedImageMime } from '../../platform/mobile-picked-file-name' +import { isPickedImageMime } from '../../platform/media-picking/mobile-picked-file-name' // Import permission errors from the Expo-free contract so page-side catches cannot pull native picker code into the bundle. -import { ImageLibraryPermissionError } from '../../platform/media-picker-contract' -import type { MobileImageSource, PickedMobileImage } from '../../platform/media-picker-contract' +import { ImageLibraryPermissionError } from '../../platform/media-picking/media-picker-contract' +import type { + MobileImageSource, + PickedMobileImage +} from '../../platform/media-picking/media-picker-contract' const MOBILE_IMAGE_READ_CHUNK_BYTES = 256 * 1024 diff --git a/mobile/src/files/attachments/use-mobile-image-attachment.ts b/mobile/src/files/attachments/use-mobile-image-attachment.ts index 450a50e4975..ab35e266d51 100644 --- a/mobile/src/files/attachments/use-mobile-image-attachment.ts +++ b/mobile/src/files/attachments/use-mobile-image-attachment.ts @@ -2,11 +2,11 @@ import { useCallback, useState } from 'react' import { translate } from '../../i18n/mobile-i18n' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import type { ConnectionState } from '../../transport/connection/connection-state' -import { useMediaPicker } from '../../platform/media-picker' +import { useMediaPicker } from '../../platform/media-picking/media-picker' import { ImageLibraryPermissionError, type MobileImageSource -} from '../../platform/media-picker-contract' +} from '../../platform/media-picking/media-picker-contract' import { attachMobileImageToTerminal } from './mobile-image-attachment' import { fitMobileAgentImage } from './mobile-agent-image-fit' diff --git a/mobile/src/files/attachments/use-mobile-session-attachments.ts b/mobile/src/files/attachments/use-mobile-session-attachments.ts index 34998f4c8a3..d097a129cff 100644 --- a/mobile/src/files/attachments/use-mobile-session-attachments.ts +++ b/mobile/src/files/attachments/use-mobile-session-attachments.ts @@ -1,7 +1,7 @@ import { useEffect } from 'react' import { AppState, type AppStateStatus } from 'react-native' -import { useClipboardReader } from '../../platform/clipboard' -import { triggerSelection, triggerError } from '../../platform/haptics' +import { useClipboardReader } from '../../platform/clipboard/clipboard' +import { triggerSelection, triggerError } from '../../platform/haptics/haptics' import { resolveMobileImageAttachmentAgent } from './mobile-image-paste-agent' import { useMobileNewTabAgentOptions } from '../../session/agent-launch/use-mobile-new-tab-agent-options' import { useMobileSessionImageAttachments } from './use-mobile-session-image-attachments' diff --git a/mobile/src/files/attachments/use-mobile-session-image-attachments.ts b/mobile/src/files/attachments/use-mobile-session-image-attachments.ts index 00af1d341b1..6c50ce1af36 100644 --- a/mobile/src/files/attachments/use-mobile-session-image-attachments.ts +++ b/mobile/src/files/attachments/use-mobile-session-image-attachments.ts @@ -1,6 +1,6 @@ import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import type { ConnectionState } from '../../transport/connection/connection-state' -import type { MobileImageSource } from '../../platform/media-picker-contract' +import type { MobileImageSource } from '../../platform/media-picking/media-picker-contract' import type { MobileNativeChatSendIntent, MobileNativeChatSendOutcome diff --git a/mobile/src/files/explorer/MobileFileExplorerPanel.test.ts b/mobile/src/files/explorer/MobileFileExplorerPanel.test.ts index dcf2feb3561..70f50164519 100644 --- a/mobile/src/files/explorer/MobileFileExplorerPanel.test.ts +++ b/mobile/src/files/explorer/MobileFileExplorerPanel.test.ts @@ -25,7 +25,9 @@ const mockTransport = vi.hoisted( }) ) -vi.mock('../../platform/use-reduced-motion', () => ({ useReducedMotionEnabled: () => true })) +vi.mock('../../platform/accessibility/use-reduced-motion', () => ({ + useReducedMotionEnabled: () => true +})) vi.mock('react-native-reanimated', () => ({ Easing: { bezier: () => (t: number) => t }, default: { View: 'AnimatedView' }, @@ -110,7 +112,7 @@ vi.mock('lucide-react-native', () => ({ X: 'X' })) -vi.mock('../../platform/haptics', () => ({ +vi.mock('../../platform/haptics/haptics', () => ({ triggerSelection: vi.fn() })) diff --git a/mobile/src/files/explorer/mobile-file-explorer-row-actions-sheet.tsx b/mobile/src/files/explorer/mobile-file-explorer-row-actions-sheet.tsx index 1fb90aa511b..acaaaa45566 100644 --- a/mobile/src/files/explorer/mobile-file-explorer-row-actions-sheet.tsx +++ b/mobile/src/files/explorer/mobile-file-explorer-row-actions-sheet.tsx @@ -2,8 +2,8 @@ import { useState } from 'react' import { Copy } from 'lucide-react-native' import { ActionSheetModal, type ActionSheetAction } from '../../components/ActionSheetModal' import { useMobileTranslation } from '../../i18n/use-mobile-translation' -import { useClipboardWriter } from '../../platform/clipboard' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import type { TreeNode } from './file-tree' /** Which row's actions are open, and the sheet that shows them. */ diff --git a/mobile/src/files/explorer/mobile-file-explorer-row.tsx b/mobile/src/files/explorer/mobile-file-explorer-row.tsx index c1da0c60af6..d773409b27d 100644 --- a/mobile/src/files/explorer/mobile-file-explorer-row.tsx +++ b/mobile/src/files/explorer/mobile-file-explorer-row.tsx @@ -8,7 +8,7 @@ import { Folder, Image as ImageIcon } from 'lucide-react-native' -import { triggerMediumImpact, triggerSelection } from '../../platform/haptics' +import { triggerMediumImpact, triggerSelection } from '../../platform/haptics/haptics' import { useMobileTranslation } from '../../i18n/use-mobile-translation' import { colors, spacing } from '../../theme/mobile-theme' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' diff --git a/mobile/src/files/media/MobilePdfPreview.tsx b/mobile/src/files/media/MobilePdfPreview.tsx index d76848660ee..bdbc71cecf9 100644 --- a/mobile/src/files/media/MobilePdfPreview.tsx +++ b/mobile/src/files/media/MobilePdfPreview.tsx @@ -3,8 +3,8 @@ import { useFocusEffect } from 'expo-router' import { ActivityIndicator, Text, View } from 'react-native' import { colors } from '../../theme/mobile-theme' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' -import { usePdfOpener } from '../../platform/pdf-opener' -import { PdfOpenRefusedError } from '../../platform/pdf-opener-contract' +import { usePdfOpener } from '../../platform/pdf-viewing/pdf-opener' +import { PdfOpenRefusedError } from '../../platform/pdf-viewing/pdf-opener-contract' import type { MobileFileMedia } from './mobile-file-media' import { filePreviewStyles as styles } from '../preview/mobile-file-preview-styles' import { MobileLoadErrorState, MobileStateButton } from '../../components/MobileStateView' diff --git a/mobile/src/files/session-documents/use-mobile-file-tap-handlers.ts b/mobile/src/files/session-documents/use-mobile-file-tap-handlers.ts index 5718a80e123..8de04d481d8 100644 --- a/mobile/src/files/session-documents/use-mobile-file-tap-handlers.ts +++ b/mobile/src/files/session-documents/use-mobile-file-tap-handlers.ts @@ -5,7 +5,7 @@ import type { ParsedFileLinkLocation } from '../../../../src/shared/file-links/f import { hostStackRouteHref } from '../../navigation/host-stack-navigation' import { useWorkspaceExecutionHost } from '../../navigation/workspace-execution-host' import { useWorkspaceRouteHandoff } from '../../navigation/workspace-route-handoff' -import { triggerSelection } from '../../platform/haptics' +import { triggerSelection } from '../../platform/haptics/haptics' import { openMobileFileTap, type FileTapSessionTab } from './mobile-file-tap-open' import { mobileSessionRouteTarget } from '../../session/mobile-session-route' import { openMobileNativeChatFileTap } from '../../session/native-chat/visual/mobile-native-chat-open-file' diff --git a/mobile/src/files/session-documents/use-mobile-session-file-actions.ts b/mobile/src/files/session-documents/use-mobile-session-file-actions.ts index 1a2652f192f..3bc5dcb5137 100644 --- a/mobile/src/files/session-documents/use-mobile-session-file-actions.ts +++ b/mobile/src/files/session-documents/use-mobile-session-file-actions.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import { useMobileFileTapHandlers } from './use-mobile-file-tap-handlers' import { resolveMobileNativeChatSessionId } from '../../session/native-chat/mobile-native-chat-eligibility' import { useProvideSessionScreenHandlers } from '../../session/session-screen-handlers' diff --git a/mobile/src/files/session-documents/use-mobile-session-markdown-actions.ts b/mobile/src/files/session-documents/use-mobile-session-markdown-actions.ts index 41502a7ba5e..6b6687eaded 100644 --- a/mobile/src/files/session-documents/use-mobile-session-markdown-actions.ts +++ b/mobile/src/files/session-documents/use-mobile-session-markdown-actions.ts @@ -1,6 +1,6 @@ import { useCallback, useMemo } from 'react' import { Keyboard, Platform } from 'react-native' -import { useClipboardWriter } from '../../platform/clipboard' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' import { translate } from '../../i18n/mobile-i18n' import { useMobileTranslation } from '../../i18n/use-mobile-translation' import { useBackClaim } from '../../navigation/use-back-claim' @@ -8,7 +8,7 @@ import { useWorkspaceExecutionHost } from '../../navigation/workspace-execution- import { useSessionLeaveGuard } from '../../session/session-leave-guard' import { markdownTabSave } from './mobile-markdown-tab-operations' import { dropDocumentEditDraft, markdownTabDraftKey } from '../../drafts/document-edit-draft-rows' -import { triggerSuccess, triggerError } from '../../platform/haptics' +import { triggerSuccess, triggerError } from '../../platform/haptics/haptics' import type { DirtyMarkdownDraft } from './session-document-state' import type { MobileSessionTab } from '../../session/tabs/mobile-session-tabs' import type { MobileSessionDiffCommentsModel } from '../../source-control/review/use-mobile-session-diff-comments' diff --git a/mobile/src/files/terminal-artifacts/MobileFilePreviewEditDraft.tsx b/mobile/src/files/terminal-artifacts/MobileFilePreviewEditDraft.tsx index 6e16aa7f29d..545dcae77f2 100644 --- a/mobile/src/files/terminal-artifacts/MobileFilePreviewEditDraft.tsx +++ b/mobile/src/files/terminal-artifacts/MobileFilePreviewEditDraft.tsx @@ -4,9 +4,9 @@ import { useDocumentEditDraft, type DocumentEditDraftOffer } from '../../drafts/use-document-edit-draft' -import { useClipboardWriter } from '../../platform/clipboard' -import { triggerError, triggerSuccess } from '../../platform/haptics' -import { useKeyboardOcclusion } from '../../platform/keyboard-occlusion' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' +import { useKeyboardOcclusion } from '../../platform/keyboard/keyboard-occlusion' import { spacing } from '../../theme/mobile-theme' import { useSafeAreaInsets } from 'react-native-safe-area-context' import type { MobileFilePreviewSource } from '../preview/mobile-file-preview-request' diff --git a/mobile/src/home/MobileHomeScreen.tsx b/mobile/src/home/MobileHomeScreen.tsx index f860dd48ea2..3d31bb2db08 100644 --- a/mobile/src/home/MobileHomeScreen.tsx +++ b/mobile/src/home/MobileHomeScreen.tsx @@ -9,7 +9,7 @@ import { useOpenMobileAccounts } from '../accounts/use-open-mobile-accounts' import { ActionSheetModal } from '../components/ActionSheetModal' import { ConfirmModal } from '../components/ConfirmModal' import { getHostListActionSheetActions } from '../host-list-action-sheet-actions' -import { triggerMediumImpact } from '../platform/haptics' +import { triggerMediumImpact } from '../platform/haptics/haptics' import { useAgentListRowActions } from '../agents/use-agent-list-row-actions' import { useOpenMobileTasks } from '../tasks/use-open-mobile-tasks' import { useOpenMobileAutomations } from '../automations/use-open-mobile-automations' diff --git a/mobile/src/home/tabs/MobileHomeAutomationsTab.tsx b/mobile/src/home/tabs/MobileHomeAutomationsTab.tsx index eeda1071407..c9f3e806753 100644 --- a/mobile/src/home/tabs/MobileHomeAutomationsTab.tsx +++ b/mobile/src/home/tabs/MobileHomeAutomationsTab.tsx @@ -14,7 +14,7 @@ import { useAutomationRuns } from '../../automations/use-automation-runs' import { describeLoadError } from '../../components/load-error-description' import { useNow } from '../../hooks/use-now' import { useMobileTranslation } from '../../i18n/use-mobile-translation' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' import { colors, spacing } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' diff --git a/mobile/src/home/tabs/MobileHomeFab.tsx b/mobile/src/home/tabs/MobileHomeFab.tsx index 0f37550c526..c25184e0e90 100644 --- a/mobile/src/home/tabs/MobileHomeFab.tsx +++ b/mobile/src/home/tabs/MobileHomeFab.tsx @@ -2,7 +2,7 @@ import { Plus } from 'lucide-react-native' import { useEffect, useState } from 'react' import { Pressable, Text, View } from 'react-native' import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' import { colors, radii, spacing } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' diff --git a/mobile/src/home/tabs/MobileHomeTabBar.tsx b/mobile/src/home/tabs/MobileHomeTabBar.tsx index f5d84087a79..129dc771c27 100644 --- a/mobile/src/home/tabs/MobileHomeTabBar.tsx +++ b/mobile/src/home/tabs/MobileHomeTabBar.tsx @@ -15,7 +15,7 @@ import Animated, { withTiming } from 'react-native-reanimated' import { useMobileTranslation } from '../../i18n/use-mobile-translation' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' import { colors, radii, spacing } from '../../theme/mobile-theme' import { themedStyleSheet } from '../../theme/themed-styles' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' diff --git a/mobile/src/home/tabs/MobileHomeUsageTab.tsx b/mobile/src/home/tabs/MobileHomeUsageTab.tsx index 0f97179ff0c..a8850bbbd2b 100644 --- a/mobile/src/home/tabs/MobileHomeUsageTab.tsx +++ b/mobile/src/home/tabs/MobileHomeUsageTab.tsx @@ -18,7 +18,7 @@ import { ClaudeIcon, OpenAIIcon } from '../../components/AgentIcons' import { useNow } from '../../hooks/use-now' import { useMobileTranslation } from '../../i18n/use-mobile-translation' import { useResponsiveLayout } from '../../layout/responsive-layout' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' import type { HomeStatsSummary } from '../../stats/home-stats-total' import { colors, spacing } from '../../theme/mobile-theme' import '../../theme/themed-styles' diff --git a/mobile/src/hooks/mobile-dictation-audio-chunk.ts b/mobile/src/hooks/mobile-dictation-audio-chunk.ts index 524c11ec299..239cd142baf 100644 --- a/mobile/src/hooks/mobile-dictation-audio-chunk.ts +++ b/mobile/src/hooks/mobile-dictation-audio-chunk.ts @@ -17,7 +17,7 @@ import { dictationSessionErrorMessage } from '../dictation/mobile-dictation-setu import type { DictationCapture, DictationCaptureChunk -} from '../platform/dictation-capture-contract' +} from '../platform/audio-capture/dictation-capture-contract' import type { MobileDictationPendingAudioBudget } from './mobile-dictation-pending-audio-budget' import type { MobileDictationHeldAudio } from './mobile-dictation-held-audio' import type { MobileDictationRecording } from './mobile-dictation-recording' diff --git a/mobile/src/hooks/mobile-dictation-background.test.tsx b/mobile/src/hooks/mobile-dictation-background.test.tsx index 585c78e85a8..06e5ab70a1c 100644 --- a/mobile/src/hooks/mobile-dictation-background.test.tsx +++ b/mobile/src/hooks/mobile-dictation-background.test.tsx @@ -7,7 +7,7 @@ import { rpcSuccess, type FakeRpcClient } from '../mobile-web-shell/bridge-host-test-fakes' -import type { DictationCapture } from '../platform/dictation-capture-contract' +import type { DictationCapture } from '../platform/audio-capture/dictation-capture-contract' const appState = vi.hoisted(() => ({ handlers: new Set<(state: string) => void>() })) const microphone = vi.hoisted(() => ({ ends: 0 })) @@ -23,7 +23,7 @@ vi.mock('react-native', () => ({ Platform: { OS: 'ios' } })) -vi.mock('../platform/dictation-capture', () => { +vi.mock('../platform/audio-capture/dictation-capture', () => { const capture: DictationCapture = { open: async () => ({ ok: true }), begin: () => true, @@ -109,29 +109,4 @@ describe('leaving the app while dictating', () => { expect(transcripts).toEqual(['what was heard']) expect(dictation().status).toBe('idle') }) - - it('keeps what the open mic heard while the computer is still starting', async () => { - const rpc = createFakeRpcClient() - const transcripts: string[] = [] - mount(rpc, transcripts) - await act(async () => { - void dictation().start() - for (let turn = 0; turn < 20 && rpc.requests.length === 0; turn += 1) { - await Promise.resolve() - } - }) - expect(dictation().status).toBe('starting') - const endsBefore = microphone.ends - - act(() => { - leave('background') - }) - expect(microphone.ends).toBe(endsBefore + 1) - - await act(async () => { - await pump(rpc) - }) - expect(transcripts).toEqual(['what was heard']) - expect(dictation().status).toBe('idle') - }) }) diff --git a/mobile/src/hooks/mobile-dictation-capture-open.ts b/mobile/src/hooks/mobile-dictation-capture-open.ts index aeb753d466a..753f4934730 100644 --- a/mobile/src/hooks/mobile-dictation-capture-open.ts +++ b/mobile/src/hooks/mobile-dictation-capture-open.ts @@ -1,5 +1,5 @@ import { dictationSessionErrorMessage } from '../dictation/mobile-dictation-setup' -import type { DictationCapture } from '../platform/dictation-capture-contract' +import type { DictationCapture } from '../platform/audio-capture/dictation-capture-contract' type OpenMobileDictationCaptureOptions = { capture: DictationCapture diff --git a/mobile/src/hooks/mobile-dictation-device-engine.ts b/mobile/src/hooks/mobile-dictation-device-engine.ts index 30758b3cc3f..d48642250a8 100644 --- a/mobile/src/hooks/mobile-dictation-device-engine.ts +++ b/mobile/src/hooks/mobile-dictation-device-engine.ts @@ -2,8 +2,8 @@ import type { BridgeSpeechFailure, BridgeSpeechRead } from '../mobile-web-shell/bridge/bridge-speech-verbs' -import type { OnDeviceSpeech } from '../platform/on-device-speech-contract' -import { DICTATION_CAPTURE_DRAIN_INTERVAL_MS } from '../platform/dictation-capture-contract' +import type { OnDeviceSpeech } from '../platform/speech/on-device-speech-contract' +import { DICTATION_CAPTURE_DRAIN_INTERVAL_MS } from '../platform/audio-capture/dictation-capture-contract' import { translate } from '../i18n/mobile-i18n' // Which engine hears this dictation. fallback: a failed phone session may replay its audio to the computer (Automatic only). diff --git a/mobile/src/hooks/mobile-dictation-engine-run.ts b/mobile/src/hooks/mobile-dictation-engine-run.ts index b61d5390e51..8ee2bfe74ee 100644 --- a/mobile/src/hooks/mobile-dictation-engine-run.ts +++ b/mobile/src/hooks/mobile-dictation-engine-run.ts @@ -1,7 +1,7 @@ import type { RefObject } from 'react' import { dictationSessionErrorMessage } from '../dictation/mobile-dictation-setup' -import type { DictationCapture } from '../platform/dictation-capture-contract' -import type { OnDeviceSpeech } from '../platform/on-device-speech-contract' +import type { DictationCapture } from '../platform/audio-capture/dictation-capture-contract' +import type { OnDeviceSpeech } from '../platform/speech/on-device-speech-contract' import { openMobileDictationCapture } from './mobile-dictation-capture-open' import { createMobileDictationId, type DictationStatus } from './mobile-dictation-session-state' import type { RpcClient } from '../transport/rpc/rpc-client-contract' diff --git a/mobile/src/hooks/mobile-dictation-finish.ts b/mobile/src/hooks/mobile-dictation-finish.ts index c676c3de41f..1e692aa30e2 100644 --- a/mobile/src/hooks/mobile-dictation-finish.ts +++ b/mobile/src/hooks/mobile-dictation-finish.ts @@ -1,5 +1,5 @@ import { rpcPayloadMember } from '../transport/rpc/rpc-reader-payload' -import type { DictationCapture } from '../platform/dictation-capture-contract' +import type { DictationCapture } from '../platform/audio-capture/dictation-capture-contract' import { isCurrentMobileDictationFinish } from './mobile-dictation-session-state' import { finishDesktopDictation } from './mobile-dictation-desktop-start' import { diff --git a/mobile/src/hooks/mobile-dictation-held-audio.ts b/mobile/src/hooks/mobile-dictation-held-audio.ts index 21e142a5683..c87b531c136 100644 --- a/mobile/src/hooks/mobile-dictation-held-audio.ts +++ b/mobile/src/hooks/mobile-dictation-held-audio.ts @@ -3,7 +3,7 @@ import { MAX_DICTATION_AUDIO_CHUNK_BYTES, PCM_BYTES_PER_SAMPLE } from '../../../src/shared/rpc-contract/speech-params' -import type { DictationCaptureChunk } from '../platform/dictation-capture-contract' +import type { DictationCaptureChunk } from '../platform/audio-capture/dictation-capture-contract' // Five minutes of 16 kHz PCM (9.6 MB): the longest recording kept for a replay to the computer. export const MAX_HELD_AUDIO_BYTES = DICTATION_SAMPLE_RATE * PCM_BYTES_PER_SAMPLE * 300 diff --git a/mobile/src/hooks/mobile-dictation-meter.ts b/mobile/src/hooks/mobile-dictation-meter.ts index dda46c194d4..36c6e4c6c64 100644 --- a/mobile/src/hooks/mobile-dictation-meter.ts +++ b/mobile/src/hooks/mobile-dictation-meter.ts @@ -12,7 +12,7 @@ import { DICTATION_SAMPLE_RATE, PCM_BYTES_PER_SAMPLE } from '../../../src/shared/rpc-contract/speech-params' -import type { DictationCapture } from '../platform/dictation-capture-contract' +import type { DictationCapture } from '../platform/audio-capture/dictation-capture-contract' const WINDOW_MS = 50 const WINDOW_SAMPLES = (DICTATION_SAMPLE_RATE * WINDOW_MS) / 1000 diff --git a/mobile/src/hooks/mobile-dictation-on-device.test.tsx b/mobile/src/hooks/mobile-dictation-on-device.test.tsx index cca33fcf3c9..8406d44a613 100644 --- a/mobile/src/hooks/mobile-dictation-on-device.test.tsx +++ b/mobile/src/hooks/mobile-dictation-on-device.test.tsx @@ -11,8 +11,8 @@ import { import type { DictationCapture, DictationCaptureChunk -} from '../platform/dictation-capture-contract' -import type { OnDeviceSpeech } from '../platform/on-device-speech-contract' +} from '../platform/audio-capture/dictation-capture-contract' +import type { OnDeviceSpeech } from '../platform/speech/on-device-speech-contract' import type { BridgeSpeechFinish, BridgeSpeechRead, @@ -40,7 +40,7 @@ vi.mock('react-native', () => ({ Platform: { OS: 'ios' } })) -vi.mock('../platform/dictation-capture', () => { +vi.mock('../platform/audio-capture/dictation-capture', () => { const capture: DictationCapture = { open: async () => ({ ok: true }), begin: () => true, @@ -55,7 +55,7 @@ vi.mock('../platform/dictation-capture', () => { return { useDictationCapture: () => capture } }) -vi.mock('../platform/on-device-speech', () => { +vi.mock('../platform/speech/on-device-speech', () => { const speech: OnDeviceSpeech = { status: async () => ({ status: 'ready', @@ -200,60 +200,3 @@ describe('a phone recognizer that fails at the finish', () => { expect(dictation().status).toBe('error') }) }) - -describe('a phone recognizer that fails after a long dictation', () => { - it('replays all of it to the computer instead of calling the connection slow', async () => { - const rpc = createFakeRpcClient() - const sent: SentRequest[] = [] - const transcripts: string[] = [] - mount(rpc, (text) => transcripts.push(text)) - await act(async () => { - await dictation().start() - }) - // 40 s of 16 kHz PCM: more than the 30 s the host may have pending at once. - for (let second = 0; second < 40; second += 1) { - speak(Array.from({ length: 32_000 }, () => second)) - } - seam.finish = { text: null, failure: 'failed' } - await act(async () => { - const stopped = dictation().stop() - await pump(rpc, sent) - await stopped - }) - expect(held.errors).toEqual([]) - expect(chunkAudio(sent).map(([seq]) => seq)).toEqual([0, 1, 2, 3, 4, 5, 6, 7]) - expect(transcripts).toEqual(['from the computer']) - }) -}) - -describe('a stop while a failed phone dictation is reaching the computer', () => { - it('finishes on the computer once its start lands, with the audio kept so far', async () => { - const rpc = createFakeRpcClient() - const sent: SentRequest[] = [] - const transcripts: string[] = [] - mount(rpc, (text) => transcripts.push(text)) - await act(async () => { - await dictation().start() - }) - speak([1, 2, 3]) - seam.read = { revision: 1, committed: '', tentative: '', failure: 'failed' } - // The drain tick reads the failure and starts the computer's session. - await act(async () => { - await vi.waitFor(() => - expect(rpc.requests.map((request) => request.method)).toEqual(['speech.dictation.start']) - ) - }) - await act(async () => { - const stopped = dictation().stop() - await pump(rpc, sent) - await stopped - }) - expect(sent.map((request) => request.method)).toEqual([ - 'speech.dictation.start', - 'speech.dictation.chunk', - 'speech.dictation.finish' - ]) - expect(transcripts).toEqual(['from the computer']) - expect(dictation().status).toBe('idle') - }) -}) diff --git a/mobile/src/hooks/mobile-dictation-recording.ts b/mobile/src/hooks/mobile-dictation-recording.ts index 872c6231794..5ca09c6daa5 100644 --- a/mobile/src/hooks/mobile-dictation-recording.ts +++ b/mobile/src/hooks/mobile-dictation-recording.ts @@ -1,4 +1,4 @@ -import type { DictationCaptureChunk } from '../platform/dictation-capture-contract' +import type { DictationCaptureChunk } from '../platform/audio-capture/dictation-capture-contract' import { MAX_HELD_AUDIO_BYTES, type MobileDictationHeldAudio } from './mobile-dictation-held-audio' // Everything one dictation heard, from the tap, whichever engine hears it: replayed to the computer when the phone fails, and kept after a failure for Retry. diff --git a/mobile/src/hooks/mobile-dictation-retry.test.tsx b/mobile/src/hooks/mobile-dictation-retry.test.tsx index 474685d5a83..cb025b734ba 100644 --- a/mobile/src/hooks/mobile-dictation-retry.test.tsx +++ b/mobile/src/hooks/mobile-dictation-retry.test.tsx @@ -10,7 +10,7 @@ import { import type { DictationCapture, DictationCaptureChunk -} from '../platform/dictation-capture-contract' +} from '../platform/audio-capture/dictation-capture-contract' type Seam = { chunkHandlers: Set<(chunk: DictationCaptureChunk) => void> @@ -25,7 +25,7 @@ vi.mock('react-native', () => ({ })) // Keep capture identity stable like both real seams; a fresh object on render would rerun teardown and cancel dictation. -vi.mock('../platform/dictation-capture', () => { +vi.mock('../platform/audio-capture/dictation-capture', () => { const capture: DictationCapture = { open: async () => ({ ok: true }), begin: () => true, diff --git a/mobile/src/hooks/mobile-dictation-send-window.test.tsx b/mobile/src/hooks/mobile-dictation-send-window.test.tsx index 5df494d67f3..2649f71518a 100644 --- a/mobile/src/hooks/mobile-dictation-send-window.test.tsx +++ b/mobile/src/hooks/mobile-dictation-send-window.test.tsx @@ -10,7 +10,7 @@ import { import type { DictationCapture, DictationCaptureChunk -} from '../platform/dictation-capture-contract' +} from '../platform/audio-capture/dictation-capture-contract' const chunkHandlers = vi.hoisted(() => new Set<(chunk: DictationCaptureChunk) => void>()) @@ -19,7 +19,7 @@ vi.mock('react-native', () => ({ Platform: { OS: 'ios' } })) -vi.mock('../platform/dictation-capture', () => { +vi.mock('../platform/audio-capture/dictation-capture', () => { const capture: DictationCapture = { open: async () => ({ ok: true }), begin: () => true, @@ -40,11 +40,6 @@ function chunkRequests(rpc: FakeRpcClient): SentRequest[] { return rpc.requests.filter((request) => request.method === 'speech.dictation.chunk') } -function paramsOf(request: SentRequest): { audioBase64: string; seq: number } { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: chunk requests carry DictationChunk params. - return request.args[1] as { audioBase64: string; seq: number } -} - async function answer(rpc: FakeRpcClient, requests: SentRequest[], text: string): Promise { for (const request of requests) { rpc.requests.splice(rpc.requests.indexOf(request), 1) @@ -94,35 +89,6 @@ function speak(count: number): void { } describe('phone dictation chunks on a link that stops answering', () => { - it('keep four requests waiting, then send the audio captured meanwhile as one, before the finish', async () => { - const rpc = createFakeRpcClient() - const transcripts: string[] = [] - const dictation = await startDictation(rpc, transcripts) - - speak(10) - const waiting = chunkRequests(rpc) - expect(waiting.map((request) => paramsOf(request).seq)).toEqual([0, 1, 2, 3]) - - let stopped: Promise = Promise.resolve() - await act(async () => { - stopped = dictation().stop() - await answer(rpc, waiting, '') - }) - // Six held two-byte captures, merged into one request after the first that came free. - const merged = chunkRequests(rpc) - expect(merged.map(paramsOf)).toEqual([ - { ...paramsOf(merged[0]), seq: 4, audioBase64: 'BAQFBQYGBwcICAkJ' } - ]) - expect(rpc.requests.some((request) => request.method === 'speech.dictation.finish')).toBe(false) - - await act(async () => { - await answer(rpc, merged, '') - await answer(rpc, [...rpc.requests], 'all ten') - await stopped - }) - expect(transcripts).toEqual(['all ten']) - }) - it('keep the newest text so far when an older reply arrives last', async () => { const rpc = createFakeRpcClient() const dictation = await startDictation(rpc) diff --git a/mobile/src/hooks/mobile-dictation-stop-tail.test.tsx b/mobile/src/hooks/mobile-dictation-stop-tail.test.tsx index a99d87534e4..1ac7b0ca48e 100644 --- a/mobile/src/hooks/mobile-dictation-stop-tail.test.tsx +++ b/mobile/src/hooks/mobile-dictation-stop-tail.test.tsx @@ -11,7 +11,7 @@ import { import type { DictationCapture, DictationCaptureChunk -} from '../platform/dictation-capture-contract' +} from '../platform/audio-capture/dictation-capture-contract' type Seam = { chunkHandlers: Set<(chunk: DictationCaptureChunk) => void> @@ -26,7 +26,7 @@ vi.mock('react-native', () => ({ })) // Keep capture identity stable like both real seams; a fresh object on render would rerun teardown and cancel dictation. -vi.mock('../platform/dictation-capture', () => { +vi.mock('../platform/audio-capture/dictation-capture', () => { const capture: DictationCapture = { open: async () => ({ ok: true }), begin: () => true, @@ -150,32 +150,6 @@ describe('the audio a capture hands over as it ends', () => { methods.indexOf('speech.dictation.finish') ) }) - - it('stops being accepted once the capture has ended', async () => { - const rpc = createFakeRpcClient() - const sent: SentRequest[] = [] - mount(rpc) - await act(async () => { - const started = dictation().start() - await pump(rpc, sent) - await started - }) - await act(async () => { - const stopped = dictation().stop() - await pump(rpc, sent) - await stopped - }) - const before = sent.filter((request) => request.method === 'speech.dictation.chunk').length - for (const handler of seam.chunkHandlers) { - handler({ data: Uint8Array.from([1, 2, 3, 4]), droppedBytes: 0 }) - } - await act(async () => { - await pump(rpc, sent) - }) - expect(sent.filter((request) => request.method === 'speech.dictation.chunk')).toHaveLength( - before - ) - }) }) describe('a finish whose dictation stopped being the current one', () => { diff --git a/mobile/src/hooks/use-mobile-dictation-interruptions.ts b/mobile/src/hooks/use-mobile-dictation-interruptions.ts index a0c5ccf0892..7f4ff4f7c50 100644 --- a/mobile/src/hooks/use-mobile-dictation-interruptions.ts +++ b/mobile/src/hooks/use-mobile-dictation-interruptions.ts @@ -1,7 +1,7 @@ import { useEffect, type RefObject } from 'react' import { AppState } from 'react-native' import { dictationSessionErrorMessage } from '../dictation/mobile-dictation-setup' -import type { DictationCapture } from '../platform/dictation-capture-contract' +import type { DictationCapture } from '../platform/audio-capture/dictation-capture-contract' import type { MobileDictationAudioPhase } from './mobile-dictation-audio-chunk' import type { DictationStatus } from './mobile-dictation-session-state' diff --git a/mobile/src/hooks/use-mobile-dictation.ts b/mobile/src/hooks/use-mobile-dictation.ts index 027042548be..36e36ff36f6 100644 --- a/mobile/src/hooks/use-mobile-dictation.ts +++ b/mobile/src/hooks/use-mobile-dictation.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' -import { useDictationCapture } from '../platform/dictation-capture' -import { useOnDeviceSpeech } from '../platform/on-device-speech' +import { useDictationCapture } from '../platform/audio-capture/dictation-capture' +import { useOnDeviceSpeech } from '../platform/speech/on-device-speech' import { dictationSessionErrorMessage } from '../dictation/mobile-dictation-setup' import { useMobileTranslation } from '../i18n/use-mobile-translation' import { useMobileDictationMeter } from './mobile-dictation-meter' diff --git a/mobile/src/hooks/use-phone-dictation-ready.ts b/mobile/src/hooks/use-phone-dictation-ready.ts index 214e659455d..6ffc8a4490c 100644 --- a/mobile/src/hooks/use-phone-dictation-ready.ts +++ b/mobile/src/hooks/use-phone-dictation-ready.ts @@ -1,5 +1,5 @@ import { useCallback, useRef, useState } from 'react' -import { useOnDeviceSpeech } from '../platform/on-device-speech' +import { useOnDeviceSpeech } from '../platform/speech/on-device-speech' import { chooseMobileDictationRoute } from './mobile-dictation-device-engine' // Whether a tap now would dictate on the phone itself. The ref answers inside a tap; the state re-renders the mic. diff --git a/mobile/src/host-screen/host-screen-overlays.tsx b/mobile/src/host-screen/host-screen-overlays.tsx index 0526700d08f..ddee0ad056a 100644 --- a/mobile/src/host-screen/host-screen-overlays.tsx +++ b/mobile/src/host-screen/host-screen-overlays.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { openExternalLink } from '../platform/external-link' +import { openExternalLink } from '../platform/external-links/external-link' import { Pressable, Text, View } from 'react-native' import { Edit3, Moon, Pin, PinOff } from 'lucide-react-native' import { buildWorktreeNavigationActions } from '../agent-history/worktree-navigation-actions' diff --git a/mobile/src/host-screen/workspace-actions/use-host-worktree-delete.ts b/mobile/src/host-screen/workspace-actions/use-host-worktree-delete.ts index 33dae59848e..e609a05f757 100644 --- a/mobile/src/host-screen/workspace-actions/use-host-worktree-delete.ts +++ b/mobile/src/host-screen/workspace-actions/use-host-worktree-delete.ts @@ -8,7 +8,7 @@ import { persistOrcaHookTrustApproval, wasOrcaHookPreviouslyApproved } from '../../new-workspace/creation/setup-hook-trust' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import { buildWorktreeRemoveRequest } from './worktree-remove-request' import { removeWorktreeRow } from '../../worktree/worktree-host-row-identity' diff --git a/mobile/src/layout/side-rail.tsx b/mobile/src/layout/side-rail.tsx index 4b2ea69a040..dc769c99ffd 100644 --- a/mobile/src/layout/side-rail.tsx +++ b/mobile/src/layout/side-rail.tsx @@ -10,7 +10,7 @@ import { import { StyleSheet, useWindowDimensions, View } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { useReservedRegions } from './reserved-regions' -import { useKeyboardOcclusion } from '../platform/keyboard-occlusion' +import { useKeyboardOcclusion } from '../platform/keyboard/keyboard-occlusion' import { getSideRailStrip, hasTerminalDirectionPadSpace, diff --git a/mobile/src/mobile-web-shell/DownloadProgressBar.tsx b/mobile/src/mobile-web-shell/DownloadProgressBar.tsx index 7364dfe8d5a..55619dab191 100644 --- a/mobile/src/mobile-web-shell/DownloadProgressBar.tsx +++ b/mobile/src/mobile-web-shell/DownloadProgressBar.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from 'react' import { Animated, Easing, Platform, StyleSheet, View } from 'react-native' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { colors } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' diff --git a/mobile/src/mobile-web-shell/MobileWebShellScreen.tsx b/mobile/src/mobile-web-shell/MobileWebShellScreen.tsx index baacf8790c5..84d89d80029 100644 --- a/mobile/src/mobile-web-shell/MobileWebShellScreen.tsx +++ b/mobile/src/mobile-web-shell/MobileWebShellScreen.tsx @@ -20,10 +20,10 @@ import { cancelledShellNavigationTarget } from './cancelled-navigation-target' import { playPageHaptic } from './page-haptics' import { useMobileWebShellBridge } from './use-mobile-web-shell-bridge' import type { MobileWebShellRuntime } from './mobile-web-shell-runtime' -import { useKeyboardOcclusion } from '../platform/keyboard-occlusion' +import { useKeyboardOcclusion } from '../platform/keyboard/keyboard-occlusion' import { useNativeDeviceVerbs } from '../platform/use-native-device-verbs' -import { useNativePdfViewer } from '../platform/native-pdf-view' -import { useNativeImageActions } from '../platform/native-image-actions' +import { useNativePdfViewer } from '../platform/pdf-viewing/native-pdf-view' +import { useNativeImageActions } from '../platform/image-actions/native-image-actions' import { processDeviceStore } from '../device-store/device-store' import { serveDeviceStoreVerb } from './device-store-verb-server' import { BRIDGE_STORE_VERB } from './bridge/bridge-store-verb' diff --git a/mobile/src/mobile-web-shell/bridge/bridge-audio-verbs.test.ts b/mobile/src/mobile-web-shell/bridge/bridge-audio-verbs.test.ts index 19da0366f3b..bee1db1acbb 100644 --- a/mobile/src/mobile-web-shell/bridge/bridge-audio-verbs.test.ts +++ b/mobile/src/mobile-web-shell/bridge/bridge-audio-verbs.test.ts @@ -2,15 +2,15 @@ import { afterEach, describe, expect, it } from 'vitest' import { BridgeNativeVerbRefusedError } from '../bridge-host-errors' -import { createNativeAudioCapture, type NativeAudioEngine } from '../../platform/native-audio' import { - BRIDGE_AUDIO_READ_MAX_BASE64_CHARS, + createNativeAudioCapture, + type NativeAudioEngine +} from '../../platform/audio-capture/native-audio' +import { BRIDGE_AUDIO_RING_MAX_BYTES, audioReadResultSchema, audioStopResultSchema } from './bridge-audio-verbs' -import { BRIDGE_MAX_MESSAGE_BYTES } from './bridge-caps' -import { readBridgeHostMessage } from './bridge-envelope' const captures = new Set>() function createCapture(engine: NativeAudioEngine) { @@ -117,36 +117,6 @@ function decode(base64: string): Uint8Array { return bytes } -describe('what the audio schemas refuse', () => { - it('accepts a complete full-ring drain within the result and frame caps', async () => { - const { engine, emit } = createTestEngine() - const capture = createCapture(engine) - await capture.serve('native.audio.start', { sampleRate: 16_000 }) - const bytes = pcm(BRIDGE_AUDIO_RING_MAX_BYTES, 19) - emit(bytes) - const result = audioReadResultSchema.parse( - await capture.serve('native.audio.read', { maxBytes: BRIDGE_AUDIO_RING_MAX_BYTES }) - ) - expect(decode(result.base64)).toEqual(bytes) - const raw = JSON.stringify({ - v: 1, - type: 'reply', - id: 'AAAAAAAAAAAAAAAAAAAAAA', - payload: { id: 'audio', ok: true, result } - }) - expect(new TextEncoder().encode(raw).byteLength).toBeLessThanOrEqual(BRIDGE_MAX_MESSAGE_BYTES) - expect(readBridgeHostMessage(raw).ok).toBe(true) - expect( - audioReadResultSchema.safeParse({ - base64: 'A'.repeat(BRIDGE_AUDIO_READ_MAX_BASE64_CHARS + 1), - droppedBytes: 0, - recording: true, - interruption: null - }).success - ).toBe(false) - }) -}) - describe('the shell capture', () => { it('serves a partial drain from the front and keeps the rest for the next read', async () => { const { engine, emit } = createTestEngine() @@ -235,30 +205,6 @@ describe('the shell capture', () => { expect(read.base64).toBe('') }) - it('leaves one capture behind when two starts race the permission prompt', async () => { - // Concurrent starts can both leave the OS prompt and reach listen; replacement must remove the earlier handlers rather than leak a ring nobody drains. - const entered = gate() - const blocked = gate() - const { engine, log, liveListeners } = createTestEngine({ - permission: async () => { - entered.release() - await blocked.promise - return 'granted' - } - }) - const capture = createCapture(engine) - const first = capture.serve('native.audio.start', { sampleRate: 16_000 }) - await entered.promise - const second = capture.serve('native.audio.start', { sampleRate: 16_000 }) - blocked.release() - await expect(first).resolves.toMatchObject({ started: true }) - await expect(second).resolves.toMatchObject({ started: true }) - expect(liveListeners()).toEqual({ microphone: 1, interruptions: 1 }) - capture.dispose() - expect(liveListeners()).toEqual({ microphone: 0, interruptions: 0 }) - expect(log.filter((entry) => entry === 'end')).toHaveLength(2) - }) - it('does not open a capture for a start that lands after the session ended', async () => { const entered = gate() const blocked = gate() @@ -280,30 +226,6 @@ describe('the shell capture', () => { expect(log).not.toContain('end') }) - it('tears the device down for a start that lost the race after opening', async () => { - const entered = gate() - const blocked = gate() - const { engine, log, liveListeners } = createTestEngine({ - open: async (sampleRate) => { - entered.release() - await blocked.promise - return { opened: true, sampleRate } - } - }) - const capture = createCapture(engine) - const pending = capture.serve('native.audio.start', { sampleRate: 16_000 }) - await entered.promise - capture.dispose() - blocked.release() - await expect(pending).resolves.toEqual({ - started: false, - sampleRate: 16_000, - permission: 'granted' - }) - expect(log.filter((entry) => entry === 'end')).toHaveLength(1) - expect(liveListeners()).toEqual({ microphone: 0, interruptions: 0 }) - }) - it('ends a capture a stop asked for while its start was still opening', async () => { const entered = gate() const blocked = gate() @@ -326,32 +248,6 @@ describe('the shell capture', () => { }) }) -describe('the screen the shell holds awake while it is capturing', () => { - it('gives it back when the engine throws after the capture is open', async () => { - const { engine, screen } = createTestEngine({ - begin: () => { - throw new Error('the audio engine would not start') - } - }) - const capture = createCapture(engine) - await expect(capture.serve('native.audio.start', { sampleRate: 16_000 })).rejects.toThrow( - 'would not start' - ) - expect(screen).toEqual(['+', '-']) - }) - - it('does not take it twice when a second start replaces the first', async () => { - const { engine, screen } = createTestEngine() - const capture = createCapture(engine) - await capture.serve('native.audio.start', { sampleRate: 16_000 }) - await capture.serve('native.audio.start', { sampleRate: 16_000 }) - // Replace the old capture’s wake tag before activating another; never hold two with only one future release. - expect(screen).toEqual(['+', '-', '+']) - capture.dispose() - expect(screen).toEqual(['+', '-', '+', '-']) - }) -}) - describe('the tail the stop reply carries', () => { it('hands a byte to the read or to the stop, never to both and never to neither', async () => { const { engine, emit } = createTestEngine() diff --git a/mobile/src/mobile-web-shell/page-haptics.ts b/mobile/src/mobile-web-shell/page-haptics.ts index aa294542eb8..45c3856553e 100644 --- a/mobile/src/mobile-web-shell/page-haptics.ts +++ b/mobile/src/mobile-web-shell/page-haptics.ts @@ -4,7 +4,7 @@ import { triggerMediumImpact, triggerSelection, triggerSuccess -} from '../platform/haptics' +} from '../platform/haptics/haptics' import type { BridgeHapticsKind } from './bridge/bridge-haptics-notify' // Reuse native functions so platform styles stay identical. The typed table covers every bridge kind; the platform-seams haptics census checks new native functions also get a kind. diff --git a/mobile/src/new-workspace/NewWorkspacePageScreen.tsx b/mobile/src/new-workspace/NewWorkspacePageScreen.tsx index c067d406cfa..0386361d49b 100644 --- a/mobile/src/new-workspace/NewWorkspacePageScreen.tsx +++ b/mobile/src/new-workspace/NewWorkspacePageScreen.tsx @@ -7,7 +7,7 @@ import { useHostWorktreeRows } from './use-host-worktree-rows' import { useRouteHandoff } from '../navigation/route-handoff' import { firstParam } from '../navigation/route-param-reader' import { workspaceRouteHref } from '../navigation/workspace-execution-host' -import { openExternalLink } from '../platform/external-link' +import { openExternalLink } from '../platform/external-links/external-link' import { useHostClient } from '../transport/connection/host-client-hooks' import { useHostProtocolGates } from '../components/host-protocol-gates-context' diff --git a/mobile/src/onboarding/NotificationOnboardingPreview.tsx b/mobile/src/onboarding/NotificationOnboardingPreview.tsx index 2a97d48997c..dbcc69a9492 100644 --- a/mobile/src/onboarding/NotificationOnboardingPreview.tsx +++ b/mobile/src/onboarding/NotificationOnboardingPreview.tsx @@ -6,7 +6,7 @@ import { useMobileTranslation } from '../i18n/use-mobile-translation' import { colors, radii, spacing, typography } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' function sampleNotifications(t: Translate) { return [ diff --git a/mobile/src/pairing/pairing-visuals.tsx b/mobile/src/pairing/pairing-visuals.tsx index d25c004d00f..825a89aafc7 100644 --- a/mobile/src/pairing/pairing-visuals.tsx +++ b/mobile/src/pairing/pairing-visuals.tsx @@ -10,7 +10,7 @@ import Animated, { import { Check } from 'lucide-react-native' import { SHEET_CURVE } from '../components/sheet-motion-values' import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { pairScanStyles as scanStyles } from '../pair-scan-styles' import { colors, radii } from '../theme/mobile-theme' import { themedStyleSheet } from '../theme/themed-styles' diff --git a/mobile/src/platform/accessibility-announcer.ts b/mobile/src/platform/accessibility/accessibility-announcer.ts similarity index 100% rename from mobile/src/platform/accessibility-announcer.ts rename to mobile/src/platform/accessibility/accessibility-announcer.ts diff --git a/mobile/src/platform/accessibility-announcer.web.ts b/mobile/src/platform/accessibility/accessibility-announcer.web.ts similarity index 85% rename from mobile/src/platform/accessibility-announcer.web.ts rename to mobile/src/platform/accessibility/accessibility-announcer.web.ts index 647555ab841..4e5f816cf49 100644 --- a/mobile/src/platform/accessibility-announcer.web.ts +++ b/mobile/src/platform/accessibility/accessibility-announcer.web.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react' -import { useNativeVerbs } from '../mobile-web-shell/bridge/use-native-verbs' +import { useNativeVerbs } from '../../mobile-web-shell/bridge/use-native-verbs' // RNW's announceForAccessibility does nothing, so the shell announces; older shells and Android leave it to the page's live region. export function useAccessibilityAnnouncer(): (message: string) => Promise { diff --git a/mobile/src/platform/native-accessibility-announce.ts b/mobile/src/platform/accessibility/native-accessibility-announce.ts similarity index 85% rename from mobile/src/platform/native-accessibility-announce.ts rename to mobile/src/platform/accessibility/native-accessibility-announce.ts index b171ff3948e..3a18cbc13f9 100644 --- a/mobile/src/platform/native-accessibility-announce.ts +++ b/mobile/src/platform/accessibility/native-accessibility-announce.ts @@ -1,5 +1,5 @@ import { AccessibilityInfo, Platform } from 'react-native' -import { a11yAnnounceParamsSchema } from '../mobile-web-shell/bridge/bridge-native-verbs' +import { a11yAnnounceParamsSchema } from '../../mobile-web-shell/bridge/bridge-native-verbs' // VoiceOver ignores the page's live regions inside the shell's WebView, so the shell announces for it; TalkBack reads them, so Android leaves it to the page. export function serveNativeAnnounceVerb(params: unknown): { announced: boolean } { diff --git a/mobile/src/platform/use-reduced-motion.ts b/mobile/src/platform/accessibility/use-reduced-motion.ts similarity index 100% rename from mobile/src/platform/use-reduced-motion.ts rename to mobile/src/platform/accessibility/use-reduced-motion.ts diff --git a/mobile/src/platform/dictation-capture-compile-fence.ts b/mobile/src/platform/audio-capture/dictation-capture-compile-fence.ts similarity index 86% rename from mobile/src/platform/dictation-capture-compile-fence.ts rename to mobile/src/platform/audio-capture/dictation-capture-compile-fence.ts index c580a2c5d22..721f05a4298 100644 --- a/mobile/src/platform/dictation-capture-compile-fence.ts +++ b/mobile/src/platform/audio-capture/dictation-capture-compile-fence.ts @@ -1,7 +1,7 @@ import type { MicrophoneDataEvent } from '@orca/expo-two-way-audio' -import { enqueueMobileDictationAudioChunk } from '../hooks/mobile-dictation-audio-chunk' +import { enqueueMobileDictationAudioChunk } from '../../hooks/mobile-dictation-audio-chunk' import type { DictationCaptureChunk } from './dictation-capture-contract' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' // Pin capture chunks separately from raw microphone events so droppedBytes cannot disappear at the sender. Unused expect-error directives fail typecheck; this module never runs. diff --git a/mobile/src/platform/dictation-capture-contract.ts b/mobile/src/platform/audio-capture/dictation-capture-contract.ts similarity index 100% rename from mobile/src/platform/dictation-capture-contract.ts rename to mobile/src/platform/audio-capture/dictation-capture-contract.ts diff --git a/mobile/src/platform/dictation-capture.ts b/mobile/src/platform/audio-capture/dictation-capture.ts similarity index 95% rename from mobile/src/platform/dictation-capture.ts rename to mobile/src/platform/audio-capture/dictation-capture.ts index e3ef12486f3..e57920a5972 100644 --- a/mobile/src/platform/dictation-capture.ts +++ b/mobile/src/platform/audio-capture/dictation-capture.ts @@ -5,7 +5,7 @@ import { tearDown, toggleRecording } from '@orca/expo-two-way-audio' -import { bridgeAudioInterruptionEndsCapture } from '../mobile-web-shell/bridge/bridge-audio-verbs' +import { bridgeAudioInterruptionEndsCapture } from '../../mobile-web-shell/bridge/bridge-audio-verbs' import { nativeMicrophoneScreenLock } from './native-audio-device' import type { DictationCapture } from './dictation-capture-contract' diff --git a/mobile/src/platform/dictation-capture.web.ts b/mobile/src/platform/audio-capture/dictation-capture.web.ts similarity index 92% rename from mobile/src/platform/dictation-capture.web.ts rename to mobile/src/platform/audio-capture/dictation-capture.web.ts index ca4aac3e0b9..c01d22a3aff 100644 --- a/mobile/src/platform/dictation-capture.web.ts +++ b/mobile/src/platform/audio-capture/dictation-capture.web.ts @@ -1,11 +1,14 @@ import { useMemo } from 'react' import { - BRIDGE_AUDIO_RING_MAX_BYTES, bridgeAudioInterruptionEndsCapture, type BridgeAudioChunk -} from '../mobile-web-shell/bridge/bridge-audio-verbs' -import { useNativeVerbs, type NativeVerbs } from '../mobile-web-shell/bridge/use-native-verbs' -import { MOBILE_DICTATION_PCM_SAMPLE_RATE } from '../hooks/mobile-dictation-pending-audio-budget' +} from '../../mobile-web-shell/bridge/bridge-audio-verbs' +import { + MAX_DICTATION_AUDIO_CHUNK_BYTES as BRIDGE_AUDIO_RING_MAX_BYTES, + DICTATION_SAMPLE_RATE as MOBILE_DICTATION_PCM_SAMPLE_RATE +} from '../../../../src/shared/rpc-contract/speech-params' +import { useNativeVerbs, type NativeVerbs } from '../../mobile-web-shell/bridge/use-native-verbs' +import '../../hooks/mobile-dictation-pending-audio-budget' import { DICTATION_CAPTURE_DRAIN_INTERVAL_MS, type DictationCapture, diff --git a/mobile/src/platform/microphone-screen-lock.ts b/mobile/src/platform/audio-capture/microphone-screen-lock.ts similarity index 100% rename from mobile/src/platform/microphone-screen-lock.ts rename to mobile/src/platform/audio-capture/microphone-screen-lock.ts diff --git a/mobile/src/platform/native-audio-device.ts b/mobile/src/platform/audio-capture/native-audio-device.ts similarity index 96% rename from mobile/src/platform/native-audio-device.ts rename to mobile/src/platform/audio-capture/native-audio-device.ts index 7ace5684478..e051019ffe0 100644 --- a/mobile/src/platform/native-audio-device.ts +++ b/mobile/src/platform/audio-capture/native-audio-device.ts @@ -7,7 +7,7 @@ import { toggleRecording } from '@orca/expo-two-way-audio' import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake' -import { BRIDGE_AUDIO_INTERRUPTIONS } from '../mobile-web-shell/bridge/bridge-audio-verbs' +import { BRIDGE_AUDIO_INTERRUPTIONS } from '../../mobile-web-shell/bridge/bridge-audio-verbs' import { createMicrophoneScreenLock } from './microphone-screen-lock' import type { NativeAudioEngine } from './native-audio' diff --git a/mobile/src/platform/native-audio-session-ownership.test.ts b/mobile/src/platform/audio-capture/native-audio-session-ownership.test.ts similarity index 97% rename from mobile/src/platform/native-audio-session-ownership.test.ts rename to mobile/src/platform/audio-capture/native-audio-session-ownership.test.ts index 60717ebcc53..d8741d69882 100644 --- a/mobile/src/platform/native-audio-session-ownership.test.ts +++ b/mobile/src/platform/audio-capture/native-audio-session-ownership.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest' import { createNativeAudioCapture, type NativeAudioEngine } from './native-audio' -import { audioReadResultSchema } from '../mobile-web-shell/bridge/bridge-audio-verbs' +import { audioReadResultSchema } from '../../mobile-web-shell/bridge/bridge-audio-verbs' function gate() { let release = () => {} diff --git a/mobile/src/platform/native-audio.ts b/mobile/src/platform/audio-capture/native-audio.ts similarity index 94% rename from mobile/src/platform/native-audio.ts rename to mobile/src/platform/audio-capture/native-audio.ts index 472f13de5bc..8392a9de928 100644 --- a/mobile/src/platform/native-audio.ts +++ b/mobile/src/platform/audio-capture/native-audio.ts @@ -1,15 +1,15 @@ import { - BRIDGE_AUDIO_RING_MAX_BYTES, bridgeAudioInterruptionEndsCapture, audioReadParamsSchema, audioStartParamsSchema, audioStopParamsSchema, type BridgeAudioInterruption, type BridgeAudioPermission -} from '../mobile-web-shell/bridge/bridge-audio-verbs' -import type { BridgeNativeVerb } from '../mobile-web-shell/bridge/bridge-native-verbs' -import { BridgeNativeVerbRefusedError } from '../mobile-web-shell/bridge-host-errors' -import { bytesToBase64 } from '../hooks/mobile-dictation-session-state' +} from '../../mobile-web-shell/bridge/bridge-audio-verbs' +import { MAX_DICTATION_AUDIO_CHUNK_BYTES as BRIDGE_AUDIO_RING_MAX_BYTES } from '../../../../src/shared/rpc-contract/speech-params' +import type { BridgeNativeVerb } from '../../mobile-web-shell/bridge/bridge-native-verbs' +import { BridgeNativeVerbRefusedError } from '../../mobile-web-shell/bridge-host-errors' +import { bytesToBase64 } from '../../hooks/mobile-dictation-session-state' import type { MicrophoneScreenLock } from './microphone-screen-lock' // Keep transcription protocol in the page so it can update without a binary release; the shell owns only microphone permission and PCM capture. diff --git a/mobile/src/platform/clipboard.test.tsx b/mobile/src/platform/clipboard/clipboard.test.tsx similarity index 54% rename from mobile/src/platform/clipboard.test.tsx rename to mobile/src/platform/clipboard/clipboard.test.tsx index 09c56cbac15..7db037f82b7 100644 --- a/mobile/src/platform/clipboard.test.tsx +++ b/mobile/src/platform/clipboard/clipboard.test.tsx @@ -1,6 +1,6 @@ import { act, create } from 'react-test-renderer' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { ClipboardImage, ClipboardReader, ClipboardWriter } from './clipboard' +import type { ClipboardImage, ClipboardReader } from './clipboard' const clipboard = vi.hoisted(() => ({ setStringAsync: vi.fn(() => Promise.resolve(true)), @@ -12,23 +12,7 @@ const clipboard = vi.hoisted(() => ({ vi.mock('expo-clipboard', () => clipboard) -import { useClipboardReader, useClipboardWriter } from './clipboard' - -function mountWriter(): ClipboardWriter { - const held: { writer: ClipboardWriter | null } = { writer: null } - function Screen(): null { - held.writer = useClipboardWriter() - return null - } - act(() => { - create() - }) - const writer = held.writer - if (writer === null) { - throw new Error('nothing mounted') - } - return writer -} +import { useClipboardReader } from './clipboard' function mountReader(): ClipboardReader { const held: { reader: ClipboardReader | null } = { reader: null } @@ -63,38 +47,7 @@ afterEach(() => { vi.restoreAllMocks() }) -describe('writing the clipboard on a phone', () => { - it('hands the text to the app unchanged', async () => { - const writer = mountWriter() - await expect(writer.writeText('copied')).resolves.toBeUndefined() - expect(clipboard.setStringAsync.mock.calls).toEqual([['copied']]) - }) - - it('propagates a pasteboard write failure', async () => { - clipboard.setStringAsync.mockRejectedValue(new Error('Clipboard unavailable')) - const writer = mountWriter() - await expect(writer.writeText('copied')).rejects.toThrow('Clipboard unavailable') - }) -}) - describe('reading the clipboard on a phone', () => { - it('hands back the text the app has', async () => { - clipboard.getStringAsync.mockImplementation(() => Promise.resolve('pasted')) - await expect(mountReader().readText()).resolves.toBe('pasted') - }) - - it('asks for PNG and returns the image and its dimensions', async () => { - clipboard.getImageAsync.mockResolvedValue({ - data: 'data:image/png;base64,AQID', - size: { width: 3, height: 1 } - }) - await expect(mountReader().readImage()).resolves.toEqual({ - data: 'data:image/png;base64,AQID', - size: { width: 3, height: 1 } - }) - expect(clipboard.getImageAsync.mock.calls).toEqual([[{ format: 'png' }]]) - }) - it('starts both probes before either has answered', async () => { // Hold both probes open to prove they start in parallel; sequential awaits add IPC latency on mount/foreground/toggle. const started: string[] = [] @@ -115,14 +68,6 @@ describe('reading the clipboard on a phone', () => { expect(started).toEqual(['string', 'image']) }) - it('probes both kinds without reading either', async () => { - clipboard.hasImageAsync.mockImplementation(() => Promise.resolve(true)) - await expect(mountReader().contents()).resolves.toEqual({ text: false, image: true }) - // Probe without reading so iOS does not show paste consent just to enable a button. - expect(clipboard.getStringAsync).not.toHaveBeenCalled() - expect(clipboard.getImageAsync).not.toHaveBeenCalled() - }) - it('reads a probe that threw as absent, rather than disabling paste on a rejection', async () => { clipboard.hasStringAsync.mockImplementation(() => Promise.reject(new Error('no pasteboard'))) await expect(mountReader().contents()).resolves.toEqual({ text: false, image: false }) diff --git a/mobile/src/platform/clipboard.ts b/mobile/src/platform/clipboard/clipboard.ts similarity index 100% rename from mobile/src/platform/clipboard.ts rename to mobile/src/platform/clipboard/clipboard.ts diff --git a/mobile/src/platform/clipboard.web.ts b/mobile/src/platform/clipboard/clipboard.web.ts similarity index 93% rename from mobile/src/platform/clipboard.web.ts rename to mobile/src/platform/clipboard/clipboard.web.ts index fe0c4c126e0..86867c3fbb0 100644 --- a/mobile/src/platform/clipboard.web.ts +++ b/mobile/src/platform/clipboard/clipboard.web.ts @@ -2,8 +2,8 @@ import { useMemo } from 'react' import { readStagedMediaItem, releaseAllStagedMedia -} from '../mobile-web-shell/bridge/staged-media-bytes' -import { useNativeVerbs } from '../mobile-web-shell/bridge/use-native-verbs' +} from '../../mobile-web-shell/bridge/staged-media-bytes' +import { useNativeVerbs } from '../../mobile-web-shell/bridge/use-native-verbs' import type { ClipboardImage, ClipboardReader, ClipboardWriter } from './clipboard' // Use native clipboard verbs: navigator.clipboard requires a secure context unavailable under iOS’s custom scheme. Ungranted writes reject for callers to report. diff --git a/mobile/src/platform/native-clipboard.ts b/mobile/src/platform/clipboard/native-clipboard.ts similarity index 91% rename from mobile/src/platform/native-clipboard.ts rename to mobile/src/platform/clipboard/native-clipboard.ts index 48fb50817fa..4da246d4c27 100644 --- a/mobile/src/platform/native-clipboard.ts +++ b/mobile/src/platform/clipboard/native-clipboard.ts @@ -3,7 +3,7 @@ import { clipboardReadParamsSchema, clipboardWriteParamsSchema, type BridgeNativeVerb -} from '../mobile-web-shell/bridge/bridge-native-verbs' +} from '../../mobile-web-shell/bridge/bridge-native-verbs' // Text-only: images use staged media handles to stay below reply caps. Parse again at the device boundary for checked types without assertions. export async function serveNativeClipboardVerb( diff --git a/mobile/src/platform/external-link.test.ts b/mobile/src/platform/external-link.test.ts deleted file mode 100644 index 42c79d6b949..00000000000 --- a/mobile/src/platform/external-link.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' - -// Mirror RN 0.83.10 openURL’s synchronous empty-URL validation; a promise-only rejecting mock cannot expose throws that escape .catch. -const linking = vi.hoisted(() => { - const validatingOpen = (url: string): Promise => { - if (url === '') { - throw new Error('Invalid URL: cannot be empty') - } - return Promise.resolve(true) - } - return { validatingOpen, openURL: vi.fn(validatingOpen) } -}) - -vi.mock('react-native', () => ({ Linking: { openURL: linking.openURL } })) - -import { openExternalLink } from './external-link' - -beforeEach(() => { - linking.openURL.mockReset() - // Restore synchronous validation too so later tests can still reproduce the escaping throw. - linking.openURL.mockImplementation(linking.validatingOpen) -}) - -afterEach(() => { - vi.restoreAllMocks() -}) - -describe('opening a URL from a phone screen', () => { - it('does not throw out of a tap handler when nothing can open the URL', async () => { - linking.openURL.mockImplementation(() => Promise.reject(new Error('no activity found'))) - expect(() => openExternalLink('mailto:someone@example.com')).not.toThrow() - await Promise.resolve() - }) -}) - -describe('a URL the platform refuses before it returns a promise', () => { - it('names the refusal rather than failing silently', () => { - const warned = vi.spyOn(console, 'warn').mockImplementation(() => undefined) - openExternalLink('') - expect(warned.mock.calls).toEqual([ - [ - '[platform] could not open a URL', - { url: '' }, - expect.objectContaining({ message: 'Invalid URL: cannot be empty' }) - ] - ]) - warned.mockRestore() - }) -}) diff --git a/mobile/src/platform/external-link.web.test.ts b/mobile/src/platform/external-link.web.test.ts deleted file mode 100644 index 537e5cb58dd..00000000000 --- a/mobile/src/platform/external-link.web.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { openExternalLink, publishExternalLinkOpener } from './external-link.web' - -let warned: ReturnType - -beforeEach(() => { - warned = vi.spyOn(console, 'warn').mockImplementation(() => undefined) -}) - -afterEach(() => { - warned.mockRestore() -}) - -describe('opening a URL from inside the shell', () => { - it('hands an allowed URL to whatever the entry published', () => { - const asked: string[] = [] - publishExternalLinkOpener((url) => { - asked.push(url) - return true - }) - openExternalLink('https://github.com/stablyai/orca') - expect(asked).toEqual(['https://github.com/stablyai/orca']) - expect(warned).not.toHaveBeenCalled() - }) - - it('names a scheme the grant does not cover and asks for nothing', () => { - const asked: string[] = [] - publishExternalLinkOpener((url) => { - asked.push(url) - return true - }) - for (const url of ['javascript:alert(1)', 'file:///etc/passwd', '/h/host-a/tasks']) { - expect(() => openExternalLink(url), url).not.toThrow() - } - expect(asked).toEqual([]) - expect(warned).toHaveBeenCalledTimes(3) - }) - - it('names a shell that would not take it, rather than reporting a tap that did nothing', () => { - publishExternalLinkOpener(() => false) - openExternalLink('https://example.com') - expect(warned.mock.calls[0]?.[0]).toBe('[page] the shell did not take a URL to open') - }) - - it('refuses everything in a document that published no opener', async () => { - // Reload module state to test its unpublished default; assert the refusal reason too because not.toThrow alone proves no policy. - vi.resetModules() - const fresh: typeof import('./external-link.web') = await import('./external-link.web') - fresh.openExternalLink('https://example.com') - expect(warned.mock.calls).toEqual([ - ['[page] the shell did not take a URL to open', { url: 'https://example.com' }] - ]) - }) -}) diff --git a/mobile/src/platform/external-link.ts b/mobile/src/platform/external-links/external-link.ts similarity index 100% rename from mobile/src/platform/external-link.ts rename to mobile/src/platform/external-links/external-link.ts diff --git a/mobile/src/platform/external-links/external-link.web.test.ts b/mobile/src/platform/external-links/external-link.web.test.ts new file mode 100644 index 00000000000..b850e254411 --- /dev/null +++ b/mobile/src/platform/external-links/external-link.web.test.ts @@ -0,0 +1,25 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { openExternalLink, publishExternalLinkOpener } from './external-link.web' + +let warned: ReturnType + +beforeEach(() => { + warned = vi.spyOn(console, 'warn').mockImplementation(() => undefined) +}) + +afterEach(() => { + warned.mockRestore() +}) + +describe('opening a URL from inside the shell', () => { + it('hands an allowed URL to whatever the entry published', () => { + const asked: string[] = [] + publishExternalLinkOpener((url) => { + asked.push(url) + return true + }) + openExternalLink('https://github.com/stablyai/orca') + expect(asked).toEqual(['https://github.com/stablyai/orca']) + expect(warned).not.toHaveBeenCalled() + }) +}) diff --git a/mobile/src/platform/external-link.web.ts b/mobile/src/platform/external-links/external-link.web.ts similarity index 91% rename from mobile/src/platform/external-link.web.ts rename to mobile/src/platform/external-links/external-link.web.ts index 487a99d565b..24df974543c 100644 --- a/mobile/src/platform/external-link.web.ts +++ b/mobile/src/platform/external-links/external-link.web.ts @@ -1,4 +1,4 @@ -import { readBridgeExternalLinkUrl } from '../mobile-web-shell/bridge/bridge-caps' +import { readBridgeExternalLinkUrl } from '../../mobile-web-shell/bridge/bridge-caps' // Use shell linking because RN Web window.open reports success even though both native WebViews refuse new windows. // Publish outside React for plain-function callers; missing publication/invalid URLs log refusal without throwing from tap handlers. diff --git a/mobile/src/platform/global-timer-receiver-test-fakes.ts b/mobile/src/platform/global-timer-receiver-test-fakes.ts deleted file mode 100644 index 518cb4a834d..00000000000 --- a/mobile/src/platform/global-timer-receiver-test-fakes.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { vi } from 'vitest' - -// Mirror WebIDL timer receivers: omitted means global; explicit nonglobal receivers throw. -function assertGlobalReceiver(receiver: unknown): void { - if (receiver !== undefined && receiver !== globalThis) { - throw new TypeError('Illegal invocation') - } -} - -export type GuardedTimerHandles = { - scheduled: ReturnType[] - cleared: ReturnType[] -} - -// Wrap current real/fake timers so virtual time still works; restore with vi.unstubAllGlobals(). -export function installIllegalInvocationTimerGuards(): GuardedTimerHandles { - const scheduleTimer = globalThis.setTimeout - const cancelTimer = globalThis.clearTimeout - const handles: GuardedTimerHandles = { scheduled: [], cleared: [] } - vi.stubGlobal('setTimeout', function (this: unknown, handler: () => void, ms?: number) { - assertGlobalReceiver(this) - const handle = scheduleTimer(handler, ms) - handles.scheduled.push(handle) - return handle - }) - vi.stubGlobal('clearTimeout', function (this: unknown, handle: ReturnType) { - assertGlobalReceiver(this) - handles.cleared.push(handle) - cancelTimer(handle) - }) - return handles -} diff --git a/mobile/src/platform/haptics.ts b/mobile/src/platform/haptics/haptics.ts similarity index 100% rename from mobile/src/platform/haptics.ts rename to mobile/src/platform/haptics/haptics.ts diff --git a/mobile/src/platform/haptics.web.ts b/mobile/src/platform/haptics/haptics.web.ts similarity index 89% rename from mobile/src/platform/haptics.web.ts rename to mobile/src/platform/haptics/haptics.web.ts index 8380772651c..ca12cb207ea 100644 --- a/mobile/src/platform/haptics.web.ts +++ b/mobile/src/platform/haptics/haptics.web.ts @@ -1,4 +1,4 @@ -import type { BridgeHapticsKind } from '../mobile-web-shell/bridge/bridge-haptics-notify' +import type { BridgeHapticsKind } from '../../mobile-web-shell/bridge/bridge-haptics-notify' // Ask native through a notify: Expo’s web fallback clicks a hidden checkbox on iOS, disrupting long presses. No reply slot is needed. // Publish outside React for plain-function callers; ungranted haptics stay quiet to avoid logging every list tap. diff --git a/mobile/src/platform/host-os.web.ts b/mobile/src/platform/host-os.web.ts index 1fa67c726d6..fd88a823c88 100644 --- a/mobile/src/platform/host-os.web.ts +++ b/mobile/src/platform/host-os.web.ts @@ -1,7 +1,5 @@ import type { HostOs } from './host-os' -export type { HostOs } from './host-os' - // Platform.OS is web here: read the phone OS for keyboard/inset arithmetic (iPadOS needs the Mac-plus-touch heuristic) and a desktop Mac for Cmd chords. export function hostOs(): HostOs { const agent = globalThis.navigator?.userAgent ?? '' diff --git a/mobile/src/platform/image-actions-contract.ts b/mobile/src/platform/image-actions/image-actions-contract.ts similarity index 87% rename from mobile/src/platform/image-actions-contract.ts rename to mobile/src/platform/image-actions/image-actions-contract.ts index a1707cddbdc..d6bb66df92d 100644 --- a/mobile/src/platform/image-actions-contract.ts +++ b/mobile/src/platform/image-actions/image-actions-contract.ts @@ -1,4 +1,4 @@ -import type { BridgeImageOperation } from '../mobile-web-shell/bridge/bridge-image-verbs' +import type { BridgeImageOperation } from '../../mobile-web-shell/bridge/bridge-image-verbs' export type ImageAction = (operation: BridgeImageOperation) => Promise diff --git a/mobile/src/platform/image-actions.ts b/mobile/src/platform/image-actions/image-actions.ts similarity index 100% rename from mobile/src/platform/image-actions.ts rename to mobile/src/platform/image-actions/image-actions.ts diff --git a/mobile/src/platform/image-actions.web.ts b/mobile/src/platform/image-actions/image-actions.web.ts similarity index 87% rename from mobile/src/platform/image-actions.web.ts rename to mobile/src/platform/image-actions/image-actions.web.ts index bb4127adc3c..ca56cf1f049 100644 --- a/mobile/src/platform/image-actions.web.ts +++ b/mobile/src/platform/image-actions/image-actions.web.ts @@ -2,20 +2,20 @@ import { useMemo } from 'react' import { agentSessionAttachmentAppends, base64DecodedByteLength -} from '../../../src/shared/agent-session/agent-session-attachments' -import { readRasterImageDataUri } from '../../../src/shared/file-preview/image-data-uri' -import { isKnownRasterImageMimeType } from '../../../src/shared/file-preview/raster-image-preview-limits' -import { isSvgFilePath } from '../../../src/shared/file-preview/svg-document' +} from '../../../../src/shared/agent-session/agent-session-attachments' +import { readRasterImageDataUri } from '../../../../src/shared/file-preview/image-data-uri' +import { isKnownRasterImageMimeType } from '../../../../src/shared/file-preview/raster-image-preview-limits' +import { isSvgFilePath } from '../../../../src/shared/file-preview/svg-document' import type { BridgeImageOperation, BridgeImageRunParams -} from '../mobile-web-shell/bridge/bridge-image-verbs' +} from '../../mobile-web-shell/bridge/bridge-image-verbs' import { NativeVerbError, useNativeVerbs, type NativeVerbReason, type NativeVerbs -} from '../mobile-web-shell/bridge/use-native-verbs' +} from '../../mobile-web-shell/bridge/use-native-verbs' import { ImageActionRefusedError, type ImageAction, diff --git a/mobile/src/platform/native-image-actions.ts b/mobile/src/platform/image-actions/native-image-actions.ts similarity index 88% rename from mobile/src/platform/native-image-actions.ts rename to mobile/src/platform/image-actions/native-image-actions.ts index 4699fed8e1b..b5a3e193a17 100644 --- a/mobile/src/platform/native-image-actions.ts +++ b/mobile/src/platform/image-actions/native-image-actions.ts @@ -3,20 +3,20 @@ import { Platform } from 'react-native' import * as Clipboard from 'expo-clipboard' import { File as FsFile, type Directory, type FileHandle } from 'expo-file-system' import { useFocusEffect, useNavigation } from 'expo-router' -import { saveImageToPhotos, shareImage } from '../../modules/orca-mobile-web-shell/src' +import { saveImageToPhotos, shareImage } from '../../../modules/orca-mobile-web-shell/src/index' import { CLIPBOARD_IMAGE_MAX_SOURCE_BYTES, CLIPBOARD_IMAGE_TOO_LARGE_ERROR -} from '../../../src/shared/clipboard-image' -import { isKnownRasterImageMimeType } from '../../../src/shared/file-preview/raster-image-preview-limits' +} from '../../../../src/shared/clipboard-image' +import { isKnownRasterImageMimeType } from '../../../../src/shared/file-preview/raster-image-preview-limits' import { imageParamsSchema, type BridgeImageRunParams -} from '../mobile-web-shell/bridge/bridge-image-verbs' -import { BridgeNativeVerbRefusedError } from '../mobile-web-shell/bridge-host-errors' -import { decodeCanonicalBase64Bytes } from '../transport/encryption/base64-byte-codec' -import { mobileFileExtensionForMime } from './mobile-picked-file-name' -import { freshCacheFolder } from './native-media-device' +} from '../../mobile-web-shell/bridge/bridge-image-verbs' +import { BridgeNativeVerbRefusedError } from '../../mobile-web-shell/bridge-host-errors' +import { decodeCanonicalBase64Bytes } from '../../transport/encryption/base64-byte-codec' +import { mobileFileExtensionForMime } from '../media-picking/mobile-picked-file-name' +import { freshCacheFolder } from '../media-picking/native-media-device' type Staged = { folder: Directory; file: FsFile; handle: FileHandle; byteLength: number } diff --git a/mobile/src/platform/keyboard-occlusion.test.tsx b/mobile/src/platform/keyboard-occlusion.test.tsx deleted file mode 100644 index c2615f7fc63..00000000000 --- a/mobile/src/platform/keyboard-occlusion.test.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' - -type Listener = (event: { - endCoordinates: { height: number; screenY?: number } - duration: number -}) => void - -type KeyboardHarness = { - listeners: Map - removed: string[] - platform: 'ios' | 'android' - // Count all subscriptions, not just surviving listeners, to detect transient duplicate keyboard work. - addListenerCalls: number - metrics: { height: number } | undefined -} - -const keyboard = vi.hoisted((): KeyboardHarness => ({ - listeners: new Map(), - removed: [], - platform: 'ios', - addListenerCalls: 0, - metrics: undefined -})) - -const regions = vi.hoisted( - (): { listener: ((value: { keyboardInset?: number }) => void) | null; removed: boolean } => ({ - listener: null, - removed: false - }) -) -vi.mock('../layout/reserved-regions', () => ({ - subscribeReservedRegionChanges: (listener: (value: { keyboardInset?: number }) => void) => { - regions.listener = listener - return () => { - regions.listener = null - regions.removed = true - } - } -})) - -vi.mock('react-native', () => ({ - Keyboard: { - metrics: () => keyboard.metrics, - addListener: (name: string, listener: Listener) => { - keyboard.addListenerCalls += 1 - keyboard.listeners.set(name, listener) - return { - remove: () => { - keyboard.removed.push(name) - keyboard.listeners.delete(name) - } - } - } - }, - Dimensions: { get: () => ({ height: 951 }) }, - Platform: { - get OS() { - return keyboard.platform - } - } -})) - -import type * as KeyboardOcclusion from './keyboard-occlusion' -let keyboardModule: typeof KeyboardOcclusion - -const subscribeSoftKeyboard: typeof KeyboardOcclusion.subscribeSoftKeyboard = (...args) => - keyboardModule.subscribeSoftKeyboard(...args) - -describe('the keyboard the phone reports', () => { - beforeEach(async () => { - vi.resetModules() - keyboardModule = await import('./keyboard-occlusion') - keyboard.listeners.clear() - keyboard.removed.length = 0 - keyboard.platform = 'ios' - keyboard.addListenerCalls = 0 - keyboard.metrics = undefined - regions.listener = null - regions.removed = false - }) - - it.each(['before', 'after'])('ignores an offscreen frame %s hide without reopening', (order) => { - const calls: string[] = [] - const unsubscribe = subscribeSoftKeyboard( - (height, duration) => calls.push(`show ${height} ${duration}`), - (duration) => calls.push(`hide ${duration}`) - ) - keyboard.listeners.get('keyboardWillShow')?.({ endCoordinates: { height: 306 }, duration: 250 }) - const frame = () => - keyboard.listeners.get('keyboardWillChangeFrame')?.({ - endCoordinates: { height: 253, screenY: 951 }, - duration: 180 - }) - if (order === 'before') { - frame() - } - keyboard.listeners.get('keyboardWillHide')?.({ endCoordinates: { height: 253 }, duration: 180 }) - if (order === 'after') { - frame() - } - // An overlapping transition's in-window frame must not override the explicit hide either. - keyboard.listeners.get('keyboardWillChangeFrame')?.({ - endCoordinates: { height: 253, screenY: 698 }, - duration: 180 - }) - expect(calls).toEqual(['show 306 250', 'hide 180']) - keyboard.listeners.get('keyboardWillShow')?.({ endCoordinates: { height: 306 }, duration: 250 }) - keyboard.listeners.get('keyboardWillChangeFrame')?.({ - endCoordinates: { height: 391, screenY: 560 }, - duration: 120 - }) - expect(calls.slice(-2)).toEqual(['show 306 250', 'show 391 120']) - unsubscribe() - expect(keyboard.listeners.size).toBe(3) - }) - - it('follows native geometry without keyboard events and preserves explicit hide ordering', () => { - const calls: string[] = [] - const unsubscribe = subscribeSoftKeyboard( - (height) => calls.push(`show ${height}`), - () => calls.push('hide') - ) - regions.listener?.({ keyboardInset: 495.5 }) - expect(calls).toEqual([]) - keyboard.listeners.get('keyboardWillShow')?.({ endCoordinates: { height: 495.5 }, duration: 0 }) - regions.listener?.({}) - regions.listener?.({ keyboardInset: 0 }) - expect(calls).toEqual(['show 495.5']) - regions.listener?.({ keyboardInset: 311 }) - expect(calls).toEqual(['show 495.5', 'show 311']) - keyboard.listeners.get('keyboardWillHide')?.({ endCoordinates: { height: 311 }, duration: 180 }) - regions.listener?.({ keyboardInset: 495.5 }) - regions.listener?.({ keyboardInset: 0 }) - expect(calls).toEqual(['show 495.5', 'show 311', 'hide']) - keyboard.listeners.get('keyboardWillShow')?.({ endCoordinates: { height: 0 }, duration: 0 }) - regions.listener?.({ keyboardInset: 311 }) - expect(calls.slice(-2)).toEqual(['show 0', 'show 311']) - unsubscribe() - expect(regions.removed).toBe(false) - }) -}) diff --git a/mobile/src/platform/keyboard-close-blur.web.test.ts b/mobile/src/platform/keyboard/keyboard-close-blur.web.test.ts similarity index 68% rename from mobile/src/platform/keyboard-close-blur.web.test.ts rename to mobile/src/platform/keyboard/keyboard-close-blur.web.test.ts index 3a7c7347956..f9a275733f0 100644 --- a/mobile/src/platform/keyboard-close-blur.web.test.ts +++ b/mobile/src/platform/keyboard/keyboard-close-blur.web.test.ts @@ -79,38 +79,4 @@ describe('a keyboard the phone closes', () => { expect(document.activeElement).toBe(field) }) - - it('keeps focus and reopens the keyboard when Chromium hides it for a tapped autocorrected word', () => { - const field = focusedField(ANDROID) - startBlurringOnKeyboardClose() - - field.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true })) - TextInputState.focusTextInput(field) - shellReports(300) - const focusSessions = vi.fn() - field.addEventListener('focus', focusSessions) - vi.advanceTimersByTime(400) - shellReports(0) - vi.advanceTimersByTime(100) - - expect(document.activeElement).toBe(field) - expect(focusSessions).toHaveBeenCalledOnce() - }) - - it.each([ - ['a touch elsewhere', 'body', 400], - ['a Back long after a touch on the field', 'field', 5000] - ] as const)('still ends focus on a close after %s', (_case, touched, waitMs) => { - const field = focusedField(ANDROID) - startBlurringOnKeyboardClose() - shellReports(300) - - const target = touched === 'field' ? field : document.body - target.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true })) - vi.advanceTimersByTime(waitMs) - shellReports(0) - vi.advanceTimersByTime(1000) - - expect(document.activeElement).not.toBe(field) - }) }) diff --git a/mobile/src/platform/keyboard-close-blur.web.ts b/mobile/src/platform/keyboard/keyboard-close-blur.web.ts similarity index 97% rename from mobile/src/platform/keyboard-close-blur.web.ts rename to mobile/src/platform/keyboard/keyboard-close-blur.web.ts index 54087c6929e..f33878a6672 100644 --- a/mobile/src/platform/keyboard-close-blur.web.ts +++ b/mobile/src/platform/keyboard/keyboard-close-blur.web.ts @@ -1,5 +1,5 @@ import { Keyboard } from 'react-native' -import { hostOs } from './host-os.web' +import { hostOs } from '../host-os.web' import { subscribeSoftKeyboard } from './keyboard-occlusion.web' // Chromium hides the IME itself to show a tapped autocorrected word's menu; Back comes with no touch. diff --git a/mobile/src/platform/keyboard-occlusion.ts b/mobile/src/platform/keyboard/keyboard-occlusion.ts similarity index 96% rename from mobile/src/platform/keyboard-occlusion.ts rename to mobile/src/platform/keyboard/keyboard-occlusion.ts index fcca29cad05..366efba1f7a 100644 --- a/mobile/src/platform/keyboard-occlusion.ts +++ b/mobile/src/platform/keyboard/keyboard-occlusion.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react' import { Dimensions, Keyboard } from 'react-native' -import { subscribeReservedRegionChanges } from '../layout/reserved-regions' -import { hostOs } from './host-os' +import { subscribeReservedRegionChanges } from '../../layout/reserved-regions' +import { hostOs } from '../host-os' export type SoftKeyboardState = { readonly height: number; readonly visible: boolean } diff --git a/mobile/src/platform/keyboard-occlusion.web.ts b/mobile/src/platform/keyboard/keyboard-occlusion.web.ts similarity index 100% rename from mobile/src/platform/keyboard-occlusion.web.ts rename to mobile/src/platform/keyboard/keyboard-occlusion.web.ts diff --git a/mobile/src/platform/keyboard-persisting-taps.ts b/mobile/src/platform/keyboard/keyboard-persisting-taps.ts similarity index 100% rename from mobile/src/platform/keyboard-persisting-taps.ts rename to mobile/src/platform/keyboard/keyboard-persisting-taps.ts diff --git a/mobile/src/platform/primary-modifier-key.ts b/mobile/src/platform/keyboard/primary-modifier-key.ts similarity index 87% rename from mobile/src/platform/primary-modifier-key.ts rename to mobile/src/platform/keyboard/primary-modifier-key.ts index 769305b0da6..6e95d561b1c 100644 --- a/mobile/src/platform/primary-modifier-key.ts +++ b/mobile/src/platform/keyboard/primary-modifier-key.ts @@ -1,4 +1,4 @@ -import { hostOs } from './host-os' +import { hostOs } from '../host-os' /** The submit chord's modifier: Cmd on an Apple keyboard (iPhone, iPad, Mac), Ctrl elsewhere. */ export function primaryModifierKey(): 'Meta' | 'Control' { diff --git a/mobile/src/platform/media-picker-test-shell.ts b/mobile/src/platform/media-picker-test-shell.ts deleted file mode 100644 index e73d091ec5e..00000000000 --- a/mobile/src/platform/media-picker-test-shell.ts +++ /dev/null @@ -1,119 +0,0 @@ -// Model only observable staging/read/eof/release behavior with real verb schemas; native-media tests own device and cache details. -import { - BRIDGE_MEDIA_READ_MAX_BYTES, - mediaNameParamsSchema, - mediaPickParamsSchema, - mediaReadParamsSchema, - mediaReleaseParamsSchema, - type BridgeMediaItem -} from '../mobile-web-shell/bridge/bridge-media-verbs' -import type { BridgeNativeVerb } from '../mobile-web-shell/bridge/bridge-native-verbs' -import { BridgeNativeVerbRefusedError } from '../mobile-web-shell/bridge-host-errors' - -export type StagedTestMedia = { - readonly bytes: Uint8Array - readonly mime?: string - readonly width?: number - readonly height?: number - readonly name?: string -} - -export type MediaTestShell = { - readonly calls: string[] - readonly released: string[] - readonly serveNativeVerb: (verb: BridgeNativeVerb, params: unknown) => Promise -} - -// Offset-derived bytes expose reordered chunks and dropped boundaries without relying on length. -export function stagedTestBytes(byteLength: number): Uint8Array { - const bytes = new Uint8Array(byteLength) - for (let index = 0; index < byteLength; index += 1) { - bytes[index] = (index * 31 + 7) % 251 - } - return bytes -} - -export function encodeTestBase64(bytes: Uint8Array): string { - let binary = '' - for (const byte of bytes) { - binary += String.fromCharCode(byte) - } - return btoa(binary) -} - -export function createMediaTestShell(options: { - readonly staged: Readonly>> - readonly refusePick?: BridgeNativeVerbRefusedError - // Short reads are legal; only eof promises completion. - readonly chunkBytes?: number - // Simulate premature eof so callers cannot silently upload a truncated file. - readonly endAt?: number -}): MediaTestShell { - const calls: string[] = [] - const released: string[] = [] - const live = new Map() - let minted = 0 - - function hold(handle: string): StagedTestMedia { - const record = live.get(handle) - if (record === undefined) { - throw new BridgeNativeVerbRefusedError( - 'native_media_handle_unknown', - 'this session holds no staged item under that handle' - ) - } - return record - } - - return { - calls, - released, - serveNativeVerb: (verb, params) => { - if (verb === 'native.media.pick') { - // Parse the real verb schema so this fake cannot accept params the device would refuse. - const { source, multiple } = mediaPickParamsSchema.parse(params) - calls.push(`pick ${source} ${multiple ? 'multiple' : 'single'}`) - if (options.refusePick) { - return Promise.reject(options.refusePick) - } - const items: BridgeMediaItem[] = (options.staged[source] ?? []).map((item) => { - minted += 1 - const handle = `media-${minted}` - live.set(handle, item) - return { - handle, - mime: item.mime ?? 'image/png', - byteLength: item.bytes.byteLength, - ...(item.width === undefined ? {} : { width: item.width }), - ...(item.height === undefined ? {} : { height: item.height }) - } - }) - return Promise.resolve({ items }) - } - if (verb === 'native.media.read') { - const { handle, offset, length } = mediaReadParamsSchema.parse(params) - calls.push(`read ${handle} ${offset}:${length}`) - const record = hold(handle) - const served = Math.min(length, BRIDGE_MEDIA_READ_MAX_BYTES, options.chunkBytes ?? length) - const last = options.endAt ?? record.bytes.byteLength - const end = Math.min(offset + served, last) - return Promise.resolve({ - base64: encodeTestBase64(record.bytes.subarray(offset, end)), - eof: end >= last - }) - } - if (verb === 'native.media.name') { - const { handle } = mediaNameParamsSchema.parse(params) - calls.push(`name ${handle}`) - return Promise.resolve({ name: hold(handle).name ?? null }) - } - const { handle } = mediaReleaseParamsSchema.parse(params) - calls.push(`release ${handle}`) - const held = live.delete(handle) - if (held) { - released.push(handle) - } - return Promise.resolve({ released: held }) - } - } -} diff --git a/mobile/src/platform/media-picker-contract.ts b/mobile/src/platform/media-picking/media-picker-contract.ts similarity index 95% rename from mobile/src/platform/media-picker-contract.ts rename to mobile/src/platform/media-picking/media-picker-contract.ts index 6108257ba3e..5f446f9e764 100644 --- a/mobile/src/platform/media-picker-contract.ts +++ b/mobile/src/platform/media-picking/media-picker-contract.ts @@ -1,4 +1,4 @@ -import { CLIPBOARD_IMAGE_MAX_SOURCE_BYTES } from '../../../src/shared/clipboard-image' +import { CLIPBOARD_IMAGE_MAX_SOURCE_BYTES } from '../../../../src/shared/clipboard-image' // Keep contracts/errors free of Expo imports so a page-side instanceof cannot pull native picker code into the bundle. diff --git a/mobile/src/platform/media-picker.ts b/mobile/src/platform/media-picking/media-picker.ts similarity index 71% rename from mobile/src/platform/media-picker.ts rename to mobile/src/platform/media-picking/media-picker.ts index 3d22a3e8d83..2065fd1922f 100644 --- a/mobile/src/platform/media-picker.ts +++ b/mobile/src/platform/media-picking/media-picker.ts @@ -1,5 +1,8 @@ -import { pickMobileImage, pickMobileImages } from '../files/attachments/mobile-image-source-picker' -import { pickMobileFiles } from '../files/attachments/mobile-file-source-picker' +import { + pickMobileImage, + pickMobileImages +} from '../../files/attachments/mobile-image-source-picker' +import { pickMobileFiles } from '../../files/attachments/mobile-file-source-picker' import type { MediaPicker } from './media-picker-contract' // Keep Expo picker imports in the native sibling: their codegen lookup throws in browsers. diff --git a/mobile/src/platform/media-picker.web.ts b/mobile/src/platform/media-picking/media-picker.web.ts similarity index 94% rename from mobile/src/platform/media-picker.web.ts rename to mobile/src/platform/media-picking/media-picker.web.ts index a41d502bf5f..21140e3755d 100644 --- a/mobile/src/platform/media-picker.web.ts +++ b/mobile/src/platform/media-picking/media-picker.web.ts @@ -1,15 +1,15 @@ import { useMemo } from 'react' -import type { BridgeMediaItem } from '../mobile-web-shell/bridge/bridge-media-verbs' +import type { BridgeMediaItem } from '../../mobile-web-shell/bridge/bridge-media-verbs' import { readStagedMediaItem, releaseAllStagedMedia, releaseStagedMedia -} from '../mobile-web-shell/bridge/staged-media-bytes' +} from '../../mobile-web-shell/bridge/staged-media-bytes' import { NativeVerbError, useNativeVerbs, type NativeVerbs -} from '../mobile-web-shell/bridge/use-native-verbs' +} from '../../mobile-web-shell/bridge/use-native-verbs' import { MobileFileTooLargeError, type MediaPicker, diff --git a/mobile/src/platform/mobile-picked-file-name.ts b/mobile/src/platform/media-picking/mobile-picked-file-name.ts similarity index 100% rename from mobile/src/platform/mobile-picked-file-name.ts rename to mobile/src/platform/media-picking/mobile-picked-file-name.ts diff --git a/mobile/src/platform/native-media-device.ts b/mobile/src/platform/media-picking/native-media-device.ts similarity index 94% rename from mobile/src/platform/native-media-device.ts rename to mobile/src/platform/media-picking/native-media-device.ts index 539bd16e6fe..6daea70e19a 100644 --- a/mobile/src/platform/native-media-device.ts +++ b/mobile/src/platform/media-picking/native-media-device.ts @@ -2,8 +2,8 @@ import * as Clipboard from 'expo-clipboard' import * as DocumentPicker from 'expo-document-picker' import { Directory, File as FsFile, Paths } from 'expo-file-system' import * as ImagePicker from 'expo-image-picker' -import { agentReadableLibraryImageOptions } from '../files/attachments/mobile-image-source-picker' -import type { MediaHandleRegistry } from '../mobile-web-shell/media-handle-registry' +import { agentReadableLibraryImageOptions } from '../../files/attachments/mobile-image-source-picker' +import type { MediaHandleRegistry } from '../../mobile-web-shell/media-handle-registry' import type { NativeMediaDeps } from './native-media' // Isolate native imports so handlers can be tested with injected device calls. diff --git a/mobile/src/platform/native-media-stage-cleanup.test.ts b/mobile/src/platform/media-picking/native-media-stage-cleanup.test.ts similarity index 98% rename from mobile/src/platform/native-media-stage-cleanup.test.ts rename to mobile/src/platform/media-picking/native-media-stage-cleanup.test.ts index b29c17c0e44..ace030069a7 100644 --- a/mobile/src/platform/native-media-stage-cleanup.test.ts +++ b/mobile/src/platform/media-picking/native-media-stage-cleanup.test.ts @@ -83,7 +83,7 @@ vi.mock('expo-file-system', () => ({ Paths: { cache: 'file:///cache' } })) -import { MediaHandleRegistry } from '../mobile-web-shell/media-handle-registry' +import { MediaHandleRegistry } from '../../mobile-web-shell/media-handle-registry' import { createNativeMediaVerbServer } from './native-media' import { copyPickedMediaIntoCache, nativeMediaDeviceDeps } from './native-media-device' diff --git a/mobile/src/platform/native-media.test.ts b/mobile/src/platform/media-picking/native-media.test.ts similarity index 54% rename from mobile/src/platform/native-media.test.ts rename to mobile/src/platform/media-picking/native-media.test.ts index 5ee9410f0d5..c2f8683270a 100644 --- a/mobile/src/platform/native-media.test.ts +++ b/mobile/src/platform/media-picking/native-media.test.ts @@ -1,30 +1,23 @@ -import { Buffer } from 'node:buffer' import { beforeEach, describe, expect, it, vi } from 'vitest' import { BRIDGE_MEDIA_MAX_LIVE_HANDLES, - BRIDGE_MEDIA_READ_MAX_BYTES, mediaPickResultSchema, mediaReadResultSchema -} from '../mobile-web-shell/bridge/bridge-media-verbs' -import { readShellRefusalCode } from '../mobile-web-shell/bridge-host-errors' -import { MediaHandleRegistry } from '../mobile-web-shell/media-handle-registry' +} from '../../mobile-web-shell/bridge/bridge-media-verbs' +import { readShellRefusalCode } from '../../mobile-web-shell/bridge-host-errors' +import { MediaHandleRegistry } from '../../mobile-web-shell/media-handle-registry' -import type { BridgeNativeVerb } from '../mobile-web-shell/bridge/bridge-native-verbs' +import type { BridgeNativeVerb } from '../../mobile-web-shell/bridge/bridge-native-verbs' import { createNativeMediaVerbServer, type NativeMediaFile } from './native-media' const CACHE = 'file:///cache' -// Track closed handles explicitly because a fake otherwise hides native descriptor leaks. -const handles: { closed: boolean }[] = [] - function fakeFile(bytes: Uint8Array): NativeMediaFile { return { size: bytes.byteLength, open: () => { let cursor = 0 - const ledger = { closed: false } - handles.push(ledger) return { get offset() { return cursor @@ -37,9 +30,7 @@ function fakeFile(bytes: Uint8Array): NativeMediaFile { cursor += slice.byteLength return slice }, - close: () => { - ledger.closed = true - } + close: () => {} } } } @@ -120,7 +111,6 @@ function refusalOf(error: unknown): string | null { } beforeEach(() => { - handles.length = 0 vi.restoreAllMocks() }) @@ -158,36 +148,6 @@ describe('picking', () => { expect(probe.registry.liveCount()).toBe(0) }) - it('discards every copy if cleanup runs between staging and minting', async () => { - const providerUris = ['content://media/1', 'content://media/2'] - const probe = harness({ - launchLibrary: () => - Promise.resolve({ - canceled: false, - assets: providerUris.map((uri) => ({ uri, mimeType: 'image/jpeg' })) - }) - }) - for (const uri of providerUris) { - probe.files.set(uri, bytesOf(4096)) - } - const picked = probe.serve('native.media.pick', { source: 'library', multiple: true }) - await Promise.resolve() - expect(probe.copied).toHaveLength(2) - expect(probe.registry.liveCount()).toBe(0) - probe.registry.releaseAll() - const [live] = probe.registry.mint([ - { uri: `${CACHE}/successor.png`, byteLength: 64, mime: 'image/png' } - ]) - await expect(picked).resolves.toEqual({ items: [] }) - expect(probe.discarded).toEqual(probe.copied.map(({ to }) => to)) - expect(probe.registry.liveCount()).toBe(1) - expect(probe.registry.read(live?.handle ?? '', 0, 64)).toMatchObject({ - uri: `${CACHE}/successor.png`, - end: 64, - eof: true - }) - }) - it('refuses a pick up front when nothing is left, before the OS copies a byte', async () => { const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) const launches: number[] = [] @@ -246,106 +206,6 @@ describe('picking', () => { warn.mockRestore() }) - it.each([0, 2])( - 'deletes an over-cap Files result while preserving %i prior handles', - async (held) => { - const liveUris = Array.from({ length: held }, (_, index) => `${CACHE}/held-${index}.png`) - const returnedUris = Array.from( - { length: BRIDGE_MEDIA_MAX_LIVE_HANDLES - held + 1 }, - (_, index) => `${CACHE}/doc-${index}.pdf` - ) - const probe = harness({ - launchFiles: () => - Promise.resolve({ - canceled: false, - assets: returnedUris.map((uri) => ({ uri, mimeType: 'application/pdf' })) - }) - }) - for (const uri of liveUris) { - probe.files.set(uri, new Uint8Array(4096)) - } - probe.registry.mint(liveUris.map((uri) => ({ uri, mime: 'image/png', byteLength: 4096 }))) - for (const uri of returnedUris) { - probe.files.set(uri, new Uint8Array(1024 * 1024)) - } - const storedBytes = (): number => - [...probe.files.values()].reduce((bytes, file) => bytes + file.byteLength, 0) - expect(storedBytes()).toBe(returnedUris.length * 1024 * 1024 + held * 4096) - await expect( - probe.serve('native.media.pick', { source: 'files', multiple: true }) - ).rejects.toSatisfy((error) => refusalOf(error) === 'native_media_handle_cap') - expect(probe.registry.liveCount()).toBe(held) - expect(probe.discarded).toEqual(returnedUris) - expect([...probe.files.keys()]).toEqual(liveUris) - expect(storedBytes()).toBe(held * 4096) - expect(probe.copied).toEqual([]) - expect(probe.written).toEqual([]) - probe.registry.releaseAll() - expect(probe.files.size).toBe(0) - } - ) - - it('leaves provider originals intact when refusing an over-cap mixed result', async () => { - const providerUri = 'content://media/external/images/media/42' - const ownedUris = Array.from( - { length: BRIDGE_MEDIA_MAX_LIVE_HANDLES }, - (_, index) => `${CACHE}/picked-${index}.png` - ) - const probe = harness({ - launchFiles: () => - Promise.resolve({ - canceled: false, - assets: [providerUri, ...ownedUris].map((uri) => ({ uri, mimeType: 'image/png' })) - }) - }) - for (const uri of [providerUri, ...ownedUris]) { - probe.files.set(uri, new Uint8Array(1024 * 1024)) - } - await expect( - probe.serve('native.media.pick', { source: 'files', multiple: true }) - ).rejects.toSatisfy((error) => refusalOf(error) === 'native_media_handle_cap') - expect(probe.discarded).toEqual(ownedUris) - expect([...probe.files.keys()]).toEqual([providerUri]) - expect(probe.files.get(providerUri)?.byteLength).toBe(1024 * 1024) - expect(probe.registry.liveCount()).toBe(0) - expect(probe.copied).toEqual([]) - }) - - it('continues sweeping refused copies when one file cannot be deleted', async () => { - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - const returnedUris = Array.from( - { length: BRIDGE_MEDIA_MAX_LIVE_HANDLES + 1 }, - (_, index) => `${CACHE}/doc-${index}.pdf` - ) - const probe = harness({ - launchFiles: () => - Promise.resolve({ - canceled: false, - assets: returnedUris.map((uri) => ({ uri, mimeType: 'application/pdf' })) - }), - discard: (uri) => { - probe.discarded.push(uri) - if (uri === returnedUris[0]) { - throw new Error('cache file is busy') - } - probe.files.delete(uri) - } - }) - try { - for (const uri of returnedUris) { - probe.files.set(uri, new Uint8Array(1024 * 1024)) - } - await expect( - probe.serve('native.media.pick', { source: 'files', multiple: true }) - ).rejects.toSatisfy((error) => refusalOf(error) === 'native_media_handle_cap') - expect(probe.discarded).toEqual(returnedUris) - expect([...probe.files.keys()]).toEqual([returnedUris[0]]) - expect(probe.registry.liveCount()).toBe(0) - } finally { - warn.mockRestore() - } - }) - it('refuses an item bigger than the staging ceiling, and deletes what it picked', async () => { const probe = harness({ openFile: () => ({ size: 64 * 1024 * 1024, open: () => fakeFile(new Uint8Array()).open() }) @@ -357,84 +217,6 @@ describe('picking', () => { }) }) -describe('reading chunks', () => { - async function staged(byteLength: number): Promise<{ probe: Harness; handle: string }> { - const probe = harness() - probe.files.set(`${CACHE}/lib.png`, bytesOf(byteLength)) - const result = mediaPickResultSchema.parse( - await probe.serve('native.media.pick', { source: 'library', multiple: false }) - ) - return { probe, handle: result.items[0]?.handle ?? '' } - } - - it('reads a million-byte item in order to eof, and the bytes come back whole', async () => { - const total = 1_000_000 - const { probe, handle } = await staged(total) - const source = bytesOf(total) - const collected = new Uint8Array(total) - let offset = 0 - let reads = 0 - for (;;) { - const chunk = mediaReadResultSchema.parse( - await probe.serve('native.media.read', { - handle, - offset, - length: BRIDGE_MEDIA_READ_MAX_BYTES - }) - ) - reads += 1 - const bytes = atob(chunk.base64) - collected.set(Buffer.from(bytes, 'latin1'), offset) - offset += bytes.length - expect(chunk.eof).toBe(offset === total) - if (chunk.eof) { - break - } - } - expect(reads).toBe(Math.ceil(total / BRIDGE_MEDIA_READ_MAX_BYTES)) - expect(offset).toBe(total) - expect(Buffer.compare(collected, source)).toBe(0) - }) - - it('closes the file handle it opened, on the way out and on the way through a throw', async () => { - const { probe, handle } = await staged(64) - await probe.serve('native.media.read', { handle, offset: 0, length: 16 }) - expect(handles).toHaveLength(1) - expect(handles[0]?.closed).toBe(true) - - const failing = harness({ - openFile: () => ({ - size: 64, - open: () => { - const ledger = { closed: false } - handles.push(ledger) - return { - offset: 0, - readBytesSync: (): Uint8Array => { - throw new Error('the device stopped reading') - }, - close: () => { - ledger.closed = true - } - } - } - }) - }) - failing.files.set(`${CACHE}/lib.png`, bytesOf(64)) - const picked = mediaPickResultSchema.parse( - await failing.serve('native.media.pick', { source: 'library', multiple: false }) - ) - await expect( - failing.serve('native.media.read', { - handle: picked.items[0]?.handle ?? '', - offset: 0, - length: 16 - }) - ).rejects.toThrow(/stopped reading/) - expect(handles.at(-1)?.closed).toBe(true) - }) -}) - describe('a provider uri the Android picker can answer', () => { it('copies it into the cache and mints the handle over the copy, bytes intact', async () => { // Android may return an uncopied provider URI when MIME resolution fails; adopt it rather than lose the selected photo. diff --git a/mobile/src/platform/native-media.ts b/mobile/src/platform/media-picking/native-media.ts similarity index 93% rename from mobile/src/platform/native-media.ts rename to mobile/src/platform/media-picking/native-media.ts index f50af3d2b3c..60c175a6082 100644 --- a/mobile/src/platform/native-media.ts +++ b/mobile/src/platform/media-picking/native-media.ts @@ -6,15 +6,13 @@ import { mediaReadParamsSchema, mediaReleaseParamsSchema, type BridgeMediaSource -} from '../mobile-web-shell/bridge/bridge-media-verbs' -import type { BridgeNativeVerb } from '../mobile-web-shell/bridge/bridge-native-verbs' -import { BridgeNativeVerbRefusedError } from '../mobile-web-shell/bridge-host-errors' -import { - MEDIA_STAGED_MAX_BYTES, - type MediaHandleRegistry, - type StagedMedia -} from '../mobile-web-shell/media-handle-registry' -import { MobileImageBase64Accumulator } from '../files/attachments/mobile-image-base64-accumulator' +} from '../../mobile-web-shell/bridge/bridge-media-verbs' +import type { BridgeNativeVerb } from '../../mobile-web-shell/bridge/bridge-native-verbs' +import { BridgeNativeVerbRefusedError } from '../../mobile-web-shell/bridge-host-errors' +import '../../mobile-web-shell/media-handle-registry' +import type { MediaHandleRegistry, StagedMedia } from '../../mobile-web-shell/media-handle-registry' +import { CLIPBOARD_IMAGE_MAX_SOURCE_BYTES as MEDIA_STAGED_MAX_BYTES } from '../../../../src/shared/clipboard-image' +import { MobileImageBase64Accumulator } from '../../files/attachments/mobile-image-base64-accumulator' // Stage native media behind handles because raw/base64 image sizes exceed reply caps; inject device calls to test provider/cancel/size failures. diff --git a/mobile/src/platform/microphone-screen-lock.test.ts b/mobile/src/platform/microphone-screen-lock.test.ts deleted file mode 100644 index cd6b41eda40..00000000000 --- a/mobile/src/platform/microphone-screen-lock.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -// Hold activation open across release to expose the race that leaves a screen awake after capture ends. -import { describe, expect, it } from 'vitest' -import { createMicrophoneScreenLock, type ScreenLockDevice } from './microphone-screen-lock' - -function createTestDevice() { - const calls: string[] = [] - const settles: (() => void)[] = [] - let holdOpen = false - const answer = (name: string): Promise => { - calls.push(name) - if (!holdOpen) { - return Promise.resolve() - } - return new Promise((resolve) => settles.push(resolve)) - } - const device: ScreenLockDevice = { - activate: (tag) => answer(`+${tag}`), - deactivate: (tag) => answer(`-${tag}`) - } - return { - device, - calls, - holdCallsOpen: () => { - holdOpen = true - }, - settleAll: () => { - holdOpen = false - for (const settle of settles.splice(0)) { - settle() - } - } - } -} - -async function flush(): Promise { - await new Promise((resolve) => setTimeout(resolve, 0)) -} - -describe('the lock a microphone holds', () => { - it('asks the device nothing for a hold it already has', async () => { - const { device, calls } = createTestDevice() - const lock = createMicrophoneScreenLock(device, 'orca-mic') - lock.hold() - lock.hold() - await flush() - expect(calls).toEqual(['+orca-mic']) - }) - - it('asks the device nothing for a release of a tag it never took', async () => { - const { device, calls } = createTestDevice() - const lock = createMicrophoneScreenLock(device, 'orca-mic') - lock.release() - await flush() - expect(calls).toEqual([]) - lock.hold() - lock.release() - lock.release() - await flush() - expect(calls).toEqual(['+orca-mic', '-orca-mic']) - }) - - it('never lets a slow activate land after the release that followed it', async () => { - const { device, calls, holdCallsOpen, settleAll } = createTestDevice() - const lock = createMicrophoneScreenLock(device, 'orca-mic') - holdCallsOpen() - lock.hold() - await flush() - // Release before activation settles must still deactivate last. - lock.release() - await flush() - expect(calls).toEqual(['+orca-mic']) - settleAll() - await flush() - expect(calls).toEqual(['+orca-mic', '-orca-mic']) - }) - - it('keeps working after a call the device refused', async () => { - const calls: string[] = [] - const device: ScreenLockDevice = { - activate: (tag) => { - calls.push(`+${tag}`) - return Promise.reject(new Error('no current activity')) - }, - deactivate: (tag) => { - calls.push(`-${tag}`) - return Promise.resolve() - } - } - const lock = createMicrophoneScreenLock(device, 'orca-mic') - lock.hold() - lock.release() - await flush() - // Failed wake locks must neither fail dictation nor block queued cleanup. - expect(calls).toEqual(['+orca-mic', '-orca-mic']) - lock.hold() - await flush() - expect(calls).toEqual(['+orca-mic', '-orca-mic', '+orca-mic']) - }) -}) diff --git a/mobile/src/platform/native-media-device.test.ts b/mobile/src/platform/native-media-device.test.ts deleted file mode 100644 index 4f5dc2c2c8a..00000000000 --- a/mobile/src/platform/native-media-device.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' - -const expo = vi.hoisted(() => ({ - launchImageLibraryAsync: vi.fn((_options: Record) => - Promise.resolve({ canceled: true }) - ), - requestMediaLibraryPermissionsAsync: vi.fn(() => Promise.resolve({ granted: true })), - getDocumentAsync: vi.fn(() => Promise.resolve({ canceled: true })), - getImageAsync: vi.fn(() => Promise.resolve(null)), - deleted: new Array(), - copiedBytes: new Array<{ uri: string; bytes: number[] }>(), - sources: new Map() -})) - -vi.mock('expo-clipboard', () => ({ getImageAsync: expo.getImageAsync })) -vi.mock('expo-document-picker', () => ({ getDocumentAsync: expo.getDocumentAsync })) -vi.mock('react-native', () => ({ Platform: { OS: 'ios' } })) -vi.mock('expo-image-picker', () => ({ - UIImagePickerPreferredAssetRepresentationMode: { Compatible: 'compatible' }, - launchImageLibraryAsync: expo.launchImageLibraryAsync, - requestMediaLibraryPermissionsAsync: expo.requestMediaLibraryPermissionsAsync -})) -vi.mock('expo-file-system', () => ({ - File: class { - readonly uri: string - readonly size = 7 - constructor(...parts: string[]) { - this.uri = parts.join('/') - } - create(): void {} - writeSync(content: string | Uint8Array): void { - if (typeof content !== 'string') { - expo.copiedBytes.push({ uri: this.uri, bytes: [...content] }) - } - } - bytesSync(): Uint8Array { - const bytes = expo.sources.get(this.uri) - if (bytes === undefined) { - throw new Error(`nothing readable at ${this.uri}`) - } - return bytes - } - delete(): void { - expo.deleted.push(this.uri) - } - }, - Paths: { cache: 'file:///cache' } -})) - -import { copyPickedMediaIntoCache } from './native-media-device' - -describe('copying a provider uri into the cache', () => { - it('deletes the file it created when the source could not be read', () => { - // Clean failed writes here because callers never learn the new file’s name. - const before = expo.deleted.length - expect(() => copyPickedMediaIntoCache('content://media/gone')).toThrow(/nothing readable/) - expect(expo.deleted.length).toBe(before + 1) - expect(expo.deleted.at(-1)?.startsWith('file:///cache/orca-media-')).toBe(true) - }) -}) diff --git a/mobile/src/platform/native-pdf-view.ts b/mobile/src/platform/pdf-viewing/native-pdf-view.ts similarity index 79% rename from mobile/src/platform/native-pdf-view.ts rename to mobile/src/platform/pdf-viewing/native-pdf-view.ts index c1627e2573a..25ee48cd393 100644 --- a/mobile/src/platform/native-pdf-view.ts +++ b/mobile/src/platform/pdf-viewing/native-pdf-view.ts @@ -2,23 +2,23 @@ import { useCallback, useLayoutEffect, useMemo, useRef } from 'react' import { Platform } from 'react-native' import { File as FsFile } from 'expo-file-system' import { useFocusEffect, useNavigation } from 'expo-router' -import { presentPdf } from '../../modules/orca-mobile-web-shell/src' -import { getRuntimePathBasename } from '../../../src/shared/cross-platform-path' -import { PDF_MIME_TYPE } from '../files/media/mobile-file-media' +import { presentPdf } from '../../../modules/orca-mobile-web-shell/src/index' +import { getRuntimePathBasename } from '../../../../src/shared/cross-platform-path' +import { PDF_MIME_TYPE } from '../../files/media/mobile-file-media' import { downloadMobileFileMedia, MobileMediaHostOutdatedError, type MobileMediaSink -} from '../files/media/mobile-file-media-download' +} from '../../files/media/mobile-file-media-download' import { pdfViewParamsSchema, type BridgePdfOpenParams -} from '../mobile-web-shell/bridge/bridge-native-verbs' -import { BridgeNativeVerbRefusedError } from '../mobile-web-shell/bridge-host-errors' -import { scopeRpcClientToExecutionHost } from '../worktree/execution-host-scoped-rpc-client' -import { useHostClient } from '../transport/connection/host-client-hooks' -import { discardUnreturnedMediaFile, freshCacheFolder } from './native-media-device' -import type { RpcClient } from '../transport/rpc/rpc-client-contract' +} from '../../mobile-web-shell/bridge/bridge-native-verbs' +import { BridgeNativeVerbRefusedError } from '../../mobile-web-shell/bridge-host-errors' +import { scopeRpcClientToExecutionHost } from '../../worktree/execution-host-scoped-rpc-client' +import { useHostClient } from '../../transport/connection/host-client-hooks' +import { discardUnreturnedMediaFile, freshCacheFolder } from '../media-picking/native-media-device' +import type { RpcClient } from '../../transport/rpc/rpc-client-contract' function pdfFileSink(file: FsFile): MobileMediaSink { file.create({ overwrite: true }) diff --git a/mobile/src/platform/pdf-opener-contract.ts b/mobile/src/platform/pdf-viewing/pdf-opener-contract.ts similarity index 100% rename from mobile/src/platform/pdf-opener-contract.ts rename to mobile/src/platform/pdf-viewing/pdf-opener-contract.ts diff --git a/mobile/src/platform/pdf-opener.ts b/mobile/src/platform/pdf-viewing/pdf-opener.ts similarity index 100% rename from mobile/src/platform/pdf-opener.ts rename to mobile/src/platform/pdf-viewing/pdf-opener.ts diff --git a/mobile/src/platform/pdf-opener.web.ts b/mobile/src/platform/pdf-viewing/pdf-opener.web.ts similarity index 84% rename from mobile/src/platform/pdf-opener.web.ts rename to mobile/src/platform/pdf-viewing/pdf-opener.web.ts index 46b9203c639..0a87ea7d194 100644 --- a/mobile/src/platform/pdf-opener.web.ts +++ b/mobile/src/platform/pdf-viewing/pdf-opener.web.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react' -import { parseExecutionHostId } from '../../../src/shared/execution-host' -import { NativeVerbError, useNativeVerbs } from '../mobile-web-shell/bridge/use-native-verbs' -import { useWorkspaceExecutionHost } from '../navigation/workspace-execution-host' +import { parseExecutionHostId } from '../../../../src/shared/execution-host' +import { NativeVerbError, useNativeVerbs } from '../../mobile-web-shell/bridge/use-native-verbs' +import { useWorkspaceExecutionHost } from '../../navigation/workspace-execution-host' import { PdfOpenRefusedError, type PdfOpener } from './pdf-opener-contract' // Older shells lack the verbs, so callers get null and ask for an app update instead. diff --git a/mobile/src/platform/shell-storage.ts b/mobile/src/platform/shell-storage/shell-storage.ts similarity index 100% rename from mobile/src/platform/shell-storage.ts rename to mobile/src/platform/shell-storage/shell-storage.ts diff --git a/mobile/src/platform/shell-storage.web.ts b/mobile/src/platform/shell-storage/shell-storage.web.ts similarity index 77% rename from mobile/src/platform/shell-storage.web.ts rename to mobile/src/platform/shell-storage/shell-storage.web.ts index ff88ce7f1ba..bd53c374a62 100644 --- a/mobile/src/platform/shell-storage.web.ts +++ b/mobile/src/platform/shell-storage/shell-storage.web.ts @@ -1,5 +1,5 @@ import { useSyncExternalStore } from 'react' -import { usePageBridgeClientIfPresent } from '../transport/connection/client-context.web' +import { usePageBridgeClientIfPresent } from '../../transport/connection/client-context.web' export function useShellStorage(): Readonly> | null { const client = usePageBridgeClientIfPresent() diff --git a/mobile/src/platform/native-speech-device.ts b/mobile/src/platform/speech/native-speech-device.ts similarity index 81% rename from mobile/src/platform/native-speech-device.ts rename to mobile/src/platform/speech/native-speech-device.ts index d2e46302426..b17a8632405 100644 --- a/mobile/src/platform/native-speech-device.ts +++ b/mobile/src/platform/speech/native-speech-device.ts @@ -5,8 +5,8 @@ import { speechFinish, speechStart } from '@orca/expo-two-way-audio' -import { loadDictationEnginePreference } from '../storage/dictation-engine-preference' -import { NATIVE_AUDIO_DEVICE_SAMPLE_RATE } from './native-audio-device' +import { loadDictationEnginePreference } from '../../storage/dictation-engine-preference' +import { NATIVE_AUDIO_DEVICE_SAMPLE_RATE } from '../audio-capture/native-audio-device' import type { NativeSpeechEngine } from './native-speech' // Isolate the device module so tests drive the server with a fake engine. diff --git a/mobile/src/platform/native-speech.test.ts b/mobile/src/platform/speech/native-speech.test.ts similarity index 97% rename from mobile/src/platform/native-speech.test.ts rename to mobile/src/platform/speech/native-speech.test.ts index 62a5be12d16..af1eb75f40a 100644 --- a/mobile/src/platform/native-speech.test.ts +++ b/mobile/src/platform/speech/native-speech.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import type { DictationEnginePreference } from '../mobile-web-shell/bridge/bridge-speech-verbs' +import type { DictationEnginePreference } from '../../mobile-web-shell/bridge/bridge-speech-verbs' import { createNativeSpeechServer, type NativeSpeechEngine } from './native-speech' function engineWith(preference: DictationEnginePreference): NativeSpeechEngine { diff --git a/mobile/src/platform/native-speech.ts b/mobile/src/platform/speech/native-speech.ts similarity index 99% rename from mobile/src/platform/native-speech.ts rename to mobile/src/platform/speech/native-speech.ts index 463132d9866..c3aeebe2579 100644 --- a/mobile/src/platform/native-speech.ts +++ b/mobile/src/platform/speech/native-speech.ts @@ -4,7 +4,7 @@ import { type BridgeSpeechFailure, type BridgeSpeechRead, type DictationEnginePreference -} from '../mobile-web-shell/bridge/bridge-speech-verbs' +} from '../../mobile-web-shell/bridge/bridge-speech-verbs' // The shell side of native.speech.act. The recognizer itself lives in the native audio module; this keeps its events between the page's polls. diff --git a/mobile/src/platform/on-device-speech-contract.ts b/mobile/src/platform/speech/on-device-speech-contract.ts similarity index 89% rename from mobile/src/platform/on-device-speech-contract.ts rename to mobile/src/platform/speech/on-device-speech-contract.ts index 3fc61546b91..ea480c17585 100644 --- a/mobile/src/platform/on-device-speech-contract.ts +++ b/mobile/src/platform/speech/on-device-speech-contract.ts @@ -3,7 +3,7 @@ import type { BridgeSpeechRead, BridgeSpeechStartResult, BridgeSpeechStatusResult -} from '../mobile-web-shell/bridge/bridge-speech-verbs' +} from '../../mobile-web-shell/bridge/bridge-speech-verbs' // The phone's own recognizer as dictation sees it, in the phone's language. Any rejection means "use the computer". export type OnDeviceSpeech = { diff --git a/mobile/src/platform/on-device-speech-locale.ts b/mobile/src/platform/speech/on-device-speech-locale.ts similarity index 77% rename from mobile/src/platform/on-device-speech-locale.ts rename to mobile/src/platform/speech/on-device-speech-locale.ts index cf5edb0684e..ce0bde3c208 100644 --- a/mobile/src/platform/on-device-speech-locale.ts +++ b/mobile/src/platform/speech/on-device-speech-locale.ts @@ -1,4 +1,4 @@ -import { readDeviceUiLocaleTags } from '../i18n/system-ui-locale' +import { readDeviceUiLocaleTags } from '../../i18n/system-ui-locale' // The phone's first language, read the same way in the shell's settings and the page, so both ask the recognizer about one locale. export function onDeviceSpeechLocale(): string { diff --git a/mobile/src/platform/on-device-speech-locale.web.ts b/mobile/src/platform/speech/on-device-speech-locale.web.ts similarity index 100% rename from mobile/src/platform/on-device-speech-locale.web.ts rename to mobile/src/platform/speech/on-device-speech-locale.web.ts diff --git a/mobile/src/platform/on-device-speech.ts b/mobile/src/platform/speech/on-device-speech.ts similarity index 80% rename from mobile/src/platform/on-device-speech.ts rename to mobile/src/platform/speech/on-device-speech.ts index b592de40404..6151b188895 100644 --- a/mobile/src/platform/on-device-speech.ts +++ b/mobile/src/platform/speech/on-device-speech.ts @@ -1,4 +1,4 @@ -import type { DictationTerm } from '../../../src/shared/dictation-dictionary' +import type { DictationTerm } from '../../../../src/shared/dictation-dictionary' import type { OnDeviceSpeech } from './on-device-speech-contract' // The session screen is a page in the shell (on-device-speech.web.ts); native screens dictate through the computer. diff --git a/mobile/src/platform/on-device-speech.web.test.tsx b/mobile/src/platform/speech/on-device-speech.web.test.tsx similarity index 96% rename from mobile/src/platform/on-device-speech.web.test.tsx rename to mobile/src/platform/speech/on-device-speech.web.test.tsx index 2bcf77019fa..efc8742c704 100644 --- a/mobile/src/platform/on-device-speech.web.test.tsx +++ b/mobile/src/platform/speech/on-device-speech.web.test.tsx @@ -4,7 +4,7 @@ import { act, create } from 'react-test-renderer' import { describe, expect, it, vi } from 'vitest' import type { OnDeviceSpeech } from './on-device-speech-contract' -vi.mock('../mobile-web-shell/bridge/use-native-verbs', () => { +vi.mock('../../mobile-web-shell/bridge/use-native-verbs', () => { const verbs = { canRecognizeSpeech: true, speechStatus: async () => ({ status: 'ready', locale: 'de-DE', preference: 'automatic' }), diff --git a/mobile/src/platform/on-device-speech.web.ts b/mobile/src/platform/speech/on-device-speech.web.ts similarity index 90% rename from mobile/src/platform/on-device-speech.web.ts rename to mobile/src/platform/speech/on-device-speech.web.ts index fbaee133ffe..e9df5bb33ec 100644 --- a/mobile/src/platform/on-device-speech.web.ts +++ b/mobile/src/platform/speech/on-device-speech.web.ts @@ -1,7 +1,7 @@ import { useLayoutEffect, useMemo, useRef } from 'react' -import type { DictationTerm } from '../../../src/shared/dictation-dictionary' -import { createDictationTextPipeline } from '../../../src/shared/dictation-text-pipeline' -import { useNativeVerbs, type NativeVerbs } from '../mobile-web-shell/bridge/use-native-verbs' +import type { DictationTerm } from '../../../../src/shared/dictation-dictionary' +import { createDictationTextPipeline } from '../../../../src/shared/dictation-text-pipeline' +import { useNativeVerbs, type NativeVerbs } from '../../mobile-web-shell/bridge/use-native-verbs' import type { OnDeviceSpeech } from './on-device-speech-contract' import { onDeviceSpeechLocale } from './on-device-speech-locale' diff --git a/mobile/src/platform/live-input-composing-range.test.ts b/mobile/src/platform/text-input/live-input-composing-range.test.ts similarity index 100% rename from mobile/src/platform/live-input-composing-range.test.ts rename to mobile/src/platform/text-input/live-input-composing-range.test.ts diff --git a/mobile/src/platform/live-input-composing-range.ts b/mobile/src/platform/text-input/live-input-composing-range.ts similarity index 100% rename from mobile/src/platform/live-input-composing-range.ts rename to mobile/src/platform/text-input/live-input-composing-range.ts diff --git a/mobile/src/platform/text-input-content-height.ts b/mobile/src/platform/text-input/text-input-content-height.ts similarity index 100% rename from mobile/src/platform/text-input-content-height.ts rename to mobile/src/platform/text-input/text-input-content-height.ts diff --git a/mobile/src/platform/text-input-content-height.web.ts b/mobile/src/platform/text-input/text-input-content-height.web.ts similarity index 100% rename from mobile/src/platform/text-input-content-height.web.ts rename to mobile/src/platform/text-input/text-input-content-height.web.ts diff --git a/mobile/src/platform/text-input-font-size.ts b/mobile/src/platform/text-input/text-input-font-size.ts similarity index 51% rename from mobile/src/platform/text-input-font-size.ts rename to mobile/src/platform/text-input/text-input-font-size.ts index d82c626b43d..dabbc5a32d8 100644 --- a/mobile/src/platform/text-input-font-size.ts +++ b/mobile/src/platform/text-input/text-input-font-size.ts @@ -1,3 +1,3 @@ -import { typography } from '../theme/mobile-theme' +import { typography } from '../../theme/mobile-theme' export const TEXT_INPUT_FONT_SIZE = typography.bodySize diff --git a/mobile/src/platform/text-input-font-size.web.ts b/mobile/src/platform/text-input/text-input-font-size.web.ts similarity index 87% rename from mobile/src/platform/text-input-font-size.web.ts rename to mobile/src/platform/text-input/text-input-font-size.web.ts index 1cd340159d0..628738be7ea 100644 --- a/mobile/src/platform/text-input-font-size.web.ts +++ b/mobile/src/platform/text-input/text-input-font-size.web.ts @@ -1,4 +1,4 @@ -import { typography } from '../theme/mobile-theme' +import { typography } from '../../theme/mobile-theme' // iOS zooms focused inputs below 16px; share this floor with the route-closure census. export const TEXT_INPUT_FONT_SIZE_FLOOR = 16 diff --git a/mobile/src/platform/text-input-handle.ts b/mobile/src/platform/text-input/text-input-handle.ts similarity index 100% rename from mobile/src/platform/text-input-handle.ts rename to mobile/src/platform/text-input/text-input-handle.ts diff --git a/mobile/src/platform/text-input-selection-reveal.web.ts b/mobile/src/platform/text-input/text-input-selection-reveal.web.ts similarity index 100% rename from mobile/src/platform/text-input-selection-reveal.web.ts rename to mobile/src/platform/text-input/text-input-selection-reveal.web.ts diff --git a/mobile/src/platform/use-native-device-verbs.ts b/mobile/src/platform/use-native-device-verbs.ts index 647133dea35..68fb6c4575d 100644 --- a/mobile/src/platform/use-native-device-verbs.ts +++ b/mobile/src/platform/use-native-device-verbs.ts @@ -2,14 +2,17 @@ import { useEffect, useMemo } from 'react' import type { BridgeNativeVerb } from '../mobile-web-shell/bridge/bridge-native-verbs' import type { BRIDGE_STORE_VERB } from '../mobile-web-shell/bridge/bridge-store-verb' import { useMediaHandleRegistry } from '../mobile-web-shell/use-media-handle-registry' -import { createNativeAudioCapture } from './native-audio' -import { NATIVE_AUDIO_DEVICE_SAMPLE_RATE, nativeAudioDeviceEngine } from './native-audio-device' -import { createNativeSpeechServer } from './native-speech' -import { nativeSpeechDeviceEngine } from './native-speech-device' -import { serveNativeClipboardVerb } from './native-clipboard' -import { serveNativeAnnounceVerb } from './native-accessibility-announce' -import { createNativeMediaVerbServer } from './native-media' -import { discardStagedMedia, nativeMediaDeviceDeps } from './native-media-device' +import { createNativeAudioCapture } from './audio-capture/native-audio' +import { + NATIVE_AUDIO_DEVICE_SAMPLE_RATE, + nativeAudioDeviceEngine +} from './audio-capture/native-audio-device' +import { createNativeSpeechServer } from './speech/native-speech' +import { nativeSpeechDeviceEngine } from './speech/native-speech-device' +import { serveNativeClipboardVerb } from './clipboard/native-clipboard' +import { serveNativeAnnounceVerb } from './accessibility/native-accessibility-announce' +import { createNativeMediaVerbServer } from './media-picking/native-media' +import { discardStagedMedia, nativeMediaDeviceDeps } from './media-picking/native-media-device' export type NativeDeviceVerb = Exclude< BridgeNativeVerb, diff --git a/mobile/src/platform/view-measurement/page-box-contract.ts b/mobile/src/platform/view-measurement/page-box-contract.ts new file mode 100644 index 00000000000..b485b0f65a5 --- /dev/null +++ b/mobile/src/platform/view-measurement/page-box-contract.ts @@ -0,0 +1 @@ +export type PageBox = { left: number; top: number; width: number; height: number } diff --git a/mobile/src/platform/page-box.ts b/mobile/src/platform/view-measurement/page-box.ts similarity index 80% rename from mobile/src/platform/page-box.ts rename to mobile/src/platform/view-measurement/page-box.ts index 32d859854d7..1821533a492 100644 --- a/mobile/src/platform/page-box.ts +++ b/mobile/src/platform/view-measurement/page-box.ts @@ -1,6 +1,6 @@ import type { HostInstance } from 'react-native' -export type PageBox = { left: number; top: number; width: number; height: number } +import type { PageBox } from './page-box-contract' // A view's box in page coordinates, the space touch events report pageX/pageY in; null when it has none. export function measurePageBox(target: HostInstance, onBox: (box: PageBox | null) => void): void { diff --git a/mobile/src/platform/page-box.web.ts b/mobile/src/platform/view-measurement/page-box.web.ts similarity index 89% rename from mobile/src/platform/page-box.web.ts rename to mobile/src/platform/view-measurement/page-box.web.ts index 604e33f09fa..fe28a65be5f 100644 --- a/mobile/src/platform/page-box.web.ts +++ b/mobile/src/platform/view-measurement/page-box.web.ts @@ -1,7 +1,5 @@ import type { View } from 'react-native' -import type { PageBox } from './page-box' - -export type { PageBox } from './page-box' +import type { PageBox } from './page-box-contract' function hasClientRect(node: unknown): node is { getBoundingClientRect: () => DOMRect } { return ( diff --git a/mobile/src/session/MobileSessionSheets.tsx b/mobile/src/session/MobileSessionSheets.tsx index 7cf3bf558db..60257879acb 100644 --- a/mobile/src/session/MobileSessionSheets.tsx +++ b/mobile/src/session/MobileSessionSheets.tsx @@ -1,11 +1,11 @@ import { Platform } from 'react-native' import { useMobileTranslation } from '../i18n/use-mobile-translation' import { getRepoIdFromWorktreeId } from '../../../src/shared/worktree/id' -import { useClipboardWriter } from '../platform/clipboard' +import { useClipboardWriter } from '../platform/clipboard/clipboard' import { Copy, FileText, RefreshCw } from 'lucide-react-native' import { MobileSessionTerminalLayoutSheet } from '../terminal/session/MobileSessionTerminalLayoutSheet' import { QuickCommandsSheet } from '../terminal/quick-commands/QuickCommandsSheet' -import { triggerSuccess, triggerError } from '../platform/haptics' +import { triggerSuccess, triggerError } from '../platform/haptics/haptics' import { ActionSheetModal } from '../components/ActionSheetModal' import { TextInputModal } from '../components/TextInputModal' import { MobileTerminalRenameModal } from '../terminal/session/MobileTerminalRenameModal' diff --git a/mobile/src/session/MobileSessionTitleBlock.tsx b/mobile/src/session/MobileSessionTitleBlock.tsx index 59fe3bf8dc4..f1d0a9543d0 100644 --- a/mobile/src/session/MobileSessionTitleBlock.tsx +++ b/mobile/src/session/MobileSessionTitleBlock.tsx @@ -7,7 +7,7 @@ import type { ConnectionState } from '../transport/connection/connection-state' import { styles } from './mobile-session-styles' import { useMobileTranslation } from '../i18n/use-mobile-translation' import { pressedStyle } from '../theme/mobile-theme' -import { useKeyboardPersistingTaps } from '../platform/keyboard-persisting-taps' +import { useKeyboardPersistingTaps } from '../platform/keyboard/keyboard-persisting-taps' /** The session's title and status line. A tap opens a card with the full name, where it lives * and its host, as desktop's tab card does, because a sideways phone cuts the title short. */ diff --git a/mobile/src/session/__mocks__/mobile-prompt-render-mocks.ts b/mobile/src/session/__mocks__/mobile-prompt-render-mocks.ts index b9ca8ec1ad5..5e4b4233439 100644 --- a/mobile/src/session/__mocks__/mobile-prompt-render-mocks.ts +++ b/mobile/src/session/__mocks__/mobile-prompt-render-mocks.ts @@ -67,7 +67,9 @@ vi.mock('../native-chat/transcript/MobileNativeChatRowOverlaysHost', () => ({ vi.mock('../native-chat/visual/MobileNativeChatVisual', () => ({ useMobileNativeChatVisualRenderer: () => null })) -vi.mock('../../platform/use-reduced-motion', () => ({ useReducedMotionEnabled: () => null })) +vi.mock('../../platform/accessibility/use-reduced-motion', () => ({ + useReducedMotionEnabled: () => null +})) vi.mock('../native-chat/transcript/MobileNativeChatLiveLine', () => ({ MobileNativeChatLiveLine: 'LiveStatus' })) diff --git a/mobile/src/session/agent-launch/new-tab-agent-host-launch.ts b/mobile/src/session/agent-launch/new-tab-agent-host-launch.ts index 38438ae18ab..4e644a5417a 100644 --- a/mobile/src/session/agent-launch/new-tab-agent-host-launch.ts +++ b/mobile/src/session/agent-launch/new-tab-agent-host-launch.ts @@ -3,7 +3,7 @@ import type { MobileSessionTab } from '../tabs/mobile-session-tabs' import type { TuiAgent } from '../../../../src/shared/agent-launch/catalog/tui-agent' import { AGENT_LAUNCH_PLACEMENT_RUNTIME_CAPABILITY } from '../../../../src/shared/agent-launch/contracts/agent-launch-runtime-capability' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import { launchAgentInExistingWorkspace, reserveMobileAgentLaunch diff --git a/mobile/src/session/mobile-dictation-session-outcomes.test.tsx b/mobile/src/session/mobile-dictation-session-outcomes.test.tsx index 46d24d9a1d9..7b788142944 100644 --- a/mobile/src/session/mobile-dictation-session-outcomes.test.tsx +++ b/mobile/src/session/mobile-dictation-session-outcomes.test.tsx @@ -49,14 +49,14 @@ vi.mock('expo-router', () => { const router = { canGoBack: () => false, push: () => {}, dismissTo: () => {} } return { useFocusEffect: () => {}, useRouter: () => router, useNavigation: () => undefined } }) -vi.mock('../platform/haptics', () => ({ +vi.mock('../platform/haptics/haptics', () => ({ triggerError: () => { seen.errorHaptics += 1 }, triggerEdgeBump: () => {}, triggerSelection: () => {} })) -vi.mock('../platform/clipboard', () => { +vi.mock('../platform/clipboard/clipboard', () => { const writer = { writeText: async (text: string) => { seen.clipboard.push(text) diff --git a/mobile/src/session/mobile-structured-chat-fork.ts b/mobile/src/session/mobile-structured-chat-fork.ts index 009bde60332..10551804932 100644 --- a/mobile/src/session/mobile-structured-chat-fork.ts +++ b/mobile/src/session/mobile-structured-chat-fork.ts @@ -1,5 +1,5 @@ import { translate } from '../i18n/mobile-i18n' -import { triggerError } from '../platform/haptics' +import { triggerError } from '../platform/haptics/haptics' import { TUI_AGENT_DISPLAY_NAMES } from '../../../src/shared/agent-launch/catalog/tui-agent-display-names' import type { TuiAgent } from '../../../src/shared/agent-launch/catalog/tui-agent' import type { diff --git a/mobile/src/session/native-chat/MobileNativeChatFloatingControls.tsx b/mobile/src/session/native-chat/MobileNativeChatFloatingControls.tsx index a7b69265afa..8f6194c3e25 100644 --- a/mobile/src/session/native-chat/MobileNativeChatFloatingControls.tsx +++ b/mobile/src/session/native-chat/MobileNativeChatFloatingControls.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react' import { Animated, Easing, Platform, Pressable, View } from 'react-native' import { ArrowDown } from 'lucide-react-native' import { SHEET_CURVE } from '../../components/sheet-motion-values' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' import { colors, feedback } from '../../theme/mobile-theme' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { styles } from './mobile-native-chat-view-styles' diff --git a/mobile/src/session/native-chat/composer/MobileNativeChatComposerField.web.tsx b/mobile/src/session/native-chat/composer/MobileNativeChatComposerField.web.tsx index 0cf471bfc88..68dc976db86 100644 --- a/mobile/src/session/native-chat/composer/MobileNativeChatComposerField.web.tsx +++ b/mobile/src/session/native-chat/composer/MobileNativeChatComposerField.web.tsx @@ -7,10 +7,10 @@ import { useWindowDimensions, type TextInputInstance } from 'react-native' -import { useKeyboardOcclusion } from '../../../platform/keyboard-occlusion' -import { primaryModifierKey } from '../../../platform/primary-modifier-key' -import { useTextInputContentHeight } from '../../../platform/text-input-content-height' -import { useTextInputSelectionReveal } from '../../../platform/text-input-selection-reveal.web' +import { useKeyboardOcclusion } from '../../../platform/keyboard/keyboard-occlusion' +import { primaryModifierKey } from '../../../platform/keyboard/primary-modifier-key' +import { useTextInputContentHeight } from '../../../platform/text-input/text-input-content-height' +import { useTextInputSelectionReveal } from '../../../platform/text-input/text-input-selection-reveal.web' import { colors } from '../../../theme/mobile-theme' import { useMobileColorScheme } from '../../../theme/use-mobile-color-scheme' import { diff --git a/mobile/src/session/native-chat/composer/MobileNativeChatComposerMenuLayer.web.tsx b/mobile/src/session/native-chat/composer/MobileNativeChatComposerMenuLayer.web.tsx index bde29deb099..71a3f75b91e 100644 --- a/mobile/src/session/native-chat/composer/MobileNativeChatComposerMenuLayer.web.tsx +++ b/mobile/src/session/native-chat/composer/MobileNativeChatComposerMenuLayer.web.tsx @@ -1,8 +1,8 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' import { View } from 'react-native' -import { useKeyboardOcclusion } from '../../../platform/keyboard-occlusion' -import { useKeyboardPersistingTaps } from '../../../platform/keyboard-persisting-taps' +import { useKeyboardOcclusion } from '../../../platform/keyboard/keyboard-occlusion' +import { useKeyboardPersistingTaps } from '../../../platform/keyboard/keyboard-persisting-taps' import { useMobileNativeChatLowerRegion } from '../mobile-native-chat-lower-region-context' import { useMobileNativeChatComposerAllocation } from './MobileNativeChatComposerAllocation' import type { ComposerMenuLayerProps } from './MobileNativeChatComposerMenuLayer' diff --git a/mobile/src/session/native-chat/composer/MobileNativeChatContextUsageRing.tsx b/mobile/src/session/native-chat/composer/MobileNativeChatContextUsageRing.tsx index 34a2162a2f2..5c408b1a510 100644 --- a/mobile/src/session/native-chat/composer/MobileNativeChatContextUsageRing.tsx +++ b/mobile/src/session/native-chat/composer/MobileNativeChatContextUsageRing.tsx @@ -24,8 +24,8 @@ import { COMPOSER_CONTROL_HIT_SLOP, composerInputStyles as inputStyles } from '../../../components/composer-input/composer-input-styles' -import { useKeyboardPersistingTaps } from '../../../platform/keyboard-persisting-taps' -import { useKeyboardOcclusion } from '../../../platform/keyboard-occlusion' +import { useKeyboardPersistingTaps } from '../../../platform/keyboard/keyboard-persisting-taps' +import { useKeyboardOcclusion } from '../../../platform/keyboard/keyboard-occlusion' import { useMobileTranslation } from '../../../i18n/use-mobile-translation' import { colors, radii, spacing, typography } from '../../../theme/mobile-theme' import { themedStyleSheet } from '../../../theme/themed-styles' diff --git a/mobile/src/session/native-chat/composer/MobileNativeChatSendButton.tsx b/mobile/src/session/native-chat/composer/MobileNativeChatSendButton.tsx index 66c1ba3b1db..b2b3336b0cc 100644 --- a/mobile/src/session/native-chat/composer/MobileNativeChatSendButton.tsx +++ b/mobile/src/session/native-chat/composer/MobileNativeChatSendButton.tsx @@ -13,7 +13,7 @@ import { composerInputStyles as inputStyles } from '../../../components/composer-input/composer-input-styles' import { useMobileTranslation } from '../../../i18n/use-mobile-translation' -import { useKeyboardPersistingTaps } from '../../../platform/keyboard-persisting-taps' +import { useKeyboardPersistingTaps } from '../../../platform/keyboard/keyboard-persisting-taps' import { colors } from '../../../theme/mobile-theme' import { useMobileColorScheme } from '../../../theme/use-mobile-color-scheme' import type { MobileNativeChatSendIntent } from './mobile-native-chat-send' diff --git a/mobile/src/session/native-chat/composer/mobile-native-chat-composer-paste.ts b/mobile/src/session/native-chat/composer/mobile-native-chat-composer-paste.ts index f4708ea1c3a..ff4d644b36f 100644 --- a/mobile/src/session/native-chat/composer/mobile-native-chat-composer-paste.ts +++ b/mobile/src/session/native-chat/composer/mobile-native-chat-composer-paste.ts @@ -4,8 +4,8 @@ import { type ClipboardEventDataLike } from '../../../../../src/shared/native-chat/input/native-chat-clipboard-event' import { isNativeChatLongPaste } from '../../../../../src/shared/native-chat/input/native-chat-long-paste' -import type { ClipboardReader } from '../../../platform/clipboard' -import type { PickedMobileImage } from '../../../platform/media-picker-contract' +import type { ClipboardReader } from '../../../platform/clipboard/clipboard' +import type { PickedMobileImage } from '../../../platform/media-picking/media-picker-contract' import { encodeBase64Bytes } from '../../../transport/encryption/base64-byte-codec' /** One paste's image: the event's file, or null when WebKit left the bytes out and the shell clipboard holds them. */ diff --git a/mobile/src/session/native-chat/composer/mobile-native-chat-file-attachment.ts b/mobile/src/session/native-chat/composer/mobile-native-chat-file-attachment.ts index 261aff9b11e..fe7cb9e6520 100644 --- a/mobile/src/session/native-chat/composer/mobile-native-chat-file-attachment.ts +++ b/mobile/src/session/native-chat/composer/mobile-native-chat-file-attachment.ts @@ -3,7 +3,7 @@ import { chatTextAttachmentName } from '../../../../../src/shared/native-chat-te import { MobileFileTooLargeError, type PickedMobileFile -} from '../../../platform/media-picker-contract' +} from '../../../platform/media-picking/media-picker-contract' import type { AgentSessionAttachmentUploadCommitResult } from '../../../../../src/shared/agent-session/agent-session-attachments' export type PendingNativeChatFile = { diff --git a/mobile/src/session/native-chat/composer/mobile-native-chat-image-attachment.ts b/mobile/src/session/native-chat/composer/mobile-native-chat-image-attachment.ts index 31fd7ceaed1..15d699faf9b 100644 --- a/mobile/src/session/native-chat/composer/mobile-native-chat-image-attachment.ts +++ b/mobile/src/session/native-chat/composer/mobile-native-chat-image-attachment.ts @@ -1,7 +1,10 @@ import { saveMobileClipboardImageAsTempFile } from '../../../files/attachments/mobile-clipboard-image' import type { MobileClipboardImageRpcSender } from '../../../files/attachments/mobile-clipboard-image-operations' // Keep picker dependencies type-only so this upload module stays free of Expo/React Native at runtime. -import type { MobileImageSource, PickedMobileImage } from '../../../platform/media-picker-contract' +import type { + MobileImageSource, + PickedMobileImage +} from '../../../platform/media-picking/media-picker-contract' // path belongs to the host for sending; previewUri belongs to the phone for display. export type PendingNativeChatImage = { diff --git a/mobile/src/session/native-chat/composer/use-mobile-native-chat-file-attachments.ts b/mobile/src/session/native-chat/composer/use-mobile-native-chat-file-attachments.ts index 4a2db329673..49eee09a2d3 100644 --- a/mobile/src/session/native-chat/composer/use-mobile-native-chat-file-attachments.ts +++ b/mobile/src/session/native-chat/composer/use-mobile-native-chat-file-attachments.ts @@ -7,8 +7,8 @@ import { } from './mobile-native-chat-draft-inputs' import { useCallback } from 'react' import type { RpcClient } from '../../../transport/rpc/rpc-client-contract' -import { useMediaPicker } from '../../../platform/media-picker' -import type { PickedMobileFile } from '../../../platform/media-picker-contract' +import { useMediaPicker } from '../../../platform/media-picking/media-picker' +import type { PickedMobileFile } from '../../../platform/media-picking/media-picker-contract' import { encodeBase64Bytes } from '../../../transport/encryption/base64-byte-codec' import { agentSessionPastedTextName } from '../../../../../src/shared/agent-session/agent-session-attachments' import { uploadMobileAgentSessionAttachment } from '../../../files/attachments/mobile-agent-session-attachment-upload' diff --git a/mobile/src/session/native-chat/composer/use-mobile-native-chat-image-attachments.ts b/mobile/src/session/native-chat/composer/use-mobile-native-chat-image-attachments.ts index c5533f5492d..a6d88dae41b 100644 --- a/mobile/src/session/native-chat/composer/use-mobile-native-chat-image-attachments.ts +++ b/mobile/src/session/native-chat/composer/use-mobile-native-chat-image-attachments.ts @@ -10,7 +10,7 @@ import { import { useCallback, useMemo } from 'react' import type { RpcClient } from '../../../transport/rpc/rpc-client-contract' import type { ConnectionState } from '../../../transport/connection/connection-state' -import type { MobileImageSource } from '../../../platform/media-picker-contract' +import type { MobileImageSource } from '../../../platform/media-picking/media-picker-contract' import type { PendingNativeChatImage } from './mobile-native-chat-image-attachment' import type { MobilePastedImages } from './mobile-native-chat-composer-paste' import { diff --git a/mobile/src/session/native-chat/composer/use-mobile-native-chat-image-upload.ts b/mobile/src/session/native-chat/composer/use-mobile-native-chat-image-upload.ts index 9e3fd851eef..8c1fe664371 100644 --- a/mobile/src/session/native-chat/composer/use-mobile-native-chat-image-upload.ts +++ b/mobile/src/session/native-chat/composer/use-mobile-native-chat-image-upload.ts @@ -8,11 +8,11 @@ import { translate } from '../../../i18n/mobile-i18n' import { CLIPBOARD_IMAGE_TOO_LARGE_ERROR } from '../../../../../src/shared/clipboard-image' import type { RpcClient } from '../../../transport/rpc/rpc-client-contract' import type { ConnectionState } from '../../../transport/connection/connection-state' -import { useMediaPicker } from '../../../platform/media-picker' +import { useMediaPicker } from '../../../platform/media-picking/media-picker' import { ImageLibraryPermissionError, type MobileImageSource -} from '../../../platform/media-picker-contract' +} from '../../../platform/media-picking/media-picker-contract' import { uploadMobileNativeChatImages, type PendingNativeChatImage @@ -21,7 +21,7 @@ import { readMobilePastedImages, type MobilePastedImages } from './mobile-native-chat-composer-paste' -import { useClipboardReader } from '../../../platform/clipboard' +import { useClipboardReader } from '../../../platform/clipboard/clipboard' import { fitMobileAgentImage } from '../../../files/attachments/mobile-agent-image-fit' type CurrentRef = { readonly current: T } diff --git a/mobile/src/session/native-chat/composer/use-mobile-native-chat-steer-chord-preview.web.ts b/mobile/src/session/native-chat/composer/use-mobile-native-chat-steer-chord-preview.web.ts index 57fb8783602..635b7b3004c 100644 --- a/mobile/src/session/native-chat/composer/use-mobile-native-chat-steer-chord-preview.web.ts +++ b/mobile/src/session/native-chat/composer/use-mobile-native-chat-steer-chord-preview.web.ts @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react' -import { primaryModifierKey } from '../../../platform/primary-modifier-key' +import { primaryModifierKey } from '../../../platform/keyboard/primary-modifier-key' import { createNativeChatSteerChordPreviewHook } from '../../../../../src/shared/native-chat/input/use-native-chat-steer-chord-preview' import type { MobileNativeChatSteerChordPreviewInput } from './use-mobile-native-chat-steer-chord-preview' diff --git a/mobile/src/session/native-chat/input/MobileNativeChatPromptCard.tsx b/mobile/src/session/native-chat/input/MobileNativeChatPromptCard.tsx index 794f8f4287a..23827476e79 100644 --- a/mobile/src/session/native-chat/input/MobileNativeChatPromptCard.tsx +++ b/mobile/src/session/native-chat/input/MobileNativeChatPromptCard.tsx @@ -10,7 +10,7 @@ import { type LayoutChangeEvent } from 'react-native' import { SHEET_CURVE, SHEET_HEIGHT_DURATION_MS } from '../../../components/sheet-motion-values' -import { useReducedMotionEnabled } from '../../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../../platform/accessibility/use-reduced-motion' import { colors, pressedStyle, radii, spacing, typography } from '../../../theme/mobile-theme' import { themedStyleSheet } from '../../../theme/themed-styles' import { useMobileColorScheme } from '../../../theme/use-mobile-color-scheme' diff --git a/mobile/src/session/native-chat/input/mobile-native-chat-control-swap.tsx b/mobile/src/session/native-chat/input/mobile-native-chat-control-swap.tsx index 4cd554e5c88..a7d1cd1fc87 100644 --- a/mobile/src/session/native-chat/input/mobile-native-chat-control-swap.tsx +++ b/mobile/src/session/native-chat/input/mobile-native-chat-control-swap.tsx @@ -1,7 +1,7 @@ import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from 'react' import { Animated, Easing, Platform, StyleSheet, View } from 'react-native' import { SHEET_CURVE } from '../../../components/sheet-motion-values' -import { useReducedMotionEnabled } from '../../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../../platform/accessibility/use-reduced-motion' const SWAP_MS = 150 const REDUCED_SWAP_MS = 100 diff --git a/mobile/src/session/native-chat/input/use-mobile-native-chat-reply-scroll.ts b/mobile/src/session/native-chat/input/use-mobile-native-chat-reply-scroll.ts index b8434307178..bf70d53ada8 100644 --- a/mobile/src/session/native-chat/input/use-mobile-native-chat-reply-scroll.ts +++ b/mobile/src/session/native-chat/input/use-mobile-native-chat-reply-scroll.ts @@ -1,6 +1,6 @@ import { useRef } from 'react' import type { LayoutChangeEvent, ScrollViewInstance } from 'react-native' -import type { MobileTextInputHandle } from '../../../platform/text-input-handle' +import type { MobileTextInputHandle } from '../../../platform/text-input/text-input-handle' // Keep a focused reply in view when its card shrinks above the keyboard. export function useMobileNativeChatReplyScroll() { diff --git a/mobile/src/session/native-chat/transcript/MobileLinkActionSheet.tsx b/mobile/src/session/native-chat/transcript/MobileLinkActionSheet.tsx index 6ab27dc905c..6650453c091 100644 --- a/mobile/src/session/native-chat/transcript/MobileLinkActionSheet.tsx +++ b/mobile/src/session/native-chat/transcript/MobileLinkActionSheet.tsx @@ -1,9 +1,9 @@ import { useState } from 'react' import { Copy, ExternalLink, Globe } from 'lucide-react-native' import { ActionSheetModal, type ActionSheetAction } from '../../../components/ActionSheetModal' -import { openExternalLink } from '../../../platform/external-link' -import { useClipboardWriter } from '../../../platform/clipboard' -import { triggerError, triggerSuccess } from '../../../platform/haptics' +import { openExternalLink } from '../../../platform/external-links/external-link' +import { useClipboardWriter } from '../../../platform/clipboard/clipboard' +import { triggerError, triggerSuccess } from '../../../platform/haptics/haptics' import { useMobileTranslation } from '../../../i18n/use-mobile-translation' // Choices for a tapped chat link: Orca's browser on the host, the phone's browser, or the clipboard. diff --git a/mobile/src/session/native-chat/transcript/MobileNativeChatMessageActionsSheet.tsx b/mobile/src/session/native-chat/transcript/MobileNativeChatMessageActionsSheet.tsx index 56289faf059..4498fdfe8f3 100644 --- a/mobile/src/session/native-chat/transcript/MobileNativeChatMessageActionsSheet.tsx +++ b/mobile/src/session/native-chat/transcript/MobileNativeChatMessageActionsSheet.tsx @@ -4,7 +4,7 @@ import { Copy, TextSelect, X } from 'lucide-react-native' import type { NativeChatMessage } from '../../../../../src/shared/native-chat/native-chat-types' import { ActionSheetContent } from '../../../components/ActionSheetModal' import { BottomDrawer } from '../../../components/bottom-drawer/BottomDrawer' -import { useClipboardWriter } from '../../../platform/clipboard' +import { useClipboardWriter } from '../../../platform/clipboard/clipboard' import { useWindowSafeAreaInsets } from '../../../layout/side-safe-area' import { colors, spacing, typography, pressedStyle } from '../../../theme/mobile-theme' import { themedStyleSheet } from '../../../theme/themed-styles' diff --git a/mobile/src/session/native-chat/transcript/MobileNativeChatRowOverlaysHost.test.tsx b/mobile/src/session/native-chat/transcript/MobileNativeChatRowOverlaysHost.test.tsx index c6a5a70f8f6..4744881acfb 100644 --- a/mobile/src/session/native-chat/transcript/MobileNativeChatRowOverlaysHost.test.tsx +++ b/mobile/src/session/native-chat/transcript/MobileNativeChatRowOverlaysHost.test.tsx @@ -24,7 +24,7 @@ vi.mock('react-native-safe-area-context', () => ({ useSafeAreaInsets: () => ({ top: 0, bottom: 0, left: 0, right: 0 }) })) vi.mock('lucide-react-native', () => ({ X: 'X' })) -vi.mock('../../../platform/haptics', () => ({ triggerMediumImpact: vi.fn() })) +vi.mock('../../../platform/haptics/haptics', () => ({ triggerMediumImpact: vi.fn() })) vi.mock('./MobileNativeChatRewindDialog', () => ({ MobileNativeChatRewindDialog: () => null })) vi.mock('./MobileNativeChatMessageActionsSheet', () => ({ MobileNativeChatMessageActionsSheet: 'MessageActionsSheet' diff --git a/mobile/src/session/native-chat/transcript/MobileNativeChatRowOverlaysHost.tsx b/mobile/src/session/native-chat/transcript/MobileNativeChatRowOverlaysHost.tsx index 49f233a0224..feed20159c7 100644 --- a/mobile/src/session/native-chat/transcript/MobileNativeChatRowOverlaysHost.tsx +++ b/mobile/src/session/native-chat/transcript/MobileNativeChatRowOverlaysHost.tsx @@ -1,7 +1,7 @@ import { useCallback, useMemo } from 'react' import type { AgentSessionRewindSurface } from '../../../../../src/shared/agent-session/lifecycle/agent-session-rewind-request' import type { NativeChatMessage } from '../../../../../src/shared/native-chat/native-chat-types' -import { triggerMediumImpact } from '../../../platform/haptics' +import { triggerMediumImpact } from '../../../platform/haptics/haptics' import { MobileNativeChatImageViewer } from '../visual/MobileNativeChatImageViewer' import { MobileImageViewerActions } from '../visual/MobileImageViewerActions' import { MobileNativeChatMessageActionsSheet } from './MobileNativeChatMessageActionsSheet' diff --git a/mobile/src/session/native-chat/transcript/MobileNativeChatToolRun.test.tsx b/mobile/src/session/native-chat/transcript/MobileNativeChatToolRun.test.tsx index 5bd9a52d583..2971bcbcc83 100644 --- a/mobile/src/session/native-chat/transcript/MobileNativeChatToolRun.test.tsx +++ b/mobile/src/session/native-chat/transcript/MobileNativeChatToolRun.test.tsx @@ -63,7 +63,7 @@ vi.mock('lucide-react-native', () => ({ vi.mock('../../../components/MobileSourceCopyButton', () => ({ MobileSourceCopyButton: 'CopyButton' })) -vi.mock('../../../platform/external-link', () => ({ openExternalLink: vi.fn() })) +vi.mock('../../../platform/external-links/external-link', () => ({ openExternalLink: vi.fn() })) vi.mock('../../../components/inline-text-selection', () => ({ INLINE_TEXT_SELECTION: false })) import { ToolRun } from './MobileNativeChatToolRun' import { @@ -79,12 +79,6 @@ describe('ToolRun', () => { renderer = null }) - function openRun(tree: ReactTestRenderer): void { - act(() => { - tree.root.findAll((node) => String(node.type) === 'Pressable')[0]!.props.onPress() - }) - } - const textIn = (node: ReactTestInstance): string[] => node .findAll((node) => String(node.type) === 'Text') @@ -138,62 +132,5 @@ describe('ToolRun', () => { show({ mounted: true }) expect(textIn(renderer!.root)).toContain('a.ts') }) - - const box = (): ReactTestInstance => - renderer!.root.findAll((node) => String(node.type) === 'ScrollView')[0]! - const scrolled = (top: number) => ({ - nativeEvent: { - contentOffset: { y: top }, - contentSize: { height: 1_000 }, - layoutMeasurement: { height: 300 } - } - }) - - it('comes back where the reader left its box after the row is recycled', () => { - show({ mounted: true }) - press('ls') - act(() => { - box().props.onScroll(scrolled(120)) - }) - show({ mounted: false }) - show({ mounted: true }) - scrolls.to.mockClear() - act(() => { - box().props.onContentSizeChange() - }) - expect(scrolls.to).toHaveBeenCalledWith({ y: 120, animated: false }) - }) - }) - - it('does not save a follow for a later row opening when new work left the height unchanged', () => { - const live = (blocks: NativeChatBlock[]) => - createElement(ToolRun, { - blocks, - activeTurnIsWorking: true, - trailing: true - }) - const call: NativeChatBlock = { type: 'tool-call', name: 'Bash', input: { command: 'ls' } } - act(() => { - renderer = create(live([call])) - }) - openRun(renderer!) - const box = () => renderer!.root.findAll((node) => String(node.type) === 'ScrollView')[0]! - act(() => { - box().props.onContentSizeChange() - }) - act(() => { - renderer!.update(live([call, { type: 'tool-result', output: 'a.ts' }])) - }) - scrolls.toEnd.mockClear() - act(() => { - renderer!.root - .findAll((node) => String(node.type) === 'Pressable') - .findLast((node) => textIn(node).includes('ls'))! - .props.onPress() - }) - act(() => { - box().props.onContentSizeChange() - }) - expect(scrolls.toEnd).not.toHaveBeenCalled() }) }) diff --git a/mobile/src/session/native-chat/transcript/mobile-native-chat-reply-reveals.ts b/mobile/src/session/native-chat/transcript/mobile-native-chat-reply-reveals.ts index 793f68d1995..e30f74ed60b 100644 --- a/mobile/src/session/native-chat/transcript/mobile-native-chat-reply-reveals.ts +++ b/mobile/src/session/native-chat/transcript/mobile-native-chat-reply-reveals.ts @@ -6,7 +6,7 @@ import { trackNativeChatReplyRows, type NativeChatReplyReveals } from '../../../../../src/shared/native-chat/transcript/native-chat-reply-reveals' -import { useReducedMotionEnabled } from '../../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../../platform/accessibility/use-reduced-motion' // Retain reveal state above list remounts/view toggles so replies resume instead of replaying. export type MobileNativeChatReplyReveals = NativeChatReplyReveals & { reducedMotion: boolean } diff --git a/mobile/src/session/native-chat/visual/MobileImageViewerActions.tsx b/mobile/src/session/native-chat/visual/MobileImageViewerActions.tsx index 388794ede2e..6a2712ac632 100644 --- a/mobile/src/session/native-chat/visual/MobileImageViewerActions.tsx +++ b/mobile/src/session/native-chat/visual/MobileImageViewerActions.tsx @@ -9,8 +9,8 @@ import { BRIDGE_IMAGE_OPERATIONS, type BridgeImageOperation } from '../../../mobile-web-shell/bridge/bridge-image-verbs' -import { useImageActions } from '../../../platform/image-actions' -import { ImageActionRefusedError } from '../../../platform/image-actions-contract' +import { useImageActions } from '../../../platform/image-actions/image-actions' +import { ImageActionRefusedError } from '../../../platform/image-actions/image-actions-contract' import { colors, feedback } from '../../../theme/mobile-theme' import type { ImageViewerActionsProps } from './MobileNativeChatImageViewer' diff --git a/mobile/src/session/native-chat/visual/MobileNativeChatImageViewer.tsx b/mobile/src/session/native-chat/visual/MobileNativeChatImageViewer.tsx index 89a6486544d..ca0f1eee382 100644 --- a/mobile/src/session/native-chat/visual/MobileNativeChatImageViewer.tsx +++ b/mobile/src/session/native-chat/visual/MobileNativeChatImageViewer.tsx @@ -17,7 +17,7 @@ import { useMobileTranslation } from '../../../i18n/use-mobile-translation' import type { PanZoomSize } from '../../../../../src/shared/pan-zoom-view' import { MobilePanZoomImage } from '../../../components/MobilePanZoomImage' import { REDUCED_MOTION_FADE_MS, SHEET_CURVE } from '../../../components/sheet-motion-values' -import { useReducedMotionEnabled } from '../../../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../../../platform/accessibility/use-reduced-motion' const VIEWER_EASING = Easing.bezier(...SHEET_CURVE) const VIEWER_OPEN_MS = 250 diff --git a/mobile/src/session/tabs/MobileSessionTabBar.tsx b/mobile/src/session/tabs/MobileSessionTabBar.tsx index bd630336b30..324de33fd37 100644 --- a/mobile/src/session/tabs/MobileSessionTabBar.tsx +++ b/mobile/src/session/tabs/MobileSessionTabBar.tsx @@ -10,14 +10,14 @@ import Animated, { withSequence, withTiming } from 'react-native-reanimated' -import { triggerMediumImpact, triggerSelection } from '../../platform/haptics' +import { triggerMediumImpact, triggerSelection } from '../../platform/haptics/haptics' import { useMobileTranslation } from '../../i18n/use-mobile-translation' import { AgentStateDot } from '../../components/AgentStateDot' import { ArrivalFade } from '../../components/ArrivalFade' import { useDrawerReducedMotion } from '../../components/bottom-drawer/bottom-drawer-constants' import { SHEET_CURVE } from '../../components/sheet-motion-values' -import { useReducedMotionEnabled } from '../../platform/use-reduced-motion' -import { useKeyboardPersistingTaps } from '../../platform/keyboard-persisting-taps' +import { useReducedMotionEnabled } from '../../platform/accessibility/use-reduced-motion' +import { useKeyboardPersistingTaps } from '../../platform/keyboard/keyboard-persisting-taps' import { useNow } from '../../hooks/use-now' import { agentStateLabel, type AgentDotState } from '../../worktree/agent-row-display' import { colors, feedback, radii, spacing } from '../../theme/mobile-theme' diff --git a/mobile/src/session/tabs/use-mobile-session-tab-switching.ts b/mobile/src/session/tabs/use-mobile-session-tab-switching.ts index 56472266ef8..aab5fc76f65 100644 --- a/mobile/src/session/tabs/use-mobile-session-tab-switching.ts +++ b/mobile/src/session/tabs/use-mobile-session-tab-switching.ts @@ -1,7 +1,7 @@ import { keepsTerminalPaneVisible } from '../../terminal/session/mobile-terminal-pane-layout' import { useCallback } from 'react' import { STARTUP_CWD_FALLBACK_SENTENCE } from '../../../../src/shared/startup-cwd-fallback' -import { triggerSelection } from '../../platform/haptics' +import { triggerSelection } from '../../platform/haptics/haptics' import type { RpcResponse } from '../../transport/rpc/rpc-wire-types' import { isCachedActiveDocumentRetap } from '../../files/session-documents/mobile-session-active-document-retap' import { activateMobileSessionTab } from './mobile-session-tab-activation' diff --git a/mobile/src/session/use-mobile-session-dictation.ts b/mobile/src/session/use-mobile-session-dictation.ts index 4bbdfad37d7..578646a336d 100644 --- a/mobile/src/session/use-mobile-session-dictation.ts +++ b/mobile/src/session/use-mobile-session-dictation.ts @@ -9,8 +9,8 @@ import { import { useFocusEffect, useNavigation } from 'expo-router' import { HOST_SESSION_ROUTE } from '../navigation/host-stack-navigation' import { useMobileSessionDictationSetup } from './use-mobile-session-dictation-setup' -import { triggerError } from '../platform/haptics' -import { useClipboardWriter } from '../platform/clipboard' +import { triggerError } from '../platform/haptics/haptics' +import { useClipboardWriter } from '../platform/clipboard/clipboard' import { useMobileDictationDestination, useMobileDictationPreviews diff --git a/mobile/src/session/use-mobile-session-lifecycle.ts b/mobile/src/session/use-mobile-session-lifecycle.ts index a86475eeda6..827381e268f 100644 --- a/mobile/src/session/use-mobile-session-lifecycle.ts +++ b/mobile/src/session/use-mobile-session-lifecycle.ts @@ -1,4 +1,4 @@ -import { useShellStorage } from '../platform/shell-storage' +import { useShellStorage } from '../platform/shell-storage/shell-storage' import { useDirectionPadAvailable } from '../layout/use-shell-side-rail' import { settleSessionPreferenceRead } from './session-preference-read' import { useEffect, useRef, useCallback, useState } from 'react' diff --git a/mobile/src/session/use-mobile-session-preference-focus.ts b/mobile/src/session/use-mobile-session-preference-focus.ts index c58786ee2b1..367a2707832 100644 --- a/mobile/src/session/use-mobile-session-preference-focus.ts +++ b/mobile/src/session/use-mobile-session-preference-focus.ts @@ -1,4 +1,4 @@ -import { useShellStorage } from '../platform/shell-storage' +import { useShellStorage } from '../platform/shell-storage/shell-storage' import { settleSessionPreferenceRead } from './session-preference-read' import { useCallback, useState } from 'react' import { useFocusEffect } from 'expo-router' diff --git a/mobile/src/session/use-mobile-session-presentation.ts b/mobile/src/session/use-mobile-session-presentation.ts index bb0c0901ab6..ec60508398d 100644 --- a/mobile/src/session/use-mobile-session-presentation.ts +++ b/mobile/src/session/use-mobile-session-presentation.ts @@ -9,7 +9,7 @@ import { useMobileTranslation } from '../i18n/use-mobile-translation' import { useInitialSessionTerminalAutoCreate } from '../terminal/session/use-initial-session-terminal-autocreate' import { MOBILE_SESSION_STATUS_LABELS } from '../transport/connection/mobile-session-connection-copy' import type { MobileSessionBulkCloseModel } from './tabs/use-mobile-session-bulk-close' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { useDirectionPadAvailable, useShellSideRail } from '../layout/use-shell-side-rail' import { useCalmConnectionState } from '../transport/connection/calm-connection-state' diff --git a/mobile/src/settings/voice-transcribe-on-section.tsx b/mobile/src/settings/voice-transcribe-on-section.tsx index d12f0073842..695a122e19b 100644 --- a/mobile/src/settings/voice-transcribe-on-section.tsx +++ b/mobile/src/settings/voice-transcribe-on-section.tsx @@ -12,8 +12,8 @@ import type { Translate } from '../i18n/mobile-i18n' import { getUiIntlLocale } from '../i18n/ui-intl-locale' import { useMobileTranslation } from '../i18n/use-mobile-translation' import type { DictationEnginePreference } from '../mobile-web-shell/bridge/bridge-speech-verbs' -import { isSpeechBusyError } from '../platform/native-speech' -import { onDeviceSpeechLocale } from '../platform/on-device-speech-locale' +import { isSpeechBusyError } from '../platform/speech/native-speech' +import { onDeviceSpeechLocale } from '../platform/speech/on-device-speech-locale' import { DEFAULT_DICTATION_ENGINE_PREFERENCE, loadDictationEnginePreference, diff --git a/mobile/src/source-control/MobileCommitDetailPage.tsx b/mobile/src/source-control/MobileCommitDetailPage.tsx index 043836947d0..3570ef1d95a 100644 --- a/mobile/src/source-control/MobileCommitDetailPage.tsx +++ b/mobile/src/source-control/MobileCommitDetailPage.tsx @@ -18,9 +18,9 @@ import { themedStyleSheet } from '../theme/themed-styles' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../i18n/use-mobile-translation' import { translate } from '../i18n/mobile-i18n' -import { useClipboardWriter } from '../platform/clipboard' -import { openExternalLink } from '../platform/external-link' -import { triggerSuccess } from '../platform/haptics' +import { useClipboardWriter } from '../platform/clipboard/clipboard' +import { openExternalLink } from '../platform/external-links/external-link' +import { triggerSuccess } from '../platform/haptics/haptics' import type { RpcClient } from '../transport/rpc/rpc-client-contract' import type { ConnectionState } from '../transport/connection/connection-state' import { STATUS_LABELS } from '../../../src/shared/git/status/git-status-labels' diff --git a/mobile/src/source-control/MobileCommitsScreen.test.tsx b/mobile/src/source-control/MobileCommitsScreen.test.tsx index 579ab21745b..36162642745 100644 --- a/mobile/src/source-control/MobileCommitsScreen.test.tsx +++ b/mobile/src/source-control/MobileCommitsScreen.test.tsx @@ -81,9 +81,11 @@ vi.mock('../components/list/ListGroup', async () => { SectionHeader: ({ title }: { title: string }) => h('Text', null, title) } }) -vi.mock('../platform/clipboard', () => ({ useClipboardWriter: () => ({ writeText: vi.fn() }) })) -vi.mock('../platform/external-link', () => ({ openExternalLink: vi.fn() })) -vi.mock('../platform/haptics', () => ({ triggerSuccess: vi.fn() })) +vi.mock('../platform/clipboard/clipboard', () => ({ + useClipboardWriter: () => ({ writeText: vi.fn() }) +})) +vi.mock('../platform/external-links/external-link', () => ({ openExternalLink: vi.fn() })) +vi.mock('../platform/haptics/haptics', () => ({ triggerSuccess: vi.fn() })) const transport = vi.hoisted( (): { forceReconnect: ((hostId: string) => Promise) | null } => ({ forceReconnect: () => Promise.resolve() diff --git a/mobile/src/source-control/MobileSourceControlContent.tsx b/mobile/src/source-control/MobileSourceControlContent.tsx index 70aed9111f6..36a901ee4a2 100644 --- a/mobile/src/source-control/MobileSourceControlContent.tsx +++ b/mobile/src/source-control/MobileSourceControlContent.tsx @@ -3,7 +3,7 @@ import { ActivityIndicator, Animated, Easing, Text, View } from 'react-native' import { colors, spacing } from '../theme/mobile-theme' import { useMobileColorScheme } from '../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../i18n/use-mobile-translation' -import { useReducedMotionEnabled } from '../platform/use-reduced-motion' +import { useReducedMotionEnabled } from '../platform/accessibility/use-reduced-motion' import { SHEET_CURVE } from '../components/sheet-motion-values' import { MobileSourceControlChangesHeader } from './MobileSourceControlChangesHeader' import { MobileCommitFailurePanel } from './MobileCommitFailurePanel' diff --git a/mobile/src/source-control/folder/use-folder-create-flow.test.tsx b/mobile/src/source-control/folder/use-folder-create-flow.test.tsx index 91ec50c2ecd..94ddbd94cc8 100644 --- a/mobile/src/source-control/folder/use-folder-create-flow.test.tsx +++ b/mobile/src/source-control/folder/use-folder-create-flow.test.tsx @@ -38,11 +38,11 @@ vi.mock('../../components/TransientToast', () => ({ showToast: (text: string) => seen.toasts.push(text) }) })) -vi.mock('../../platform/haptics', () => ({ +vi.mock('../../platform/haptics/haptics', () => ({ triggerSuccess: () => (seen.haptics += 1), triggerError: () => (seen.haptics += 1) })) -vi.mock('../../platform/external-link', () => ({ openExternalLink: () => {} })) +vi.mock('../../platform/external-links/external-link', () => ({ openExternalLink: () => {} })) vi.mock('../../transport/connection/host-client-hooks', () => ({ useHostClient: () => ({ client: null, state: 'connected' }) })) diff --git a/mobile/src/source-control/folder/use-folder-create-flow.tsx b/mobile/src/source-control/folder/use-folder-create-flow.tsx index 899a510ea38..c2e4651e615 100644 --- a/mobile/src/source-control/folder/use-folder-create-flow.tsx +++ b/mobile/src/source-control/folder/use-folder-create-flow.tsx @@ -6,8 +6,8 @@ import { NewWorktreeModalController } from '../../components/NewWorktreeModalCon import type { MobileWorkspaceRepo } from '../../components/new-worktree-modal-types' import { TransientToast, useTransientToast } from '../../components/TransientToast' import { useMobileTranslation } from '../../i18n/use-mobile-translation' -import { openExternalLink } from '../../platform/external-link' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { openExternalLink } from '../../platform/external-links/external-link' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import { useHostClient } from '../../transport/connection/host-client-hooks' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import type { FolderListRow } from './mobile-folder-attached-worktrees' diff --git a/mobile/src/source-control/mobile-source-control-oversized-stage-all.test.tsx b/mobile/src/source-control/mobile-source-control-oversized-stage-all.test.tsx deleted file mode 100644 index 680730b022a..00000000000 --- a/mobile/src/source-control/mobile-source-control-oversized-stage-all.test.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { createElement, useRef } from 'react' -import { act, create } from 'react-test-renderer' -import { describe, expect, it, vi } from 'vitest' -import { createFakeBridgePortPair } from '../mobile-web-shell/bridge/bridge-port-pair-test-harness' -// Mock native Flow/haptics imports; retain the real bridge-to-error path. -vi.mock('react-native', () => ({ - Platform: { OS: 'ios', select: (options: Record) => options.ios }, - StyleSheet: { create: (value: unknown) => value, hairlineWidth: 1 } -})) -// Mock expo-router through the native routing seam to obtain a checked RouteHandoff. -vi.mock('expo-router', () => ({ - useRouter: () => ({ - back: () => undefined, - push: () => undefined, - replace: () => undefined, - navigate: () => undefined, - dismissTo: () => undefined, - prefetch: () => undefined, - canGoBack: () => false, - setParams: () => undefined - }) -})) -vi.mock('../platform/haptics', () => ({ - triggerSuccess: () => undefined, - triggerError: () => undefined, - triggerSelection: () => undefined, - triggerWarning: () => undefined, - triggerImpact: () => undefined -})) - -import { useRouteHandoff } from '../navigation/route-handoff' -import { useMobileGitRequests } from './use-mobile-git-requests' -import { useMobileSourceControlRunners } from './use-mobile-source-control-runners' - -// Exercise the real oversized-frame refusal through the screen’s runner: the old shell silently dropped it and left busy state forever. -// Checking only the bridge or only a hand-thrown runner error would miss whether the screen actually displays this refusal. - -const PATH_COUNT = 12_000 -const STAGEABLE_PATHS = Array.from( - { length: PATH_COUNT }, - (_, index) => `packages/app/src/features/some/deeply/nested/module/file-${index}-abcdefghij.ts` -) - -function idleRunnerParams() { - return { - hostId: 'host-a', - worktreeId: 'wt-1', - status: null, - branchLabel: 'main', - commitMessage: '', - stagedEntries: [], - generatingMessage: false, - unstageablePaths: [], - sendCommitRequest: vi.fn(), - runGitSyncSteps: vi.fn(), - loadStatus: vi.fn().mockResolvedValue(true), - setBusyAction: vi.fn(), - setCommitMessage: vi.fn(), - setGeneratingMessage: vi.fn(), - setShowActionSheet: vi.fn(), - setLocalBranches: vi.fn(), - setShowBranchPicker: vi.fn(), - setCreatedPrUrl: vi.fn(), - setCreatedPrWarning: vi.fn(), - recordCommitFailure: vi.fn() - } -} - -describe('stage-all over the outbound frame cap', () => { - it('reports the oversized action and suggests fewer files, then stops being busy', async () => { - const pair = createFakeBridgePortPair() - await pair.flush() - const setActionError = vi.fn() - const setBusyAction = vi.fn() - - // Use a holder: TypeScript cannot prove Probe ran and would keep a local binding narrowed to null. - const captured: { stageAll: (() => Promise) | null } = { stageAll: null } - function Probe(): null { - const router = useRouteHandoff() - const mountedRef = useRef(true) - const busyActionRef = useRef(null) - const { sendGitRequest } = useMobileGitRequests({ - client: pair.client, - connState: 'connected', - worktreeId: 'wt-1' - }) - captured.stageAll = useMobileSourceControlRunners({ - ...idleRunnerParams(), - client: pair.client, - router, - stageablePaths: STAGEABLE_PATHS, - sendGitRequest, - mountedRef, - busyActionRef, - setBusyAction, - setActionError - }).stageAll - return null - } - await act(async () => { - create(createElement(Probe)) - }) - const run = captured.stageAll - if (!run) { - throw new Error('the runners hook never rendered') - } - await act(async () => { - await run() - }) - - const message = setActionError.mock.calls.map(([value]) => value).findLast(Boolean) - expect(message).toBeTruthy() - expect(message).toMatch(/[a-z]{4}/) - expect(message).not.toMatch(/^bridge_/) - expect(message).toContain('fewer files') - expect(setBusyAction.mock.calls.map(([value]) => value)).toEqual(['stage-all', null]) - }) -}) diff --git a/mobile/src/source-control/review/use-mobile-diff-review-comment-actions.ts b/mobile/src/source-control/review/use-mobile-diff-review-comment-actions.ts index a9d92e9dfd2..fc3e699a3d8 100644 --- a/mobile/src/source-control/review/use-mobile-diff-review-comment-actions.ts +++ b/mobile/src/source-control/review/use-mobile-diff-review-comment-actions.ts @@ -1,7 +1,7 @@ import { useCallback, useRef, type Dispatch, type SetStateAction } from 'react' import { translate } from '../../i18n/mobile-i18n' import type { DiffComment, MobileDiffReviewState } from '../../../../src/shared/diff-comment-types' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import type { ConnectionState } from '../../transport/connection/connection-state' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import { interpretOrThrowRefusalMessage } from '../../transport/rpc/rpc-refusal-message' diff --git a/mobile/src/source-control/review/use-mobile-diff-review-git-actions.ts b/mobile/src/source-control/review/use-mobile-diff-review-git-actions.ts index 4096bdeec05..23f8deb4a01 100644 --- a/mobile/src/source-control/review/use-mobile-diff-review-git-actions.ts +++ b/mobile/src/source-control/review/use-mobile-diff-review-git-actions.ts @@ -8,7 +8,7 @@ import { reviewGitBulkUnstage } from './mobile-diff-review-git-operations' import { getUnstageablePaths } from '../mobile-git-status' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import type { MobileDiffReviewQueueItem } from './mobile-diff-review-queue' import type { GitMutationMethod } from './mobile-diff-review-screen-model' diff --git a/mobile/src/source-control/review/use-mobile-diff-review-github.ts b/mobile/src/source-control/review/use-mobile-diff-review-github.ts index 7b3ad054e98..a21650f7e12 100644 --- a/mobile/src/source-control/review/use-mobile-diff-review-github.ts +++ b/mobile/src/source-control/review/use-mobile-diff-review-github.ts @@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react' import { translate } from '../../i18n/mobile-i18n' import type { ConnectionState } from '../../transport/connection/connection-state' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import { fetchSetPRFileViewed } from './github-pr-review-line-mutations' import { useHostMutation } from '../../query/host-mutation' import { prCommentTarget, prCommentWrite } from './pr-comment-writes' diff --git a/mobile/src/source-control/review/use-mobile-diff-review-send-actions.ts b/mobile/src/source-control/review/use-mobile-diff-review-send-actions.ts index 503eec21acc..d2317edbbcf 100644 --- a/mobile/src/source-control/review/use-mobile-diff-review-send-actions.ts +++ b/mobile/src/source-control/review/use-mobile-diff-review-send-actions.ts @@ -3,8 +3,8 @@ import { translate } from '../../i18n/mobile-i18n' import type { DiffComment, MobileDiffReviewState } from '../../../../src/shared/diff-comment-types' import type { ConnectionState } from '../../transport/connection/connection-state' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' -import { useClipboardWriter } from '../../platform/clipboard' -import { triggerSuccess } from '../../platform/haptics' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' +import { triggerSuccess } from '../../platform/haptics/haptics' import { formatDiffComments } from '../../../../src/shared/diff-comments-format' import { clearSentMobileDiffComments, markMobileDiffCommentsSent } from './mobile-diff-comment-edit' import { reviewTerminalListRead, reviewTerminalSendRun } from './mobile-review-terminal-operations' diff --git a/mobile/src/source-control/review/use-mobile-pr-ai-triage.ts b/mobile/src/source-control/review/use-mobile-pr-ai-triage.ts index 27b9135a907..74a89e7c149 100644 --- a/mobile/src/source-control/review/use-mobile-pr-ai-triage.ts +++ b/mobile/src/source-control/review/use-mobile-pr-ai-triage.ts @@ -1,7 +1,7 @@ import { useCallback, useRef, useState } from 'react' import type { ConnectionState } from '../../transport/connection/connection-state' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import { useMobileTranslation } from '../../i18n/use-mobile-translation' import { launchAgentWithPrompt, promptedLaunchNotice } from './pr-ai-triage-launch' import type { LaunchedAgentOpens } from '../../session/agent-launch/launched-agent-opener' diff --git a/mobile/src/source-control/review/use-mobile-pr-comment-actions.ts b/mobile/src/source-control/review/use-mobile-pr-comment-actions.ts index de7ca0658c3..ca7c5efd7c3 100644 --- a/mobile/src/source-control/review/use-mobile-pr-comment-actions.ts +++ b/mobile/src/source-control/review/use-mobile-pr-comment-actions.ts @@ -4,7 +4,7 @@ import type { ConnectionState } from '../../transport/connection/connection-stat import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import type { GitHubPrRepoSlug } from './github-pr-repo-slug' import { useHostMutation } from '../../query/host-mutation' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import { useMobileTranslation } from '../../i18n/use-mobile-translation' import { buildAddRootCommentParams, diff --git a/mobile/src/source-control/review/use-mobile-pr-text-action.ts b/mobile/src/source-control/review/use-mobile-pr-text-action.ts index c77b49bc46f..94b992bea8f 100644 --- a/mobile/src/source-control/review/use-mobile-pr-text-action.ts +++ b/mobile/src/source-control/review/use-mobile-pr-text-action.ts @@ -6,7 +6,7 @@ import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import type { GitHubPrRepoSlug } from './github-pr-repo-slug' import { defineHostMutation, useHostMutation } from '../../query/host-mutation' import { fetchUpdatePRBody, fetchUpdatePRTitle } from './github-pr-mutations' -import { triggerError, triggerSuccess } from '../../platform/haptics' +import { triggerError, triggerSuccess } from '../../platform/haptics/haptics' import { useMobileTranslation } from '../../i18n/use-mobile-translation' import { buildUpdatePRBodyParams, diff --git a/mobile/src/source-control/review/use-mobile-session-diff-comments.ts b/mobile/src/source-control/review/use-mobile-session-diff-comments.ts index 451d6c0a667..bcd6c9efbff 100644 --- a/mobile/src/source-control/review/use-mobile-session-diff-comments.ts +++ b/mobile/src/source-control/review/use-mobile-session-diff-comments.ts @@ -1,12 +1,12 @@ import { useEffect, useCallback, useMemo, useRef, useState } from 'react' import { formatDiffComments } from '../../../../src/shared/diff-comments-format' import { translate } from '../../i18n/mobile-i18n' -import { useClipboardWriter } from '../../platform/clipboard' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' import { interpretOrThrowRefusalMessage } from '../../transport/rpc/rpc-refusal-message' import type { SendRequestOptions } from '../../transport/rpc/unvalidated-rpc-request-port' import { sessionWorktreeRecordRead } from '../../worktree/session-worktree-record-operations' import { sessionWorktreeNotesWrite } from './mobile-review-notes-operations' -import { triggerSelection, triggerSuccess, triggerError } from '../../platform/haptics' +import { triggerSelection, triggerSuccess, triggerError } from '../../platform/haptics/haptics' import { addMobileDiffComment, normalizeMobileDiffComments, diff --git a/mobile/src/source-control/use-mobile-commit-failure-recovery.ts b/mobile/src/source-control/use-mobile-commit-failure-recovery.ts index 3defee675d8..045c78c8de9 100644 --- a/mobile/src/source-control/use-mobile-commit-failure-recovery.ts +++ b/mobile/src/source-control/use-mobile-commit-failure-recovery.ts @@ -1,7 +1,7 @@ import { useCallback, useMemo, useRef, useState } from 'react' import type { ConnectionState } from '../transport/connection/connection-state' import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import { triggerError, triggerSuccess } from '../platform/haptics' +import { triggerError, triggerSuccess } from '../platform/haptics/haptics' import { translate } from '../i18n/mobile-i18n' import { useHostProtocolGates } from '../components/host-protocol-gates-context' import { launchAgentWithPrompt, promptedLaunchNotice } from './review/pr-ai-triage-launch' diff --git a/mobile/src/source-control/use-mobile-commit-message-generation.test.ts b/mobile/src/source-control/use-mobile-commit-message-generation.test.ts index 6714b1255c7..480a48333b9 100644 --- a/mobile/src/source-control/use-mobile-commit-message-generation.test.ts +++ b/mobile/src/source-control/use-mobile-commit-message-generation.test.ts @@ -3,7 +3,7 @@ import { act, create, type ReactTestRenderer } from 'react-test-renderer' import { afterEach, describe, expect, it, vi } from 'vitest' import type { MobileGenerateCommitMessageResult } from './mobile-commit-message-ai' -vi.mock('../platform/haptics', () => ({ triggerError: vi.fn(), triggerSuccess: vi.fn() })) +vi.mock('../platform/haptics/haptics', () => ({ triggerError: vi.fn(), triggerSuccess: vi.fn() })) const reply = vi.hoisted(() => { const held: { resolve: ((result: MobileGenerateCommitMessageResult) => void) | null diff --git a/mobile/src/source-control/use-mobile-commit-message-generation.ts b/mobile/src/source-control/use-mobile-commit-message-generation.ts index c6e31b7c1c8..0f70a938a4d 100644 --- a/mobile/src/source-control/use-mobile-commit-message-generation.ts +++ b/mobile/src/source-control/use-mobile-commit-message-generation.ts @@ -7,7 +7,7 @@ import { type SetStateAction } from 'react' import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import { triggerError, triggerSuccess } from '../platform/haptics' +import { triggerError, triggerSuccess } from '../platform/haptics/haptics' import { translate } from '../i18n/mobile-i18n' import { cancelMobileCommitMessage, requestMobileCommitMessage } from './mobile-commit-message-ai' import { textGenerationErrorText } from './text-generation-failure-copy' diff --git a/mobile/src/source-control/use-mobile-source-control-commit-runners.ts b/mobile/src/source-control/use-mobile-source-control-commit-runners.ts index 48bcfadf5bd..224c5c611f4 100644 --- a/mobile/src/source-control/use-mobile-source-control-commit-runners.ts +++ b/mobile/src/source-control/use-mobile-source-control-commit-runners.ts @@ -1,6 +1,6 @@ import { useCallback, type Dispatch, type MutableRefObject, type SetStateAction } from 'react' import { translate } from '../i18n/mobile-i18n' -import { triggerError, triggerSuccess } from '../platform/haptics' +import { triggerError, triggerSuccess } from '../platform/haptics/haptics' import type { LoadStatusOptions } from './mobile-source-control-screen-state' import type { MobileCommitFailureRecovery, diff --git a/mobile/src/source-control/use-mobile-source-control-keyboard-lift.ts b/mobile/src/source-control/use-mobile-source-control-keyboard-lift.ts index 233d4cb078c..0030eb46a22 100644 --- a/mobile/src/source-control/use-mobile-source-control-keyboard-lift.ts +++ b/mobile/src/source-control/use-mobile-source-control-keyboard-lift.ts @@ -1,4 +1,4 @@ -import { useKeyboardOcclusion } from '../platform/keyboard-occlusion' +import { useKeyboardOcclusion } from '../platform/keyboard/keyboard-occlusion' // Use the platform occlusion seam: react-native-web Keyboard never fires, so pages measure visualViewport. export function useMobileSourceControlKeyboardLift(): number { diff --git a/mobile/src/source-control/use-mobile-source-control-runners.ts b/mobile/src/source-control/use-mobile-source-control-runners.ts index 956899af2a6..5a0700c7c1c 100644 --- a/mobile/src/source-control/use-mobile-source-control-runners.ts +++ b/mobile/src/source-control/use-mobile-source-control-runners.ts @@ -2,7 +2,7 @@ import { useCallback, type Dispatch, type MutableRefObject, type SetStateAction import { translate } from '../i18n/mobile-i18n' import type { RouteHandoff } from '../navigation/route-handoff' import type { RpcClient } from '../transport/rpc/rpc-client-contract' -import { triggerError, triggerSuccess } from '../platform/haptics' +import { triggerError, triggerSuccess } from '../platform/haptics/haptics' import { useMobileCommitMessageGeneration } from './use-mobile-commit-message-generation' import { useMobileSourceControlCommitRunners } from './use-mobile-source-control-commit-runners' import { useMobileSourceControlActionSheetRunners } from './use-mobile-source-control-action-sheet-runners' diff --git a/mobile/src/tasks/item-detail/mobile-tasks-item-actions.tsx b/mobile/src/tasks/item-detail/mobile-tasks-item-actions.tsx index 0e8cc216012..6626aa1e05d 100644 --- a/mobile/src/tasks/item-detail/mobile-tasks-item-actions.tsx +++ b/mobile/src/tasks/item-detail/mobile-tasks-item-actions.tsx @@ -12,7 +12,7 @@ import { MoreHorizontal } from 'lucide-react-native' import { colors } from '../../theme/mobile-theme' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import { styles } from '../mobile-tasks-legacy-styles' import { taskExternalOpenLabel, diff --git a/mobile/src/tasks/item-detail/mobile-tasks-item-detail-content.tsx b/mobile/src/tasks/item-detail/mobile-tasks-item-detail-content.tsx index 0885b5550e4..5b0bf1b61b7 100644 --- a/mobile/src/tasks/item-detail/mobile-tasks-item-detail-content.tsx +++ b/mobile/src/tasks/item-detail/mobile-tasks-item-detail-content.tsx @@ -3,7 +3,7 @@ import { translate } from '../../i18n/mobile-i18n' import { isBrokenCheck } from '../../../../src/shared/source-control/checks/pr-checks-fix-prompt' import { View, Text, Pressable } from 'react-native' import { colors, pressedStyle } from '../../theme/mobile-theme' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import { ExternalLink } from 'lucide-react-native' import { TextField } from '../../components/composer-input/TextField' import { styles, getGitLabPipelineStatusStyle } from '../mobile-tasks-legacy-styles' diff --git a/mobile/src/tasks/item-detail/use-mobile-tasks-hosted-comment-review-actions.tsx b/mobile/src/tasks/item-detail/use-mobile-tasks-hosted-comment-review-actions.tsx index ecbac3e8f7b..afbd07cb4f1 100644 --- a/mobile/src/tasks/item-detail/use-mobile-tasks-hosted-comment-review-actions.tsx +++ b/mobile/src/tasks/item-detail/use-mobile-tasks-hosted-comment-review-actions.tsx @@ -1,6 +1,6 @@ import { refreshMobileTaskPRChecks } from '../hosted-review/mobile-task-pr-check-refresh' import { githubTaskKey, sameGitHubTask, requireGitHubTaskTarget } from './github-task-identity' -import { useClipboardWriter } from '../../platform/clipboard' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' import type { HostedMetadataActionsModel } from './use-mobile-tasks-hosted-metadata-actions' import { scheduleMobileTaskCopyFeedbackReset } from '../screen/mobile-task-copy-feedback-timer' import { useCallback } from 'react' diff --git a/mobile/src/tasks/project-board/mobile-tasks-project-detail-drawer.tsx b/mobile/src/tasks/project-board/mobile-tasks-project-detail-drawer.tsx index 134bddf4bed..900c88630b5 100644 --- a/mobile/src/tasks/project-board/mobile-tasks-project-detail-drawer.tsx +++ b/mobile/src/tasks/project-board/mobile-tasks-project-detail-drawer.tsx @@ -2,7 +2,7 @@ import type { ConnectionPresentationModel } from '../screen-state/use-mobile-tas import { translate } from '../../i18n/mobile-i18n' import { BottomDrawer } from '../../components/bottom-drawer/BottomDrawer' import { View, Text, Pressable, ActivityIndicator } from 'react-native' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import { ExternalLink, Copy, Plus, RefreshCw, X, GitBranch } from 'lucide-react-native' import { colors, pressedStyle } from '../../theme/mobile-theme' import { TaskProviderLogo } from '../../components/TaskProviderLogo' diff --git a/mobile/src/tasks/project-board/mobile-tasks-project-review-panels.tsx b/mobile/src/tasks/project-board/mobile-tasks-project-review-panels.tsx index dabf6ac39af..c175c4c7c6a 100644 --- a/mobile/src/tasks/project-board/mobile-tasks-project-review-panels.tsx +++ b/mobile/src/tasks/project-board/mobile-tasks-project-review-panels.tsx @@ -13,7 +13,7 @@ import { View, Text, ActivityIndicator, Pressable } from 'react-native' import { colors, pressedStyle } from '../../theme/mobile-theme' import { Check, ExternalLink } from 'lucide-react-native' import { TextField } from '../../components/composer-input/TextField' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import { styles } from '../mobile-tasks-legacy-styles' export function renderMobileTasksProjectReviewPanels(model: ConnectionPresentationModel) { diff --git a/mobile/src/tasks/screen/mobile-tasks-filter-sheet.tsx b/mobile/src/tasks/screen/mobile-tasks-filter-sheet.tsx index d0e8ed6e75b..ceedb8f0816 100644 --- a/mobile/src/tasks/screen/mobile-tasks-filter-sheet.tsx +++ b/mobile/src/tasks/screen/mobile-tasks-filter-sheet.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react' import type { ConnectionPresentationModel } from '../screen-state/use-mobile-tasks-connection-presentation' import { translate as t } from '../../i18n/mobile-i18n' import { BottomDrawer } from '../../components/bottom-drawer/BottomDrawer' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import { Text, View } from 'react-native' import type { PickerOption } from '../../components/PickerModal' import { styles } from '../mobile-tasks-legacy-styles' diff --git a/mobile/src/tasks/task-list/mobile-tasks-provider-item-list.tsx b/mobile/src/tasks/task-list/mobile-tasks-provider-item-list.tsx index 1bc3efbaa89..4b00a279a8b 100644 --- a/mobile/src/tasks/task-list/mobile-tasks-provider-item-list.tsx +++ b/mobile/src/tasks/task-list/mobile-tasks-provider-item-list.tsx @@ -3,7 +3,7 @@ import { translate } from '../../i18n/mobile-i18n' import { View, ActivityIndicator, Text, FlatList, Pressable } from 'react-native' import { colors, spacing, type MobileColorScheme, pressedStyle } from '../../theme/mobile-theme' import { ChevronLeft, ChevronRight, ExternalLink, GitBranch } from 'lucide-react-native' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import { getHostedReviewSignalTone, getHostedReviewLabel, diff --git a/mobile/src/tasks/workspace-creation/mobile-tasks-create-drawers.tsx b/mobile/src/tasks/workspace-creation/mobile-tasks-create-drawers.tsx index a12ace81ec4..429c44e20ed 100644 --- a/mobile/src/tasks/workspace-creation/mobile-tasks-create-drawers.tsx +++ b/mobile/src/tasks/workspace-creation/mobile-tasks-create-drawers.tsx @@ -8,7 +8,7 @@ import { ChevronDown, ExternalLink, Lock } from 'lucide-react-native' import { MessageBox } from '../../components/composer-input/MessageBox' import { TextField } from '../../components/composer-input/TextField' import { PickerModal } from '../../components/PickerModal' -import { openExternalLink } from '../../platform/external-link' +import { openExternalLink } from '../../platform/external-links/external-link' import { styles } from '../mobile-tasks-legacy-styles' import { getRepoBadgeColor } from '../repositories/mobile-tasks-repository-presentation' import type { RepoSummary, LinearTeam } from '../mobile-tasks-provider-detail-types' diff --git a/mobile/src/terminal/quick-commands/QuickCommandRow.tsx b/mobile/src/terminal/quick-commands/QuickCommandRow.tsx index 5cfc56b24e4..ef034cd5aad 100644 --- a/mobile/src/terminal/quick-commands/QuickCommandRow.tsx +++ b/mobile/src/terminal/quick-commands/QuickCommandRow.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react' import { View, Text, Pressable, StyleSheet } from 'react-native' -import { useClipboardWriter } from '../../platform/clipboard' -import { triggerError } from '../../platform/haptics' +import { useClipboardWriter } from '../../platform/clipboard/clipboard' +import { triggerError } from '../../platform/haptics/haptics' import { Check, Copy, Pencil, Play, Trash2 } from 'lucide-react-native' import { colors, radii, spacing, typography } from '../../theme/mobile-theme' import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' diff --git a/mobile/src/terminal/quick-commands/mobile-session-quick-command-launch.ts b/mobile/src/terminal/quick-commands/mobile-session-quick-command-launch.ts index 6a8f270e4ab..bb0c5edb0e0 100644 --- a/mobile/src/terminal/quick-commands/mobile-session-quick-command-launch.ts +++ b/mobile/src/terminal/quick-commands/mobile-session-quick-command-launch.ts @@ -1,6 +1,6 @@ import { buildMobileQuickCommandLaunch, type MobileQuickCommandLaunch } from '../quick-commands' -import { triggerError } from '../../platform/haptics' +import { triggerError } from '../../platform/haptics/haptics' import { translate } from '../../i18n/mobile-i18n' import type { TerminalQuickCommand } from '../../../../src/shared/terminal/quick-commands/terminal-quick-command-types' import type { MobileNewTabAgentOption } from '../../session/agent-launch/mobile-new-tab-agent-options' diff --git a/mobile/src/terminal/session/MobileSessionAccessoryKeys.tsx b/mobile/src/terminal/session/MobileSessionAccessoryKeys.tsx index 49d2a259dce..fadfc6fab54 100644 --- a/mobile/src/terminal/session/MobileSessionAccessoryKeys.tsx +++ b/mobile/src/terminal/session/MobileSessionAccessoryKeys.tsx @@ -7,7 +7,7 @@ import { Plus, Smartphone } from 'lucide-react-native' -import { triggerMediumImpact } from '../../platform/haptics' +import { triggerMediumImpact } from '../../platform/haptics/haptics' import { createTerminalLiveAccessoryInput } from '../terminal-live-accessory-input' import { TERMINAL_ACCESSORY_REPEAT_DELAY_MS } from '../terminal-accessory-repeat' import { getVisibleTerminalAccessoryKeys } from '../terminal-accessory-layout' @@ -18,7 +18,7 @@ import { useMobileColorScheme } from '../../theme/use-mobile-color-scheme' import { useMobileTranslation } from '../../i18n/use-mobile-translation' import { colors } from '../../theme/mobile-theme' import { styles as sessionStyles } from '../../session/mobile-session-styles' -import { useKeyboardPersistingTaps } from '../../platform/keyboard-persisting-taps' +import { useKeyboardPersistingTaps } from '../../platform/keyboard/keyboard-persisting-taps' import type { MobileSessionController } from '../../session/use-mobile-session-controller' import { useMobileSessionKeyboardLift } from './use-mobile-session-keyboard-lift' import { diff --git a/mobile/src/terminal/session/TerminalMoreKeysSheet.tsx b/mobile/src/terminal/session/TerminalMoreKeysSheet.tsx index df03af5a62e..4478aa75a2d 100644 --- a/mobile/src/terminal/session/TerminalMoreKeysSheet.tsx +++ b/mobile/src/terminal/session/TerminalMoreKeysSheet.tsx @@ -4,7 +4,7 @@ import { ClipboardPaste, FileUp, ImagePlus, Monitor, Plus, Smartphone } from 'lu import type { LucideIcon } from 'lucide-react-native' import { BottomDrawer } from '../../components/bottom-drawer/BottomDrawer' import { SectionHeader } from '../../components/list/ListGroup' -import { triggerMediumImpact } from '../../platform/haptics' +import { triggerMediumImpact } from '../../platform/haptics/haptics' import type { TerminalAccessoryKey } from '../terminal-accessory-keys' import { TERMINAL_SHORTCUT_SPECIAL_KEYS } from '../terminal-accessory-keys' import type { CustomKey } from '../terminal-custom-keys' diff --git a/mobile/src/terminal/session/mobile-session-terminal-create-feedback.ts b/mobile/src/terminal/session/mobile-session-terminal-create-feedback.ts index d4a85ec9f55..cac26da7eb5 100644 --- a/mobile/src/terminal/session/mobile-session-terminal-create-feedback.ts +++ b/mobile/src/terminal/session/mobile-session-terminal-create-feedback.ts @@ -1,4 +1,4 @@ -import { triggerError } from '../../platform/haptics' +import { triggerError } from '../../platform/haptics/haptics' import { terminalCreateCopy } from './mobile-session-terminal-create-copy' import type { MobileSessionAttachmentsModel } from '../../files/attachments/use-mobile-session-attachments' import type { MobileSessionTerminalCreateOptions } from './mobile-session-terminal-create-request' diff --git a/mobile/src/terminal/session/use-mobile-session-accessory-selection.ts b/mobile/src/terminal/session/use-mobile-session-accessory-selection.ts index 8d82b194027..fd4059f41da 100644 --- a/mobile/src/terminal/session/use-mobile-session-accessory-selection.ts +++ b/mobile/src/terminal/session/use-mobile-session-accessory-selection.ts @@ -6,7 +6,7 @@ import { createTerminalAccessoryRepeatController, createTerminalAccessoryRepeatSender } from '../terminal-accessory-repeat' -import { useClipboardReader, useClipboardWriter } from '../../platform/clipboard' +import { useClipboardReader, useClipboardWriter } from '../../platform/clipboard/clipboard' import { translate } from '../../i18n/mobile-i18n' import { newTabRepoListRead, @@ -17,7 +17,7 @@ import { triggerSuccess, triggerError, triggerEdgeBump -} from '../../platform/haptics' +} from '../../platform/haptics/haptics' import { sameTerminalKeyboardAvoidanceMetrics, type TerminalKeyboardAvoidanceMetrics, diff --git a/mobile/src/terminal/session/use-mobile-session-keyboard-lift.ts b/mobile/src/terminal/session/use-mobile-session-keyboard-lift.ts index ee3b0121461..1e4bfc6b030 100644 --- a/mobile/src/terminal/session/use-mobile-session-keyboard-lift.ts +++ b/mobile/src/terminal/session/use-mobile-session-keyboard-lift.ts @@ -1,4 +1,4 @@ -import { useKeyboardOcclusion } from '../../platform/keyboard-occlusion' +import { useKeyboardOcclusion } from '../../platform/keyboard/keyboard-occlusion' import { hostOs } from '../../platform/host-os' import { computeActiveTerminalKeyboardLift } from '../terminal-keyboard-avoidance-lift' import type { MobileSessionController } from '../../session/use-mobile-session-controller' diff --git a/mobile/src/terminal/session/use-mobile-session-keyboard-state.ts b/mobile/src/terminal/session/use-mobile-session-keyboard-state.ts index 785f282bb4f..e7e9b0146f0 100644 --- a/mobile/src/terminal/session/use-mobile-session-keyboard-state.ts +++ b/mobile/src/terminal/session/use-mobile-session-keyboard-state.ts @@ -1,5 +1,8 @@ import { useEffect, useCallback } from 'react' -import { currentSoftKeyboardHeight, subscribeSoftKeyboard } from '../../platform/keyboard-occlusion' +import { + currentSoftKeyboardHeight, + subscribeSoftKeyboard +} from '../../platform/keyboard/keyboard-occlusion' import { useTerminalViewportRefit } from '../terminal-viewport-refit' import { removeCustomKey, type CustomKey } from '../terminal-custom-keys' import { useWorkspaceExecutionHost } from '../../navigation/workspace-execution-host' diff --git a/mobile/src/terminal/session/use-mobile-session-terminal-create-actions.ts b/mobile/src/terminal/session/use-mobile-session-terminal-create-actions.ts index cb0f22fc01a..65183baf4c6 100644 --- a/mobile/src/terminal/session/use-mobile-session-terminal-create-actions.ts +++ b/mobile/src/terminal/session/use-mobile-session-terminal-create-actions.ts @@ -1,6 +1,6 @@ import type { RpcSuccess } from '../../transport/rpc/rpc-wire-types' import { sessionTabCreateTerminal } from '../../session/tabs/mobile-session-tab-write-operations' -import { triggerSuccess, triggerError } from '../../platform/haptics' +import { triggerSuccess, triggerError } from '../../platform/haptics/haptics' import { terminalCreateCopy } from './mobile-session-terminal-create-copy' import { createMobileSessionTerminalCreateFeedback } from './mobile-session-terminal-create-feedback' import { buildTerminalSendParams } from '../terminal-send-request' diff --git a/mobile/src/terminal/session/use-mobile-session-terminal-runtime.ts b/mobile/src/terminal/session/use-mobile-session-terminal-runtime.ts index 35405bc89a3..b09eee00f15 100644 --- a/mobile/src/terminal/session/use-mobile-session-terminal-runtime.ts +++ b/mobile/src/terminal/session/use-mobile-session-terminal-runtime.ts @@ -7,7 +7,7 @@ import type { PendingSessionSelection } from '../../session/tabs/pending-session import { useState, useRef, useCallback } from 'react' import type { TerminalFrame } from '../terminal-webview-messages' import type { Keyboard } from 'react-native' -import type { MobileTextInputHandle } from '../../platform/text-input-handle' +import type { MobileTextInputHandle } from '../../platform/text-input/text-input-handle' import { useFocusEffect } from 'expo-router' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import type { ConnectionState } from '../../transport/connection/connection-state' diff --git a/mobile/src/terminal/session/use-mobile-session-terminal-send-actions.ts b/mobile/src/terminal/session/use-mobile-session-terminal-send-actions.ts index edb2ba3e1ae..0aeac5734a7 100644 --- a/mobile/src/terminal/session/use-mobile-session-terminal-send-actions.ts +++ b/mobile/src/terminal/session/use-mobile-session-terminal-send-actions.ts @@ -1,8 +1,8 @@ import { useCallback } from 'react' import { Keyboard } from 'react-native' import { translate } from '../../i18n/mobile-i18n' -import { triggerError } from '../../platform/haptics' -import { currentSoftKeyboardHeight } from '../../platform/keyboard-occlusion' +import { triggerError } from '../../platform/haptics/haptics' +import { currentSoftKeyboardHeight } from '../../platform/keyboard/keyboard-occlusion' import type { createTerminalLiveAccessoryInput } from '../terminal-live-accessory-input' import { sendTerminalLiveAccessoryRawBytes } from '../terminal-live-accessory-raw-send' import { diff --git a/mobile/src/terminal/session/use-mobile-terminal-paste.ts b/mobile/src/terminal/session/use-mobile-terminal-paste.ts index 8d61401c0b3..4d929ff8e50 100644 --- a/mobile/src/terminal/session/use-mobile-terminal-paste.ts +++ b/mobile/src/terminal/session/use-mobile-terminal-paste.ts @@ -6,7 +6,7 @@ import { import { useCallback, type RefObject } from 'react' import { translate } from '../../i18n/mobile-i18n' import { terminalInputSend } from '../mobile-terminal-operations' -import { useClipboardReader } from '../../platform/clipboard' +import { useClipboardReader } from '../../platform/clipboard/clipboard' import type { TerminalModes } from '../terminal-webview-contract' import type { RpcClient } from '../../transport/rpc/rpc-client-contract' import type { ConnectionState } from '../../transport/connection/connection-state' diff --git a/mobile/src/terminal/session/use-session-terminal-key-rail.ts b/mobile/src/terminal/session/use-session-terminal-key-rail.ts index c7806a45aac..ab62a8b364b 100644 --- a/mobile/src/terminal/session/use-session-terminal-key-rail.ts +++ b/mobile/src/terminal/session/use-session-terminal-key-rail.ts @@ -8,7 +8,7 @@ import { isTerminalPhoneDisplayMode } from './terminal-phone-display-mode' import { isMobileTerminalTabRunningAgent } from './mobile-terminal-tab-agent' import { useShellTerminalKeyRail } from './shell-terminal-key-rail' import type { MobileSessionController } from '../../session/use-mobile-session-controller' -import { useKeyboardOcclusion } from '../../platform/keyboard-occlusion' +import { useKeyboardOcclusion } from '../../platform/keyboard/keyboard-occlusion' const ESC = '\x1b' const CTRL_C = '\x03' diff --git a/mobile/src/terminal/use-terminal-live-input-click.test.ts b/mobile/src/terminal/use-terminal-live-input-click.test.ts index 059167df485..ad9d95e82a4 100644 --- a/mobile/src/terminal/use-terminal-live-input-click.test.ts +++ b/mobile/src/terminal/use-terminal-live-input-click.test.ts @@ -5,7 +5,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import type { TerminalLiveInputSender } from './terminal-live-input-sender' import { useTerminalLiveInputCommit } from './use-terminal-live-input-commit' -vi.mock('../platform/live-input-composing-range', () => ({ +vi.mock('../platform/text-input/live-input-composing-range', () => ({ reportedLiveInputComposing: (isComposing: boolean | undefined) => isComposing })) diff --git a/mobile/src/terminal/use-terminal-live-input-commit.test.ts b/mobile/src/terminal/use-terminal-live-input-commit.test.ts index 9f45a1c1abc..a1a34afeb1f 100644 --- a/mobile/src/terminal/use-terminal-live-input-commit.test.ts +++ b/mobile/src/terminal/use-terminal-live-input-commit.test.ts @@ -5,7 +5,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import type { TerminalLiveInputSender } from './terminal-live-input-sender' import { useTerminalLiveInputCommit } from './use-terminal-live-input-commit' -vi.mock('../platform/live-input-composing-range', () => ({ +vi.mock('../platform/text-input/live-input-composing-range', () => ({ reportedLiveInputComposing: (isComposing: boolean | undefined) => isComposing })) diff --git a/mobile/src/terminal/use-terminal-live-input-commit.ts b/mobile/src/terminal/use-terminal-live-input-commit.ts index a51d9bfab1e..9ab23403bdb 100644 --- a/mobile/src/terminal/use-terminal-live-input-commit.ts +++ b/mobile/src/terminal/use-terminal-live-input-commit.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useRef, type RefObject } from 'react' import type { TextInputInstance } from 'react-native' -import { reportedLiveInputComposing } from '../platform/live-input-composing-range' +import { reportedLiveInputComposing } from '../platform/text-input/live-input-composing-range' import { getTerminalLiveSpecialKeyDecision } from './terminal-live-text-commit' import type { TerminalLiveAccessoryInput } from './terminal-live-accessory-input' import type { TerminalLiveInputSender } from './terminal-live-input-sender' diff --git a/mobile/src/terminal/use-terminal-live-input-focus.ts b/mobile/src/terminal/use-terminal-live-input-focus.ts index cd9608745a5..63d03346d10 100644 --- a/mobile/src/terminal/use-terminal-live-input-focus.ts +++ b/mobile/src/terminal/use-terminal-live-input-focus.ts @@ -1,5 +1,5 @@ import { useCallback, useLayoutEffect, useRef, type RefObject } from 'react' -import { currentSoftKeyboardHeight } from '../platform/keyboard-occlusion' +import { currentSoftKeyboardHeight } from '../platform/keyboard/keyboard-occlusion' import { clearTerminalLiveInputFocusTimer, focusTerminalLiveInputTarget, diff --git a/mobile/web-entry/index.tsx b/mobile/web-entry/index.tsx index df11b8b7952..dc5f74447fb 100644 --- a/mobile/web-entry/index.tsx +++ b/mobile/web-entry/index.tsx @@ -38,10 +38,10 @@ import { startPageLocaleCatalogs } from '../src/i18n/page-locale-catalog-source' import { getUiIntlLocale } from '../src/i18n/ui-intl-locale' import { readDeviceUiLocaleTags, resolveUiLocaleFromTags } from '../src/i18n/system-ui-locale' import { publishPageHostProfile } from '../src/mobile-web-shell/bridge/page-host-profile' -import { publishExternalLinkOpener } from '../src/platform/external-link.web' -import { publishHapticsNotifier } from '../src/platform/haptics.web' -import { startBlurringOnKeyboardClose } from '../src/platform/keyboard-close-blur.web' -import { publishShellKeyboardSource } from '../src/platform/keyboard-occlusion.web' +import { publishExternalLinkOpener } from '../src/platform/external-links/external-link.web' +import { publishHapticsNotifier } from '../src/platform/haptics/haptics.web' +import { startBlurringOnKeyboardClose } from '../src/platform/keyboard/keyboard-close-blur.web' +import { publishShellKeyboardSource } from '../src/platform/keyboard/keyboard-occlusion.web' // Select the page-client provider explicitly; the builder aliases extensionless screen imports to this same web module. import { RpcClientProvider } from '../src/transport/connection/client-context.web' import { OrcaQueryProvider } from '../src/query/orca-query-provider.web' diff --git a/mobile/web-entry/web-overrides.json b/mobile/web-entry/web-overrides.json index a624a12ad27..15667e1d838 100644 --- a/mobile/web-entry/web-overrides.json +++ b/mobile/web-entry/web-overrides.json @@ -15,7 +15,7 @@ }, { "file": "packages/expo-two-way-audio/src/ExpoTwoWayAudioModule.web.ts", - "reason": "Vendored with the module, not added for Route A. Nothing on the page reaches it any more: dictation's capture went behind src/platform/dictation-capture.web.ts, which asks the shell for the microphone, so the only importer of @orca/expo-two-way-audio is the native half of that seam. The file stays because it ships with the vendored module, and it answers denied microphone permission, which is what a browser outside the shell can honestly say." + "reason": "Vendored with the module, not added for Route A. Nothing on the page reaches it any more: dictation's capture went behind src/platform/audio-capture/dictation-capture.web.ts, which asks the shell for the microphone, so the only importer of @orca/expo-two-way-audio is the native half of that seam. The file stays because it ships with the vendored module, and it answers denied microphone permission, which is what a browser outside the shell can honestly say." }, { "file": "src/navigation/use-back-claim.web.ts", @@ -58,15 +58,15 @@ "reason": "The shell renders this page for this route, so the page has no shell to mount inside itself. Its native file reaches OrcaMobileWebShellView, whose requireNativeViewManager call runs at import and throws in a browser." }, { - "file": "src/platform/text-input-font-size.web.ts", + "file": "src/platform/text-input/text-input-font-size.web.ts", "reason": "iOS zooms the page on focus of any text input under 16px and does not zoom back out, which leaves the document at a scale other than 1 for a whole typing session. keyboard-occlusion.web.ts reads a scale other than 1 as 'not a keyboard' on purpose, because geometry cannot separate a zoom from a keyboard, so the commit bar and the note composer would never lift on the platform they were written for. The web file raises the app's 14px body size to that floor; the native file is the body size, so a phone renders exactly what it rendered before. maximum-scale=1 on the viewport meta was the other fix and was rejected: Android WebView honours it and it would take deliberate pinch zoom away from low-vision users." }, { - "file": "src/platform/text-input-content-height.web.ts", + "file": "src/platform/text-input/text-input-content-height.web.ts", "reason": "react-native-web renders a multiline TextInput as a